From a3b056953a938584a248227d4c8a4b3743599045 Mon Sep 17 00:00:00 2001 From: dan63047 Date: Sat, 10 Jun 2023 00:04:51 +0300 Subject: [PATCH] Now we have list of tracked players (just like side bar in main view) --- lib/main.dart | 2 +- lib/services/sqlite_db_controller.dart | 1 + lib/services/tetrio_crud.dart | 56 +++++++++++++++------- lib/views/main_view.dart | 56 +++++++++++++--------- lib/views/states_view.dart | 23 --------- lib/views/tracked_players_view.dart | 65 ++++++++++++++++++++++++++ 6 files changed, 140 insertions(+), 63 deletions(-) delete mode 100644 lib/views/states_view.dart create mode 100644 lib/views/tracked_players_view.dart diff --git a/lib/main.dart b/lib/main.dart index 3de6c39..ffcb2fa 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,7 +3,7 @@ import 'package:sqflite_common_ffi/sqflite_ffi.dart'; import 'package:tetra_stats/views/main_view.dart'; import 'package:tetra_stats/views/compare_view.dart'; import 'package:tetra_stats/views/settings_view.dart'; -import 'package:tetra_stats/views/states_view.dart'; +import 'package:tetra_stats/views/tracked_players_view.dart'; import 'package:tetra_stats/views/calc_view.dart'; void main() { diff --git a/lib/services/sqlite_db_controller.dart b/lib/services/sqlite_db_controller.dart index 700a48e..5d8f6d4 100644 --- a/lib/services/sqlite_db_controller.dart +++ b/lib/services/sqlite_db_controller.dart @@ -19,6 +19,7 @@ class DB { final db = await openDatabase(dbPath); _db = db; await db.execute(createTetrioUsersTable); + await db.execute(createTetrioUsersToTrack); } on MissingPlatformDirectoryException { throw UnableToGetDocuments(); } diff --git a/lib/services/tetrio_crud.dart b/lib/services/tetrio_crud.dart index a398b94..600ae52 100644 --- a/lib/services/tetrio_crud.dart +++ b/lib/services/tetrio_crud.dart @@ -2,17 +2,13 @@ import 'dart:async'; import 'dart:convert'; import 'dart:developer' as developer; import 'package:http/http.dart' as http; -import 'dart:async'; -import 'package:sqflite/sqflite.dart'; -// import 'package:sqflite/sqflite.dart'; -// import 'package:path_provider/path_provider.dart' show MissingPlatformDirectoryException, getApplicationDocumentsDirectory; -// import 'package:path/path.dart' show join; import 'package:tetra_stats/services/crud_exceptions.dart'; import 'package:tetra_stats/services/sqlite_db_controller.dart'; import 'package:tetra_stats/data_objects/tetrio.dart'; const String dbName = "TetraStats.db"; const String tetrioUsersTable = "tetrioUsers"; +const String tetrioUsersToTrackTable = "tetrioUsersToTrack"; const String idCol = "id"; const String nickCol = "nickname"; const String statesCol = "jsonStates"; @@ -23,6 +19,12 @@ const String createTetrioUsersTable = ''' "jsonStates" TEXT, PRIMARY KEY("id") );'''; +const String createTetrioUsersToTrack = ''' + CREATE TABLE IF NOT EXISTS "tetrioUsersToTrack" ( + "id" TEXT NOT NULL UNIQUE, + PRIMARY KEY("ID") + ) +'''; class TetrioService extends DB { Map> _players = {}; @@ -50,9 +52,9 @@ class TetrioService extends DB { developer.log("_cachePlayers: $_players", name: "services/tetrio_crud"); } - Future deletePlayer({required String id, required DB udb}) async { + Future deletePlayer(String id) async { await ensureDbIsOpen(); - final db = udb.getDatabaseOrThrow(); + final db = getDatabaseOrThrow(); final deletedPlayer = await db.delete(tetrioUsersTable, where: '$idCol = ?', whereArgs: [id.toLowerCase()]); if (deletedPlayer != 1) { throw CouldNotDeletePlayer(); @@ -62,16 +64,6 @@ class TetrioService extends DB { } } - // Future > getOrCreatePlayer({required String id}) async { - // try{ - // final player = await getPlayer(id: id); - // return player; - // } on TetrioPlayerNotExist{ - - // final player = await createPlayer(tetrioPlayer: tetrioPlayer) - // } - // } - Future createPlayer(TetrioPlayer tetrioPlayer) async { ensureDbIsOpen(); final db = getDatabaseOrThrow(); @@ -87,6 +79,36 @@ class TetrioService extends DB { _tetrioStreamController.add(_players); } + Future addPlayerToTrack(TetrioPlayer tetrioPlayer) async { + ensureDbIsOpen(); + final db = getDatabaseOrThrow(); + final results = await db.query(tetrioUsersToTrackTable, where: '$idCol = ?', whereArgs: [tetrioPlayer.userId.toLowerCase()]); + if (results.isNotEmpty) { + throw TetrioPlayerAlreadyExist(); + } + db.insert(tetrioUsersToTrackTable, {idCol: tetrioPlayer.userId}); + } + + Future> getAllPlayerToTrack() async { + await ensureDbIsOpen(); + final db = getDatabaseOrThrow(); + final players = await db.query(tetrioUsersToTrackTable); + developer.log("getAllPlayerToTrack: $players", name: "services/tetrio_crud"); + return players.map((noteRow) => noteRow["id"].toString()); + } + + Future deletePlayerToTrack(String id) async { + await ensureDbIsOpen(); + final db = getDatabaseOrThrow(); + final deletedPlayer = await db.delete(tetrioUsersToTrackTable, where: '$idCol = ?', whereArgs: [id.toLowerCase()]); + if (deletedPlayer != 1) { + throw CouldNotDeletePlayer(); + } else { + // _players.removeWhere((key, value) => key == id); + // _tetrioStreamController.add(_players); + } + } + Future storeState(TetrioPlayer tetrioPlayer) async { ensureDbIsOpen(); final db = getDatabaseOrThrow(); diff --git a/lib/views/main_view.dart b/lib/views/main_view.dart index db2617c..a797b95 100644 --- a/lib/views/main_view.dart +++ b/lib/views/main_view.dart @@ -156,7 +156,7 @@ class _MainState extends State with SingleTickerProviderStateMixin { ), const PopupMenuItem( value: "/states", - child: Text('States'), + child: Text('Players you track'), ), const PopupMenuItem( value: "/calc", @@ -376,7 +376,7 @@ class _UserThingy extends StatelessWidget { return LayoutBuilder(builder: (context, constraints) { bool bigScreen = constraints.maxWidth > 768; double bannerHeight = bigScreen ? 240 : 120; - double pfpHeight = bigScreen ? 64 : 32; + double pfpHeight = 128; return Column( children: [ Flex( @@ -407,7 +407,7 @@ class _UserThingy extends StatelessWidget { ? Image.asset( "res/avatars/tetrio_banned.png", fit: BoxFit.fitHeight, - height: 128, + height: pfpHeight, ) : player.avatarRevision != null ? Image.network("https://tetr.io/user-content/avatars/${player.userId}.jpg?rv=${player.avatarRevision}", @@ -416,31 +416,47 @@ class _UserThingy extends StatelessWidget { return Image.asset( "res/avatars/tetrio_anon.png", fit: BoxFit.fitHeight, - height: 128, + height: pfpHeight, ); }) : Image.asset( "res/avatars/tetrio_anon.png", fit: BoxFit.fitHeight, - height: 128, + height: pfpHeight, ), ), ), + if (player.verified) + Padding( + padding: EdgeInsets.fromLTRB( + pfpHeight - 22, + bigScreen // verified icon top padding: + ? (player.bannerRevision != null ? bannerHeight + pfpHeight - 96 : pfpHeight + pfpHeight - 32) // for big screen + : (player.bannerRevision != null ? bannerHeight + pfpHeight - 58 : pfpHeight + pfpHeight - 32), // for small screen + 0, + 0), + child: const Icon(Icons.verified), + ) ], ), Flexible( - child: Column( - children: [ - Text(player.username, style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)), - TextButton( - child: Text(player.userId, style: const TextStyle(fontFamily: "Eurostile Round Condensed", fontSize: 14)), - onPressed: () { - copyToClipboard(player.userId); - ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("Copied to clipboard!"))); - }), - ], - ), - ), + child: Column( + children: [ + Text(player.username, style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)), + TextButton( + child: Text(player.userId, style: const TextStyle(fontFamily: "Eurostile Round Condensed", fontSize: 14)), + onPressed: () { + copyToClipboard(player.userId); + ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("Copied to clipboard!"))); + }), + IconButton( + icon: Icon(Icons.addchart), + style: ButtonStyle(), + onPressed: () {}, + ), + Text("Track") + ], + )), ], ), (player.role != "banned") @@ -459,11 +475,7 @@ class _UserThingy extends StatelessWidget { ), if (player.gameTime >= Duration.zero) _StatCellNum( - playerStat: player.gameTime.inHours, - playerStatLabel: "Hours\nPlayed", - isScreenBig: bigScreen, - snackBar: player.gameTime.toString(), - ), + playerStat: player.gameTime.inHours, playerStatLabel: "Hours\nPlayed", isScreenBig: bigScreen, snackBar: player.gameTime.toString()), if (player.gamesPlayed >= 0) _StatCellNum(playerStat: player.gamesPlayed, isScreenBig: bigScreen, playerStatLabel: "Online\nGames"), if (player.gamesWon >= 0) _StatCellNum(playerStat: player.gamesWon, isScreenBig: bigScreen, playerStatLabel: "Games\nWon"), if (player.friendCount > 0) _StatCellNum(playerStat: player.friendCount, isScreenBig: bigScreen, playerStatLabel: "Friends"), diff --git a/lib/views/states_view.dart b/lib/views/states_view.dart deleted file mode 100644 index 9320d2d..0000000 --- a/lib/views/states_view.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'package:flutter/material.dart'; - -class StatesView extends StatefulWidget { - const StatesView({Key? key}) : super(key: key); - - @override - State createState() => StatesState(); -} - -class StatesState extends State { - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text("States of NICKNAME account"), - ), - backgroundColor: Colors.black, - body: const SafeArea( - child: Text( - "So it's gonna be possible to store history of the account. One single piece of account history is what i call \"State\". In this view you will be able to control states, delete really old ones if too many.\n\nRight now app doesn't even store it.")), - ); - } -} diff --git a/lib/views/tracked_players_view.dart b/lib/views/tracked_players_view.dart new file mode 100644 index 0000000..eb710cb --- /dev/null +++ b/lib/views/tracked_players_view.dart @@ -0,0 +1,65 @@ +import 'package:flutter/material.dart'; +import 'package:tetra_stats/data_objects/tetrio.dart'; +import 'package:tetra_stats/services/tetrio_crud.dart'; + +final TetrioService teto = TetrioService(); + +class StatesView extends StatefulWidget { + const StatesView({Key? key}) : super(key: key); + + @override + State createState() => StatesState(); +} + +class StatesState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text("Players you track"), + ), + backgroundColor: Colors.black, + body: SafeArea( + child: StreamBuilder( + stream: teto.allPlayers, + builder: (context, snapshot) { + switch (snapshot.connectionState) { + case ConnectionState.none: + return Center(child: Text('none case of StreamBuilder')); + case ConnectionState.waiting: + case ConnectionState.active: + final allPlayers = (snapshot.data != null) ? snapshot.data as Map> : >{}; + List keys = allPlayers.keys.toList(); + print(allPlayers.toString()); + return NestedScrollView( + headerSliverBuilder: (context, value) { + return [ + SliverToBoxAdapter( + child: Padding( + padding: const EdgeInsets.only(left: 16), + child: Text( + 'There is ${allPlayers.length} players', + style: TextStyle(color: Colors.white, fontSize: 25), + ), + )), + const SliverToBoxAdapter(child: Divider()) + ]; + }, + body: ListView.builder( + itemCount: allPlayers.length, + 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}"), + onTap: () {}, + ); + })); + case ConnectionState.done: + return Center( + child: Text('done case of StreamBuilder', + style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 42), textAlign: TextAlign.center)); + } + })), + ); + } +}