Main view now finally have a proper fetch function
Also prepared for making TL history function
This commit is contained in:
parent
ffbe76e5cc
commit
974294f167
|
@ -1,9 +1,6 @@
|
|||
import 'dart:math';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:vector_math/vector_math.dart';
|
||||
import 'dart:developer' as developer;
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'dart:convert';
|
||||
|
||||
const double noTrRd = 60.9;
|
||||
const double apmWeight = 1;
|
||||
|
@ -84,7 +81,7 @@ class TetrioPlayer {
|
|||
|
||||
double get level => pow((xp / 500), 0.6) + (xp / (5000 + (max(0, xp - 4 * pow(10, 6)) / 5000))) + 1;
|
||||
|
||||
TetrioPlayer.fromJson(Map<String, dynamic> json, DateTime stateTime, bool fetchRecords) {
|
||||
TetrioPlayer.fromJson(Map<String, dynamic> json, DateTime stateTime) {
|
||||
//developer.log("TetrioPlayer.fromJson $stateTime: $json", name: "data_objects/tetrio");
|
||||
userId = json['_id'];
|
||||
username = json['username'];
|
||||
|
@ -112,25 +109,6 @@ class TetrioPlayer {
|
|||
friendCount = json['friend_count'] ?? 0;
|
||||
badstanding = json['badstanding'];
|
||||
botmaster = json['botmaster'];
|
||||
if (fetchRecords) {
|
||||
var url = Uri.https('ch.tetr.io', 'api/users/$userId/records');
|
||||
Future response = http.get(url);
|
||||
response.then((value) {
|
||||
if (value.statusCode == 200) {
|
||||
Map jsonRecords = jsonDecode(value.body);
|
||||
sprint = jsonRecords['data']['records']['40l']['record'] != null
|
||||
? [RecordSingle.fromJson(jsonRecords['data']['records']['40l']['record'], jsonRecords['data']['records']['40l']['rank'])]
|
||||
: [];
|
||||
blitz = jsonRecords['data']['records']['blitz']['record'] != null
|
||||
? [RecordSingle.fromJson(jsonRecords['data']['records']['blitz']['record'], jsonRecords['data']['records']['blitz']['rank'])]
|
||||
: [];
|
||||
zen = TetrioZen.fromJson(jsonRecords['data']['zen']);
|
||||
} else {
|
||||
developer.log("TetrioPlayer.fromJson exception", name: "data_objects/tetrio", error: value.statusCode);
|
||||
throw Exception('Failed to fetch player');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
|
|
|
@ -30,6 +30,7 @@ const String createTetrioUsersToTrack = '''
|
|||
class TetrioService extends DB {
|
||||
Map<String, List<TetrioPlayer>> _players = {};
|
||||
final Map<String, TetrioPlayer> _playersCache = {};
|
||||
final Map<String, Map<String, dynamic>> _recordsCache = {};
|
||||
final Map<String, TetraLeagueAlphaStream> _tlStreamsCache = {}; // i'm trying to respect oskware api It should look something like {"cached_until": TetrioPlayer}
|
||||
static final TetrioService _shared = TetrioService._sharedInstance();
|
||||
factory TetrioService() => _shared;
|
||||
|
@ -94,10 +95,6 @@ class TetrioService extends DB {
|
|||
if (jsonDecode(response.body)['success']) {
|
||||
TetraLeagueAlphaStream stream = TetraLeagueAlphaStream.fromJson(
|
||||
jsonDecode(response.body)['data']['records'], userID);
|
||||
// if (addToDB) {
|
||||
// await ensureDbIsOpen();
|
||||
// storeState(player);
|
||||
// }
|
||||
developer.log("getTLStream: $userID stream retrieved and cached", name: "services/tetrio_crud");
|
||||
_tlStreamsCache[jsonDecode(response.body)['cache']['cached_until'].toString()] = stream;
|
||||
return stream;
|
||||
|
@ -111,6 +108,47 @@ class TetrioService extends DB {
|
|||
}
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> fetchRecords(String userID) async {
|
||||
try{
|
||||
var cached = _recordsCache.entries.firstWhere((element) => element.value['user'] == userID);
|
||||
if (DateTime.fromMillisecondsSinceEpoch(int.parse(cached.key.toString()), isUtc: true).isAfter(DateTime.now())){
|
||||
developer.log("fetchRecords: $userID records retrieved from cache, that expires ${DateTime.fromMillisecondsSinceEpoch(int.parse(cached.key.toString()), isUtc: true)}", name: "services/tetrio_crud");
|
||||
return cached.value;
|
||||
}else{
|
||||
_recordsCache.remove(cached.key);
|
||||
developer.log("fetchRecords: $userID records expired (${DateTime.fromMillisecondsSinceEpoch(int.parse(cached.key.toString()), isUtc: true)})", name: "services/tetrio_crud");
|
||||
}
|
||||
}catch(e){
|
||||
developer.log("fetchRecords: Trying to retrieve $userID records", name: "services/tetrio_crud");
|
||||
}
|
||||
|
||||
var url = Uri.https('ch.tetr.io', 'api/users/${userID.toLowerCase().trim()}/records');
|
||||
final response = await http.get(url);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
if (jsonDecode(response.body)['success']) {
|
||||
Map jsonRecords = jsonDecode(response.body);
|
||||
var sprint = jsonRecords['data']['records']['40l']['record'] != null
|
||||
? [RecordSingle.fromJson(jsonRecords['data']['records']['40l']['record'], jsonRecords['data']['records']['40l']['rank'])]
|
||||
: [];
|
||||
var blitz = jsonRecords['data']['records']['blitz']['record'] != null
|
||||
? [RecordSingle.fromJson(jsonRecords['data']['records']['blitz']['record'], jsonRecords['data']['records']['blitz']['rank'])]
|
||||
: [];
|
||||
var zen = TetrioZen.fromJson(jsonRecords['data']['zen']);
|
||||
Map<String, dynamic> map = {"user": userID.toLowerCase().trim(), "sprint": sprint, "blitz": blitz, "zen": zen};
|
||||
developer.log("fetchRecords: $userID stream retrieved and cached", name: "services/tetrio_crud");
|
||||
_recordsCache[jsonDecode(response.body)['cache']['cached_until'].toString()] = map;
|
||||
return map;
|
||||
} else {
|
||||
developer.log("fetchRecords User dosen't exist", name: "services/tetrio_crud", error: response.body);
|
||||
throw Exception("User doesn't exist");
|
||||
}
|
||||
} else {
|
||||
developer.log("fetchRecords Failed to fetch records", name: "services/tetrio_crud", error: response.statusCode);
|
||||
throw Exception('Failed to fetch player');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> createPlayer(TetrioPlayer tetrioPlayer) async {
|
||||
ensureDbIsOpen();
|
||||
final db = getDatabaseOrThrow();
|
||||
|
@ -216,7 +254,7 @@ class TetrioService extends DB {
|
|||
} else {
|
||||
dynamic rawStates = results.first['jsonStates'] as String;
|
||||
rawStates = json.decode(rawStates);
|
||||
rawStates.forEach((k, v) => states.add(TetrioPlayer.fromJson(v, DateTime.fromMillisecondsSinceEpoch(int.parse(k)), false)));
|
||||
rawStates.forEach((k, v) => states.add(TetrioPlayer.fromJson(v, DateTime.fromMillisecondsSinceEpoch(int.parse(k)))));
|
||||
_players.removeWhere((key, value) => key == id);
|
||||
_players.addEntries({states.last.userId: states}.entries);
|
||||
_tetrioStreamController.add(_players);
|
||||
|
@ -224,7 +262,7 @@ class TetrioService extends DB {
|
|||
}
|
||||
}
|
||||
|
||||
Future<TetrioPlayer> fetchPlayer(String user, bool addToDB) async {
|
||||
Future<TetrioPlayer> fetchPlayer(String user) async {
|
||||
try{
|
||||
var cached = _playersCache.entries.firstWhere((element) => element.value.userId == user || element.value.username == user);
|
||||
if (DateTime.fromMillisecondsSinceEpoch(int.parse(cached.key.toString()), isUtc: true).isAfter(DateTime.now())){
|
||||
|
@ -244,11 +282,7 @@ class TetrioService extends DB {
|
|||
if (response.statusCode == 200) {
|
||||
if (jsonDecode(response.body)['success']) {
|
||||
TetrioPlayer player = TetrioPlayer.fromJson(
|
||||
jsonDecode(response.body)['data']['user'], DateTime.fromMillisecondsSinceEpoch(jsonDecode(response.body)['cache']['cached_at'], isUtc: true), true);
|
||||
if (addToDB) {
|
||||
await ensureDbIsOpen();
|
||||
storeState(player);
|
||||
}
|
||||
jsonDecode(response.body)['data']['user'], DateTime.fromMillisecondsSinceEpoch(jsonDecode(response.body)['cache']['cached_at'], isUtc: true));
|
||||
developer.log("fetchPlayer: $user retrieved and cached", name: "services/tetrio_crud");
|
||||
_playersCache[jsonDecode(response.body)['cache']['cached_until'].toString()] = player;
|
||||
return player;
|
||||
|
@ -272,7 +306,7 @@ class TetrioService extends DB {
|
|||
// what the fuck am i doing here?
|
||||
var test = json.decode(row['jsonStates'] as String);
|
||||
List<TetrioPlayer> states = [];
|
||||
test.forEach((k, v) => states.add(TetrioPlayer.fromJson(v, DateTime.fromMillisecondsSinceEpoch(int.parse(k)), false)));
|
||||
test.forEach((k, v) => states.add(TetrioPlayer.fromJson(v, DateTime.fromMillisecondsSinceEpoch(int.parse(k)))));
|
||||
data.addEntries({states.last.userId: states}.entries);
|
||||
return data;
|
||||
});
|
||||
|
|
|
@ -45,7 +45,7 @@ class CompareState extends State<CompareView> {
|
|||
|
||||
void fetchRedSide(String user) async {
|
||||
try {
|
||||
theRedSide = await teto.fetchPlayer(user, false);
|
||||
theRedSide = await teto.fetchPlayer(user);
|
||||
late List<TetrioPlayer> states;
|
||||
try{
|
||||
states = await teto.getPlayer(theRedSide!.userId);
|
||||
|
@ -73,7 +73,7 @@ class CompareState extends State<CompareView> {
|
|||
|
||||
void fetchGreenSide(String user) async {
|
||||
try {
|
||||
theGreenSide = await teto.fetchPlayer(user, false);
|
||||
theGreenSide = await teto.fetchPlayer(user);
|
||||
late List<TetrioPlayer> states;
|
||||
greenSideStates = null;
|
||||
try{
|
||||
|
|
|
@ -10,8 +10,9 @@ import 'package:tetra_stats/widgets/stat_sell_num.dart';
|
|||
import 'package:tetra_stats/widgets/tl_thingy.dart';
|
||||
import 'package:tetra_stats/widgets/user_thingy.dart';
|
||||
|
||||
late Future<List> me;
|
||||
String _searchFor = "dan63047";
|
||||
Future<TetrioPlayer>? me;
|
||||
String _titleNickname = "dan63047";
|
||||
final TetrioService teto = TetrioService();
|
||||
late SharedPreferences prefs;
|
||||
const allowedHeightForPlayerIdInPixels = 40.0;
|
||||
|
@ -23,7 +24,7 @@ final NumberFormat f2 = NumberFormat.decimalPatternDigits(decimalDigits: 2);
|
|||
class MainView extends StatefulWidget {
|
||||
const MainView({Key? key}) : super(key: key);
|
||||
|
||||
String get title => "Tetra Stats: $_searchFor";
|
||||
String get title => "Tetra Stats: $_titleNickname";
|
||||
|
||||
@override
|
||||
State<MainView> createState() => _MainState();
|
||||
|
@ -38,6 +39,7 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
|||
final List<Widget> myTabs = [
|
||||
const Tab(text: "Tetra League"),
|
||||
const Tab(text: "TL Records"),
|
||||
const Tab(text: "TL History"),
|
||||
const Tab(text: "40 Lines"),
|
||||
const Tab(text: "Blitz"),
|
||||
const Tab(text: "Other"),
|
||||
|
@ -77,7 +79,7 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
|||
void initState() {
|
||||
teto.open();
|
||||
_scrollController = ScrollController();
|
||||
_tabController = TabController(length: 5, vsync: this);
|
||||
_tabController = TabController(length: 6, vsync: this);
|
||||
_getPreferences()
|
||||
.then((value) => changePlayer(prefs.getString("player") ?? "dan63047"));
|
||||
super.initState();
|
||||
|
@ -96,12 +98,20 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
|||
|
||||
void changePlayer(String player) {
|
||||
setState(() {
|
||||
_tabController.animateTo(0, duration: const Duration(milliseconds: 300));
|
||||
_searchFor = player;
|
||||
me = teto.fetchPlayer(player, false);
|
||||
me = fetch(_searchFor);
|
||||
});
|
||||
}
|
||||
|
||||
Future<List> fetch(String nickOrID) async {
|
||||
TetrioPlayer me = await teto.fetchPlayer(nickOrID);
|
||||
setState((){_titleNickname = me.username;});
|
||||
bool isTracking = await teto.isPlayerTracking(nickOrID);
|
||||
if (isTracking) teto.storeState(me);
|
||||
Map<String, dynamic> records = await teto.fetchRecords(me.userId);
|
||||
return [me, records, isTracking];
|
||||
}
|
||||
|
||||
void _justUpdate() {
|
||||
setState(() {});
|
||||
}
|
||||
|
@ -173,7 +183,7 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
|||
],
|
||||
),
|
||||
body: SafeArea(
|
||||
child: FutureBuilder<TetrioPlayer>(
|
||||
child: FutureBuilder<List<dynamic>>(
|
||||
future: me,
|
||||
builder: (context, snapshot) {
|
||||
switch (snapshot.connectionState) {
|
||||
|
@ -197,18 +207,13 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
|||
case ConnectionState.done:
|
||||
//bool bigScreen = MediaQuery.of(context).size.width > 1024;
|
||||
if (snapshot.hasData) {
|
||||
if (_searchFor.length > 16)
|
||||
_searchFor = snapshot.data!.username;
|
||||
teto.isPlayerTracking(snapshot.data!.userId).then((value) {
|
||||
if (value) teto.storeState(snapshot.data!);
|
||||
});
|
||||
return NestedScrollView(
|
||||
controller: _scrollController,
|
||||
headerSliverBuilder: (context, value) {
|
||||
return [
|
||||
SliverToBoxAdapter(
|
||||
child: UserThingy(
|
||||
player: snapshot.data!,
|
||||
player: snapshot.data![0],
|
||||
showStateTimestamp: false,
|
||||
setState: _justUpdate,
|
||||
)),
|
||||
|
@ -217,9 +222,6 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
|||
controller: _tabController,
|
||||
isScrollable: true,
|
||||
tabs: myTabs,
|
||||
onTap: (int tabId) {
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
),
|
||||
];
|
||||
|
@ -228,19 +230,20 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
|||
controller: _tabController,
|
||||
children: [
|
||||
TLThingy(
|
||||
tl: snapshot.data!.tlSeason1,
|
||||
userID: snapshot.data!.userId),
|
||||
_TLRecords(userID: snapshot.data!.userId),
|
||||
tl: snapshot.data![0].tlSeason1,
|
||||
userID: snapshot.data![0].userId),
|
||||
_TLRecords(userID: snapshot.data![0].userId),
|
||||
Text("kekwa"),
|
||||
_RecordThingy(
|
||||
record: (snapshot.data!.sprint.isNotEmpty)
|
||||
? snapshot.data!.sprint[0]
|
||||
record: (snapshot.data![1]['sprint'].isNotEmpty)
|
||||
? snapshot.data![1]['sprint'][0]
|
||||
: null),
|
||||
_RecordThingy(
|
||||
record: (snapshot.data!.blitz.isNotEmpty)
|
||||
? snapshot.data!.blitz[0]
|
||||
record: (snapshot.data![1]['blitz'].isNotEmpty)
|
||||
? snapshot.data![1]['blitz'][0]
|
||||
: null),
|
||||
_OtherThingy(
|
||||
zen: snapshot.data!.zen, bio: snapshot.data!.bio)
|
||||
zen: snapshot.data![1]['zen'], bio: snapshot.data![0].bio)
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue