IT'S WORKING... Somehow

This commit is contained in:
dan63047 2023-06-08 19:47:33 +03:00
parent 18f02a9c54
commit ff04c27edb
3 changed files with 122 additions and 84 deletions

View File

@ -43,9 +43,9 @@ class DB {
} }
} }
Future<void> _ensureDbIsOpen(DB udb) async { Future<void> ensureDbIsOpen() async {
try { try {
await udb.open(); await open();
} on DatabaseAlreadyOpen { } on DatabaseAlreadyOpen {
// empty // empty
} }

View File

@ -2,6 +2,8 @@ import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:developer' as developer; import 'dart:developer' as developer;
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'dart:async';
import 'package:sqflite/sqflite.dart';
// import 'package:sqflite/sqflite.dart'; // import 'package:sqflite/sqflite.dart';
// import 'package:path_provider/path_provider.dart' show MissingPlatformDirectoryException, getApplicationDocumentsDirectory; // import 'package:path_provider/path_provider.dart' show MissingPlatformDirectoryException, getApplicationDocumentsDirectory;
// import 'package:path/path.dart' show join; // import 'package:path/path.dart' show join;
@ -22,22 +24,34 @@ const String createTetrioUsersTable = '''
PRIMARY KEY("id") PRIMARY KEY("id")
);'''; );''';
class TetrioService { class TetrioService extends DB {
Map<String, List<TetrioPlayer>> _players = {}; Map<String, List<TetrioPlayer>> _players = {};
final _tetrioStreamController = StreamController<Map<String, List<TetrioPlayer>>>.broadcast(); static final TetrioService _shared = TetrioService._sharedInstance();
factory TetrioService() => _shared;
TetrioService(DB udb) { late final StreamController<Map<String, List<TetrioPlayer>>> _tetrioStreamController;
_cachePlayers(udb); TetrioService._sharedInstance() {
_tetrioStreamController = StreamController<Map<String, List<TetrioPlayer>>>.broadcast(onListen: () {
_tetrioStreamController.sink.add(_players);
});
} }
Future<void> _cachePlayers(DB udb) async { @override
final allPlayers = await getAllPlayers(udb: udb); Future<void> open() async {
await super.open();
await _cachePlayers();
}
Stream<Map<String, List<TetrioPlayer>>> get allPlayers => _tetrioStreamController.stream;
Future<void> _cachePlayers() async {
final allPlayers = await getAllPlayers();
_players = allPlayers.toList().first; // ??? _players = allPlayers.toList().first; // ???
_tetrioStreamController.add(_players); _tetrioStreamController.add(_players);
developer.log("_cachePlayers: $_players", name: "services/tetrio_crud"); developer.log("_cachePlayers: $_players", name: "services/tetrio_crud");
} }
Future<void> deletePlayer({required String id, required DB udb}) async { Future<void> deletePlayer({required String id, required DB udb}) async {
await ensureDbIsOpen();
final db = udb.getDatabaseOrThrow(); final db = udb.getDatabaseOrThrow();
final deletedPlayer = await db.delete(tetrioUsersTable, where: '$idCol = ?', whereArgs: [id.toLowerCase()]); final deletedPlayer = await db.delete(tetrioUsersTable, where: '$idCol = ?', whereArgs: [id.toLowerCase()]);
if (deletedPlayer != 1) { if (deletedPlayer != 1) {
@ -58,9 +72,9 @@ class TetrioService {
// } // }
// } // }
Future<void> createPlayer({required TetrioPlayer tetrioPlayer, required DB udb}) async { Future<void> createPlayer(TetrioPlayer tetrioPlayer) async {
_ensureDbIsOpen(udb); ensureDbIsOpen();
final db = udb.getDatabaseOrThrow(); final db = getDatabaseOrThrow();
final results = await db.query(tetrioUsersTable, limit: 1, where: '$idCol = ?', whereArgs: [tetrioPlayer.userId.toLowerCase()]); final results = await db.query(tetrioUsersTable, limit: 1, where: '$idCol = ?', whereArgs: [tetrioPlayer.userId.toLowerCase()]);
if (results.isNotEmpty) { if (results.isNotEmpty) {
throw TetrioPlayerAlreadyExist(); throw TetrioPlayerAlreadyExist();
@ -73,15 +87,15 @@ class TetrioService {
_tetrioStreamController.add(_players); _tetrioStreamController.add(_players);
} }
Future<void> storeState(TetrioPlayer tetrioPlayer, DB udb) async { Future<void> storeState(TetrioPlayer tetrioPlayer) async {
_ensureDbIsOpen(udb); ensureDbIsOpen();
final db = udb.getDatabaseOrThrow(); final db = getDatabaseOrThrow();
late List<TetrioPlayer> states; late List<TetrioPlayer> states;
try { try {
states = await getPlayer(id: tetrioPlayer.userId, udb: udb); states = await getPlayer(tetrioPlayer.userId);
} on TetrioPlayerNotExist { } on TetrioPlayerNotExist {
await createPlayer(tetrioPlayer: tetrioPlayer, udb: udb); await createPlayer(tetrioPlayer);
states = await getPlayer(id: tetrioPlayer.userId, udb: udb); states = await getPlayer(tetrioPlayer.userId);
} }
if (!_players[tetrioPlayer.userId]!.last.isSameState(tetrioPlayer)) states.add(tetrioPlayer); if (!_players[tetrioPlayer.userId]!.last.isSameState(tetrioPlayer)) states.add(tetrioPlayer);
final Map<String, dynamic> statesJson = {}; final Map<String, dynamic> statesJson = {};
@ -94,9 +108,9 @@ class TetrioService {
_tetrioStreamController.add(_players); _tetrioStreamController.add(_players);
} }
Future<List<TetrioPlayer>> getPlayer({required String id, required DB udb}) async { Future<List<TetrioPlayer>> getPlayer(String id) async {
_ensureDbIsOpen(udb); ensureDbIsOpen();
final db = udb.getDatabaseOrThrow(); final db = getDatabaseOrThrow();
List<TetrioPlayer> states = []; List<TetrioPlayer> states = [];
final results = await db.query(tetrioUsersTable, limit: 1, where: '$idCol = ?', whereArgs: [id.toLowerCase()]); final results = await db.query(tetrioUsersTable, limit: 1, where: '$idCol = ?', whereArgs: [id.toLowerCase()]);
if (results.isEmpty) { if (results.isEmpty) {
@ -112,7 +126,7 @@ class TetrioService {
} }
} }
Future<TetrioPlayer> fetchPlayer(String user, DB udb, bool addToDB) async { Future<TetrioPlayer> fetchPlayer(String user, bool addToDB) async {
var url = Uri.https('ch.tetr.io', 'api/users/${user.toLowerCase().trim()}'); var url = Uri.https('ch.tetr.io', 'api/users/${user.toLowerCase().trim()}');
final response = await http.get(url); final response = await http.get(url);
@ -121,8 +135,8 @@ class TetrioService {
TetrioPlayer player = TetrioPlayer.fromJson( TetrioPlayer player = TetrioPlayer.fromJson(
jsonDecode(response.body)['data']['user'], DateTime.fromMillisecondsSinceEpoch(jsonDecode(response.body)['cache']['cached_at'], isUtc: true), true); jsonDecode(response.body)['data']['user'], DateTime.fromMillisecondsSinceEpoch(jsonDecode(response.body)['cache']['cached_at'], isUtc: true), true);
if (addToDB) { if (addToDB) {
_ensureDbIsOpen(udb); await ensureDbIsOpen();
storeState(player, udb); storeState(player);
} }
return player; return player;
} else { } else {
@ -135,17 +149,17 @@ class TetrioService {
} }
} }
Future<void> _ensureDbIsOpen(DB udb) async { // Future<void> _ensureDbIsOpen() async {
try { // try {
await udb.open(); // await open();
} on DatabaseAlreadyOpen { // } on DatabaseAlreadyOpen {
// empty // // empty
} // }
} // }
Future<Iterable<Map<String, List<TetrioPlayer>>>> getAllPlayers({required DB udb}) async { Future<Iterable<Map<String, List<TetrioPlayer>>>> getAllPlayers() async {
await _ensureDbIsOpen(udb); await ensureDbIsOpen();
final db = udb.getDatabaseOrThrow(); final db = getDatabaseOrThrow();
final players = await db.query(tetrioUsersTable); final players = await db.query(tetrioUsersTable);
Map<String, List<TetrioPlayer>> data = {}; Map<String, List<TetrioPlayer>> data = {};
//developer.log("getAllPlayers: $players", name: "services/tetrio_crud"); //developer.log("getAllPlayers: $players", name: "services/tetrio_crud");

View File

@ -17,8 +17,7 @@ extension StringExtension on String {
String _searchFor = "dan63047"; String _searchFor = "dan63047";
Future<TetrioPlayer>? me; Future<TetrioPlayer>? me;
DB db = DB(); final TetrioService teto = TetrioService();
late final TetrioService teto;
late SharedPreferences prefs; late SharedPreferences prefs;
const allowedHeightForPlayerIdInPixels = 40.0; const allowedHeightForPlayerIdInPixels = 40.0;
const allowedHeightForPlayerBioInPixels = 30.0; const allowedHeightForPlayerBioInPixels = 30.0;
@ -76,8 +75,8 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
@override @override
void initState() { void initState() {
db.open(); //teto = TetrioService();
teto = TetrioService(db); teto.open();
_scrollController = ScrollController(); _scrollController = ScrollController();
_tabController = TabController(length: 4, vsync: this); _tabController = TabController(length: 4, vsync: this);
_getPreferences().then((value) => changePlayer(prefs.getString("player") ?? "dan63047")); _getPreferences().then((value) => changePlayer(prefs.getString("player") ?? "dan63047"));
@ -89,7 +88,7 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
void dispose() { void dispose() {
_tabController.dispose(); _tabController.dispose();
_scrollController.dispose(); _scrollController.dispose();
db.close(); //db.close();
super.dispose(); super.dispose();
developer.log("Main view disposed", name: "main_view"); developer.log("Main view disposed", name: "main_view");
} }
@ -102,7 +101,7 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
setState(() { setState(() {
_tabController.animateTo(0, duration: const Duration(milliseconds: 300)); _tabController.animateTo(0, duration: const Duration(milliseconds: 300));
_searchFor = player; _searchFor = player;
me = teto.fetchPlayer(player, db, false); me = teto.fetchPlayer(player, false);
}); });
} }
@ -195,51 +194,62 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
future: me, future: me,
builder: (context, snapshot) { builder: (context, snapshot) {
developer.log("builder ($context): $snapshot", name: "main_view"); developer.log("builder ($context): $snapshot", name: "main_view");
if (snapshot.connectionState == ConnectionState.waiting) { switch (snapshot.connectionState) {
return const Center( case ConnectionState.none:
child: CircularProgressIndicator( return Center(
color: Colors.white, child: Text('none case of FutureBuilder',
)); style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 42), textAlign: TextAlign.center));
} case ConnectionState.waiting:
if (snapshot.hasData) { return const Center(child: CircularProgressIndicator(color: Colors.white));
bool bigScreen = MediaQuery.of(context).size.width > 1024; case ConnectionState.active:
return NestedScrollView( return Center(
controller: _scrollController, child: Text('active case of FutureBuilder',
headerSliverBuilder: (context, value) { style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 42), textAlign: TextAlign.center));
return [ case ConnectionState.done:
SliverToBoxAdapter(child: _UserThingy(player: snapshot.data!)), bool bigScreen = MediaQuery.of(context).size.width > 1024;
SliverToBoxAdapter( if (snapshot.hasData) {
child: TabBar( return NestedScrollView(
controller: _tabController, controller: _scrollController,
isScrollable: true, headerSliverBuilder: (context, value) {
tabs: myTabs, return [
onTap: (int tabId) { SliverToBoxAdapter(child: _UserThingy(player: snapshot.data!)),
setState(() { SliverToBoxAdapter(
developer.log("Tab changed to $tabId", name: "main_view"); child: TabBar(
}); controller: _tabController,
}, isScrollable: true,
), tabs: myTabs,
onTap: (int tabId) {
setState(() {
developer.log("Tab changed to $tabId", name: "main_view");
});
},
),
),
];
},
body: TabBarView(
controller: _tabController,
children: [
_TLThingy(tl: snapshot.data!.tlSeason1, userID: snapshot.data!.userId),
_RecordThingy(record: (snapshot.data!.sprint.isNotEmpty) ? snapshot.data!.sprint[0] : null),
_RecordThingy(record: (snapshot.data!.blitz.isNotEmpty) ? snapshot.data!.blitz[0] : null),
_OtherThingy(zen: snapshot.data!.zen, bio: snapshot.data!.bio)
],
), ),
]; );
}, } else if (snapshot.hasError) {
body: TabBarView( return Center(
controller: _tabController, child:
children: [ Text('${snapshot.error}', style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 42), textAlign: TextAlign.center));
_TLThingy(tl: snapshot.data!.tlSeason1, userID: snapshot.data!.userId), }
_RecordThingy(record: (snapshot.data!.sprint.isNotEmpty) ? snapshot.data!.sprint[0] : null), break;
_RecordThingy(record: (snapshot.data!.blitz.isNotEmpty) ? snapshot.data!.blitz[0] : null), default:
_OtherThingy(zen: snapshot.data!.zen, bio: snapshot.data!.bio) return Center(
], child: Text('default case of FutureBuilder',
), style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 42), textAlign: TextAlign.center));
);
} else if (snapshot.hasError) {
return Center(
child: Text('${snapshot.error}', style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 42), textAlign: TextAlign.center));
} }
return const Center( return Center(
child: CircularProgressIndicator( child: Text('end of FutureBuilder', style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 42), textAlign: TextAlign.center));
color: Colors.white,
));
}, },
), ),
), ),
@ -271,6 +281,20 @@ class NavDrawer extends StatelessWidget {
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
), ),
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:
return Center(child: Text('${snapshot.data}'));
case ConnectionState.done:
return Center(child: Text('done case of StreamBuilder'));
}
},
)
], ],
), ),
); );
@ -452,7 +476,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.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 ${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",