diff --git a/lib/services/tetrio_crud.dart b/lib/services/tetrio_crud.dart index 1d93cab..8308dcb 100644 --- a/lib/services/tetrio_crud.dart +++ b/lib/services/tetrio_crud.dart @@ -576,7 +576,7 @@ class TetrioService extends DB { /// Retrieves Tetra League history from p1nkl0bst3r api for a player with given [id]. Returns a list of states /// (state = instance of [TetrioPlayer] at some point of time). Can throw an exception if fails to retrieve data. - Future> fetchAndsaveTLHistory(String id) async { + Future> fetchAndsaveTLHistory(String id) async { Uri url; if (kIsWeb) { url = Uri.https('ts.dan63.by', 'oskware_bridge.php', {"endpoint": "TLHistory", "user": id}); @@ -1100,15 +1100,11 @@ class TetrioService extends DB { } } - Future> getStates(String userID, int season) async { + Future> getStates(String userID, {int? season}) async { await ensureDbIsOpen(); final db = getDatabaseOrThrow(); - List query = await db.query(tetrioLeagueTable, where: '"id" = ? AND "season" = ?', whereArgs: [userID, season]); - List result = []; - for (var entry in query){ - result.add(TetraLeague.fromJson(entry as Map, entry["timestamp"], entry["season"], entry["id"])); - } - return result; + List query = await db.query(tetrioLeagueTable, where: season != null ? '"id" LIKE ? AND "season" = ?' : '"id" LIKE ?', whereArgs: season != null ? ["${userID}%", season] : ["${userID}%"], orderBy: '"id" ASC'); + return [for (var entry in query) TetraLeague.fromJson(entry as Map, DateTime.fromMillisecondsSinceEpoch(int.parse(entry["id"].substring(24), radix: 16)), entry["season"], entry["id"].substring(0, 24))]; } /// Saves state (which is [TetraLeague]) to the local database. @@ -1121,14 +1117,6 @@ class TetrioService extends DB { } } - Future> getHistory(String id, {int season = currentSeason}) async { - await ensureDbIsOpen(); - final db = getDatabaseOrThrow(); - List raw = await db.query(tetrioLeagueTable, where: '"id" = ? AND "season" = ?', whereArgs: [id, season]); - List result = [for (var entry in raw) TetraLeague.fromJson(entry as Map, DateTime.fromMillisecondsSinceEpoch(int.parse(entry["id"].substring(24), radix: 16)), entry["season"], entry["id"].substring(0, 24))]; - return result; - } - /// Remove state (which is [tetrioPlayer]) from the local database // Future deleteState(TetrioPlayer tetrioPlayer) async { // await ensureDbIsOpen(); diff --git a/lib/views/main_view.dart b/lib/views/main_view.dart index 078a2fb..9e52818 100644 --- a/lib/views/main_view.dart +++ b/lib/views/main_view.dart @@ -40,6 +40,7 @@ import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:go_router/go_router.dart'; int _chartsIndex = 0; +int _season = currentSeason-1; bool _gamesPlayedInsteadOfDateAndTime = false; late ZoomPanBehavior _zoomPanBehavior; bool _smooth = false; @@ -73,7 +74,8 @@ class _MainState extends State with TickerProviderStateMixin { String _searchFor = "6098518e3d5155e6ec429cdc"; // who we looking for String _titleNickname = ""; /// Each dropdown menu item contains list of dots for the graph - List>> chartsData = []; + /// chartsData[season-1][chart] + List>>> chartsData = []; //var tableData = []; final bodyGlobalKey = GlobalKey(); bool _showSearchBar = false; @@ -217,9 +219,9 @@ class _MainState extends State with TickerProviderStateMixin { // Making list of Tetra League matches bool isTracking = await teto.isPlayerTracking(me.userId); - List states = await teto.getHistory(me.userId); - TetraLeague? compareWith; - Set uniqueTL = {}; + List> states = await Future.wait>([ + teto.getStates(me.userId, season: 1), teto.getStates(me.userId, season: 2), + ]); List storedRecords = await teto.getTLMatchesbyPlayerID(me.userId); // get old matches if (isTracking){ // if tracked - save data to local DB await teto.storeState(summaries.league); @@ -277,31 +279,32 @@ class _MainState extends State with TickerProviderStateMixin { // if (uniqueTL.isEmpty) uniqueTL.add(summaries.league); // } // Also i need previous Tetra League State for comparison if avaliable - if (states.length >= 2){ - compareWith = states.elementAtOrNull(states.length - 2); - chartsData = >>[ // Dumping charts data into dropdown menu items, while cheking if every entry is valid - DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.gamesPlayed > 9) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.tr)], child: Text(t.statCellNum.tr)), - DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.gamesPlayed > 9) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.glicko!)], child: const Text("Glicko")), - DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.gamesPlayed > 9) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.rd!)], child: const Text("Rating Deviation")), - DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.apm != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.apm!)], child: Text(t.statCellNum.apm.replaceAll(RegExp(r'\n'), " "))), - DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.pps != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.pps!)], child: Text(t.statCellNum.pps.replaceAll(RegExp(r'\n'), " "))), - DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.vs != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.vs!)], child: Text(t.statCellNum.vs.replaceAll(RegExp(r'\n'), " "))), - DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.app)], child: Text(t.statCellNum.app.replaceAll(RegExp(r'\n'), " "))), - DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.dss)], child: Text(t.statCellNum.dss.replaceAll(RegExp(r'\n'), " "))), - DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.dsp)], child: Text(t.statCellNum.dsp.replaceAll(RegExp(r'\n'), " "))), - DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.appdsp)], child: const Text("APP + DS/P")), - DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.vsapm)], child: const Text("VS/APM")), - DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.cheese)], child: Text(t.statCellNum.cheese.replaceAll(RegExp(r'\n'), " "))), - DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.gbe)], child: Text(t.statCellNum.gbe.replaceAll(RegExp(r'\n'), " "))), - DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.nyaapp)], child: Text(t.statCellNum.nyaapp.replaceAll(RegExp(r'\n'), " "))), - DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.area)], child: Text(t.statCellNum.area.replaceAll(RegExp(r'\n'), " "))), - DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.estTr != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.estTr!.esttr)], child: Text(t.statCellNum.estOfTR.replaceAll(RegExp(r'\n'), " "))), - DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.esttracc != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.esttracc!)], child: Text(t.statCellNum.accOfEst.replaceAll(RegExp(r'\n'), " "))), - DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.playstyle != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.playstyle!.opener)], child: const Text("Opener")), - DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.playstyle != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.playstyle!.plonk)], child: const Text("Plonk")), - DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.playstyle != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.playstyle!.infds)], child: const Text("Inf. DS")), - DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.playstyle != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.playstyle!.stride)], child: const Text("Stride")), - ]; + TetraLeague? compareWith; + if (states[1].length >= 2 || states[0].length >= 2){ + compareWith = states[1].length >= 2 ? states[1].elementAtOrNull(states.length - 2) : null; + chartsData = [for (List s in states) >>[ // Dumping charts data into dropdown menu items, while cheking if every entry is valid + DropdownMenuItem(value: [for (var tl in s) if (tl.gamesPlayed > 9) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.tr)], child: Text(t.statCellNum.tr)), + DropdownMenuItem(value: [for (var tl in s) if (tl.gamesPlayed > 9) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.glicko!)], child: const Text("Glicko")), + DropdownMenuItem(value: [for (var tl in s) if (tl.gamesPlayed > 9) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.rd!)], child: const Text("Rating Deviation")), + DropdownMenuItem(value: [for (var tl in s) if (tl.apm != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.apm!)], child: Text(t.statCellNum.apm.replaceAll(RegExp(r'\n'), " "))), + DropdownMenuItem(value: [for (var tl in s) if (tl.pps != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.pps!)], child: Text(t.statCellNum.pps.replaceAll(RegExp(r'\n'), " "))), + DropdownMenuItem(value: [for (var tl in s) if (tl.vs != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.vs!)], child: Text(t.statCellNum.vs.replaceAll(RegExp(r'\n'), " "))), + DropdownMenuItem(value: [for (var tl in s) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.app)], child: Text(t.statCellNum.app.replaceAll(RegExp(r'\n'), " "))), + DropdownMenuItem(value: [for (var tl in s) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.dss)], child: Text(t.statCellNum.dss.replaceAll(RegExp(r'\n'), " "))), + DropdownMenuItem(value: [for (var tl in s) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.dsp)], child: Text(t.statCellNum.dsp.replaceAll(RegExp(r'\n'), " "))), + DropdownMenuItem(value: [for (var tl in s) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.appdsp)], child: const Text("APP + DS/P")), + DropdownMenuItem(value: [for (var tl in s) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.vsapm)], child: const Text("VS/APM")), + DropdownMenuItem(value: [for (var tl in s) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.cheese)], child: Text(t.statCellNum.cheese.replaceAll(RegExp(r'\n'), " "))), + DropdownMenuItem(value: [for (var tl in s) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.gbe)], child: Text(t.statCellNum.gbe.replaceAll(RegExp(r'\n'), " "))), + DropdownMenuItem(value: [for (var tl in s) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.nyaapp)], child: Text(t.statCellNum.nyaapp.replaceAll(RegExp(r'\n'), " "))), + DropdownMenuItem(value: [for (var tl in s) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.area)], child: Text(t.statCellNum.area.replaceAll(RegExp(r'\n'), " "))), + DropdownMenuItem(value: [for (var tl in s) if (tl.estTr != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.estTr!.esttr)], child: Text(t.statCellNum.estOfTR.replaceAll(RegExp(r'\n'), " "))), + DropdownMenuItem(value: [for (var tl in s) if (tl.esttracc != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.esttracc!)], child: Text(t.statCellNum.accOfEst.replaceAll(RegExp(r'\n'), " "))), + DropdownMenuItem(value: [for (var tl in s) if (tl.playstyle != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.playstyle!.opener)], child: const Text("Opener")), + DropdownMenuItem(value: [for (var tl in s) if (tl.playstyle != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.playstyle!.plonk)], child: const Text("Plonk")), + DropdownMenuItem(value: [for (var tl in s) if (tl.playstyle != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.playstyle!.infds)], child: const Text("Inf. DS")), + DropdownMenuItem(value: [for (var tl in s) if (tl.playstyle != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.playstyle!.stride)], child: const Text("Stride")), + ]]; }else{ compareWith = null; chartsData = []; @@ -312,7 +315,7 @@ class _MainState extends State with TickerProviderStateMixin { changePlayer(me.userId); }); } - return [me, summaries, news, tlStream, recentZenith, recentZenithEX, states]; + return [me, summaries, news, tlStream, recentZenith, recentZenithEX, states[currentSeason-1]]; //return [me, records, states, tlMatches, compareWith, isTracking, news, topTR, recent, sprint, blitz, tlMatches.elementAtOrNull(0)?.timestamp]; } @@ -850,7 +853,7 @@ class _ZenithRecords extends StatelessWidget { } class _History extends StatelessWidget{ - final List>> chartsData; + final List>>> chartsData; final String userID; final Function update; final Function changePlayer; @@ -873,7 +876,7 @@ class _History extends StatelessWidget{ )); } bool bigScreen = MediaQuery.of(context).size.width > 768; - List<_HistoryChartSpot> selectedGraph = chartsData[_chartsIndex].value!; + List<_HistoryChartSpot> selectedGraph = chartsData[_season][_chartsIndex].value!; return SingleChildScrollView( scrollDirection: Axis.vertical, child: Column( @@ -883,6 +886,20 @@ class _History extends StatelessWidget{ spacing: 20, crossAxisAlignment: WrapCrossAlignment.center, children: [ + Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Padding(padding: EdgeInsets.all(8.0), child: Text("Season:", style: TextStyle(fontSize: 22))), + DropdownButton( + items: [for (int i = 1; i <= currentSeason; i++) DropdownMenuItem(value: i-1, child: Text("$i"))], + value: _season, + onChanged: (value) { + _season = value!; + update(); + } + ), + ], + ), Row( mainAxisSize: MainAxisSize.min, children: [ @@ -902,10 +919,10 @@ class _History extends StatelessWidget{ children: [ const Padding(padding: EdgeInsets.all(8.0), child: Text("Y:", style: TextStyle(fontSize: 22))), DropdownButton( - items: chartsData, - value: chartsData[_chartsIndex].value, + items: chartsData[_season], + value: chartsData[_season][_chartsIndex].value, onChanged: (value) { - _chartsIndex = chartsData.indexWhere((element) => element.value == value); + _chartsIndex = chartsData[_season].indexWhere((element) => element.value == value); update(); } ), @@ -926,13 +943,13 @@ class _History extends StatelessWidget{ IconButton(onPressed: () => _zoomPanBehavior.reset(), icon: const Icon(Icons.refresh), alignment: Alignment.center,) ], ), - if(chartsData[_chartsIndex].value!.length > 1) _HistoryChartThigy(data: selectedGraph, smooth: _smooth, yAxisTitle: _historyShortTitles[_chartsIndex], bigScreen: bigScreen, leftSpace: bigScreen? 80 : 45, yFormat: bigScreen? f2 : NumberFormat.compact(), xFormat: NumberFormat.compact()) - else if (chartsData[_chartsIndex].value!.length <= 1) Center(child: Column( + if(chartsData[_season][_chartsIndex].value!.length > 1) _HistoryChartThigy(data: selectedGraph, smooth: _smooth, yAxisTitle: _historyShortTitles[_chartsIndex], bigScreen: bigScreen, leftSpace: bigScreen? 80 : 45, yFormat: bigScreen? f2 : NumberFormat.compact(), xFormat: NumberFormat.compact()) + else if (chartsData[_season][_chartsIndex].value!.length <= 1) Center(child: Column( mainAxisSize: MainAxisSize.min, children: [ Text(t.notEnoughData, style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28)), - if (wasActiveInTL) Text(t.errors.actionSuggestion), - if (wasActiveInTL) TextButton(onPressed: (){changePlayer(userID, fetchHistory: true);}, child: Text(t.fetchAndsaveTLHistory)) + if (wasActiveInTL && _season == 0) Text(t.errors.actionSuggestion), + if (wasActiveInTL && _season == 0) TextButton(onPressed: (){changePlayer(userID, fetchHistory: true);}, child: Text(t.fetchAndsaveTLHistory)) ], )) ], diff --git a/lib/views/states_view.dart b/lib/views/states_view.dart index f5a0f2c..af7b32f 100644 --- a/lib/views/states_view.dart +++ b/lib/views/states_view.dart @@ -11,7 +11,7 @@ import 'package:tetra_stats/widgets/text_timestamp.dart'; import 'package:window_manager/window_manager.dart'; class StatesView extends StatefulWidget { - final List states; + final List states; const StatesView({super.key, required this.states}); @override @@ -25,7 +25,7 @@ class StatesState extends State { void initState() { if (!kIsWeb && !Platform.isAndroid && !Platform.isIOS){ windowManager.getTitle().then((value) => oldWindowTitle = value); - windowManager.setTitle("Tetra Stats: ${t.statesViewTitle(number: widget.states.length, nickname: widget.states.last.username.toUpperCase())}"); + windowManager.setTitle("Tetra Stats: ${t.statesViewTitle(number: widget.states.length, nickname: widget.states.last.id.toUpperCase())}"); } super.initState(); } @@ -41,14 +41,14 @@ class StatesState extends State { final t = Translations.of(context); return Scaffold( appBar: AppBar( - title: Text(t.statesViewTitle(number: widget.states.length, nickname: widget.states.last.username.toUpperCase())), + title: Text(t.statesViewTitle(number: widget.states.length, nickname: widget.states.first.id)), actions: [ IconButton( onPressed: (){ Navigator.push( context, MaterialPageRoute( - builder: (context) => MatchesView(userID: widget.states.first.userId, username: widget.states.first.username), + builder: (context) => MatchesView(userID: widget.states.first.id, username: widget.states.first.id), ), ); }, icon: const Icon(Icons.list), tooltip: t.viewAllMatches) @@ -60,24 +60,24 @@ class StatesState extends State { itemCount: widget.states.length, itemBuilder: (context, index) { return ListTile( - title: Text(timestamp(widget.states[index].state)), - subtitle: Text(t.statesViewEntry(level: widget.states[index].level.toStringAsFixed(2), gameTime: widget.states[index].gameTime, friends: widget.states[index].friendCount, rd: 0)), + title: Text(timestamp(widget.states[index].timestamp)), + //subtitle: Text(t.statesViewEntry(level: widget.states[index].level.toStringAsFixed(2), gameTime: widget.states[index].gameTime, friends: widget.states[index].friendCount, rd: 0)), trailing: IconButton( icon: const Icon(Icons.delete_forever), onPressed: () { - DateTime nn = widget.states[index].state; + //DateTime nn = widget.states[index].state; // teto.deleteState(widget.states[index]).then((value) => setState(() { // ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.stateRemoved(date: timestamp(nn))))); // })); }, ), onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => StateView(state: widget.states[index]), - ), - ); + // Navigator.push( + // context, + // MaterialPageRoute( + // builder: (context) => StateView(state: widget.states[index]), + // ), + // ); }, ); }))); diff --git a/lib/views/tracked_players_view.dart b/lib/views/tracked_players_view.dart index 905e3f4..ef4cc6c 100644 --- a/lib/views/tracked_players_view.dart +++ b/lib/views/tracked_players_view.dart @@ -119,12 +119,12 @@ class TrackedPlayersState extends State { }, ), onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => StatesView(states: allPlayers[keys[index]]!), - ), - ); + // Navigator.push( + // context, + // MaterialPageRoute( + // builder: (context) => StatesView(states: allPlayers[keys[index]]!), + // ), + // ); }, ); }));