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 'dart:math';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:vector_math/vector_math.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 noTrRd = 60.9;
|
||||||
const double apmWeight = 1;
|
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;
|
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");
|
//developer.log("TetrioPlayer.fromJson $stateTime: $json", name: "data_objects/tetrio");
|
||||||
userId = json['_id'];
|
userId = json['_id'];
|
||||||
username = json['username'];
|
username = json['username'];
|
||||||
|
@ -112,25 +109,6 @@ class TetrioPlayer {
|
||||||
friendCount = json['friend_count'] ?? 0;
|
friendCount = json['friend_count'] ?? 0;
|
||||||
badstanding = json['badstanding'];
|
badstanding = json['badstanding'];
|
||||||
botmaster = json['botmaster'];
|
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() {
|
Map<String, dynamic> toJson() {
|
||||||
|
|
|
@ -30,6 +30,7 @@ const String createTetrioUsersToTrack = '''
|
||||||
class TetrioService extends DB {
|
class TetrioService extends DB {
|
||||||
Map<String, List<TetrioPlayer>> _players = {};
|
Map<String, List<TetrioPlayer>> _players = {};
|
||||||
final Map<String, TetrioPlayer> _playersCache = {};
|
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}
|
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();
|
static final TetrioService _shared = TetrioService._sharedInstance();
|
||||||
factory TetrioService() => _shared;
|
factory TetrioService() => _shared;
|
||||||
|
@ -94,10 +95,6 @@ class TetrioService extends DB {
|
||||||
if (jsonDecode(response.body)['success']) {
|
if (jsonDecode(response.body)['success']) {
|
||||||
TetraLeagueAlphaStream stream = TetraLeagueAlphaStream.fromJson(
|
TetraLeagueAlphaStream stream = TetraLeagueAlphaStream.fromJson(
|
||||||
jsonDecode(response.body)['data']['records'], userID);
|
jsonDecode(response.body)['data']['records'], userID);
|
||||||
// if (addToDB) {
|
|
||||||
// await ensureDbIsOpen();
|
|
||||||
// storeState(player);
|
|
||||||
// }
|
|
||||||
developer.log("getTLStream: $userID stream retrieved and cached", name: "services/tetrio_crud");
|
developer.log("getTLStream: $userID stream retrieved and cached", name: "services/tetrio_crud");
|
||||||
_tlStreamsCache[jsonDecode(response.body)['cache']['cached_until'].toString()] = stream;
|
_tlStreamsCache[jsonDecode(response.body)['cache']['cached_until'].toString()] = stream;
|
||||||
return 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 {
|
Future<void> createPlayer(TetrioPlayer tetrioPlayer) async {
|
||||||
ensureDbIsOpen();
|
ensureDbIsOpen();
|
||||||
final db = getDatabaseOrThrow();
|
final db = getDatabaseOrThrow();
|
||||||
|
@ -216,7 +254,7 @@ class TetrioService extends DB {
|
||||||
} else {
|
} else {
|
||||||
dynamic rawStates = results.first['jsonStates'] as String;
|
dynamic rawStates = results.first['jsonStates'] as String;
|
||||||
rawStates = json.decode(rawStates);
|
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.removeWhere((key, value) => key == id);
|
||||||
_players.addEntries({states.last.userId: states}.entries);
|
_players.addEntries({states.last.userId: states}.entries);
|
||||||
_tetrioStreamController.add(_players);
|
_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{
|
try{
|
||||||
var cached = _playersCache.entries.firstWhere((element) => element.value.userId == user || element.value.username == user);
|
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())){
|
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 (response.statusCode == 200) {
|
||||||
if (jsonDecode(response.body)['success']) {
|
if (jsonDecode(response.body)['success']) {
|
||||||
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));
|
||||||
if (addToDB) {
|
|
||||||
await ensureDbIsOpen();
|
|
||||||
storeState(player);
|
|
||||||
}
|
|
||||||
developer.log("fetchPlayer: $user retrieved and cached", name: "services/tetrio_crud");
|
developer.log("fetchPlayer: $user retrieved and cached", name: "services/tetrio_crud");
|
||||||
_playersCache[jsonDecode(response.body)['cache']['cached_until'].toString()] = player;
|
_playersCache[jsonDecode(response.body)['cache']['cached_until'].toString()] = player;
|
||||||
return player;
|
return player;
|
||||||
|
@ -272,7 +306,7 @@ class TetrioService extends DB {
|
||||||
// what the fuck am i doing here?
|
// what the fuck am i doing here?
|
||||||
var test = json.decode(row['jsonStates'] as String);
|
var test = json.decode(row['jsonStates'] as String);
|
||||||
List<TetrioPlayer> states = [];
|
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);
|
data.addEntries({states.last.userId: states}.entries);
|
||||||
return data;
|
return data;
|
||||||
});
|
});
|
||||||
|
|
|
@ -45,7 +45,7 @@ class CompareState extends State<CompareView> {
|
||||||
|
|
||||||
void fetchRedSide(String user) async {
|
void fetchRedSide(String user) async {
|
||||||
try {
|
try {
|
||||||
theRedSide = await teto.fetchPlayer(user, false);
|
theRedSide = await teto.fetchPlayer(user);
|
||||||
late List<TetrioPlayer> states;
|
late List<TetrioPlayer> states;
|
||||||
try{
|
try{
|
||||||
states = await teto.getPlayer(theRedSide!.userId);
|
states = await teto.getPlayer(theRedSide!.userId);
|
||||||
|
@ -73,7 +73,7 @@ class CompareState extends State<CompareView> {
|
||||||
|
|
||||||
void fetchGreenSide(String user) async {
|
void fetchGreenSide(String user) async {
|
||||||
try {
|
try {
|
||||||
theGreenSide = await teto.fetchPlayer(user, false);
|
theGreenSide = await teto.fetchPlayer(user);
|
||||||
late List<TetrioPlayer> states;
|
late List<TetrioPlayer> states;
|
||||||
greenSideStates = null;
|
greenSideStates = null;
|
||||||
try{
|
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/tl_thingy.dart';
|
||||||
import 'package:tetra_stats/widgets/user_thingy.dart';
|
import 'package:tetra_stats/widgets/user_thingy.dart';
|
||||||
|
|
||||||
|
late Future<List> me;
|
||||||
String _searchFor = "dan63047";
|
String _searchFor = "dan63047";
|
||||||
Future<TetrioPlayer>? me;
|
String _titleNickname = "dan63047";
|
||||||
final TetrioService teto = TetrioService();
|
final TetrioService teto = TetrioService();
|
||||||
late SharedPreferences prefs;
|
late SharedPreferences prefs;
|
||||||
const allowedHeightForPlayerIdInPixels = 40.0;
|
const allowedHeightForPlayerIdInPixels = 40.0;
|
||||||
|
@ -23,7 +24,7 @@ final NumberFormat f2 = NumberFormat.decimalPatternDigits(decimalDigits: 2);
|
||||||
class MainView extends StatefulWidget {
|
class MainView extends StatefulWidget {
|
||||||
const MainView({Key? key}) : super(key: key);
|
const MainView({Key? key}) : super(key: key);
|
||||||
|
|
||||||
String get title => "Tetra Stats: $_searchFor";
|
String get title => "Tetra Stats: $_titleNickname";
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<MainView> createState() => _MainState();
|
State<MainView> createState() => _MainState();
|
||||||
|
@ -38,6 +39,7 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
||||||
final List<Widget> myTabs = [
|
final List<Widget> myTabs = [
|
||||||
const Tab(text: "Tetra League"),
|
const Tab(text: "Tetra League"),
|
||||||
const Tab(text: "TL Records"),
|
const Tab(text: "TL Records"),
|
||||||
|
const Tab(text: "TL History"),
|
||||||
const Tab(text: "40 Lines"),
|
const Tab(text: "40 Lines"),
|
||||||
const Tab(text: "Blitz"),
|
const Tab(text: "Blitz"),
|
||||||
const Tab(text: "Other"),
|
const Tab(text: "Other"),
|
||||||
|
@ -77,7 +79,7 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
||||||
void initState() {
|
void initState() {
|
||||||
teto.open();
|
teto.open();
|
||||||
_scrollController = ScrollController();
|
_scrollController = ScrollController();
|
||||||
_tabController = TabController(length: 5, vsync: this);
|
_tabController = TabController(length: 6, vsync: this);
|
||||||
_getPreferences()
|
_getPreferences()
|
||||||
.then((value) => changePlayer(prefs.getString("player") ?? "dan63047"));
|
.then((value) => changePlayer(prefs.getString("player") ?? "dan63047"));
|
||||||
super.initState();
|
super.initState();
|
||||||
|
@ -96,12 +98,20 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
||||||
|
|
||||||
void changePlayer(String player) {
|
void changePlayer(String player) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_tabController.animateTo(0, duration: const Duration(milliseconds: 300));
|
|
||||||
_searchFor = player;
|
_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() {
|
void _justUpdate() {
|
||||||
setState(() {});
|
setState(() {});
|
||||||
}
|
}
|
||||||
|
@ -173,7 +183,7 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: FutureBuilder<TetrioPlayer>(
|
child: FutureBuilder<List<dynamic>>(
|
||||||
future: me,
|
future: me,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
switch (snapshot.connectionState) {
|
switch (snapshot.connectionState) {
|
||||||
|
@ -197,18 +207,13 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
||||||
case ConnectionState.done:
|
case ConnectionState.done:
|
||||||
//bool bigScreen = MediaQuery.of(context).size.width > 1024;
|
//bool bigScreen = MediaQuery.of(context).size.width > 1024;
|
||||||
if (snapshot.hasData) {
|
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(
|
return NestedScrollView(
|
||||||
controller: _scrollController,
|
controller: _scrollController,
|
||||||
headerSliverBuilder: (context, value) {
|
headerSliverBuilder: (context, value) {
|
||||||
return [
|
return [
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: UserThingy(
|
child: UserThingy(
|
||||||
player: snapshot.data!,
|
player: snapshot.data![0],
|
||||||
showStateTimestamp: false,
|
showStateTimestamp: false,
|
||||||
setState: _justUpdate,
|
setState: _justUpdate,
|
||||||
)),
|
)),
|
||||||
|
@ -217,9 +222,6 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
||||||
controller: _tabController,
|
controller: _tabController,
|
||||||
isScrollable: true,
|
isScrollable: true,
|
||||||
tabs: myTabs,
|
tabs: myTabs,
|
||||||
onTap: (int tabId) {
|
|
||||||
setState(() {});
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
@ -228,19 +230,20 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
||||||
controller: _tabController,
|
controller: _tabController,
|
||||||
children: [
|
children: [
|
||||||
TLThingy(
|
TLThingy(
|
||||||
tl: snapshot.data!.tlSeason1,
|
tl: snapshot.data![0].tlSeason1,
|
||||||
userID: snapshot.data!.userId),
|
userID: snapshot.data![0].userId),
|
||||||
_TLRecords(userID: snapshot.data!.userId),
|
_TLRecords(userID: snapshot.data![0].userId),
|
||||||
|
Text("kekwa"),
|
||||||
_RecordThingy(
|
_RecordThingy(
|
||||||
record: (snapshot.data!.sprint.isNotEmpty)
|
record: (snapshot.data![1]['sprint'].isNotEmpty)
|
||||||
? snapshot.data!.sprint[0]
|
? snapshot.data![1]['sprint'][0]
|
||||||
: null),
|
: null),
|
||||||
_RecordThingy(
|
_RecordThingy(
|
||||||
record: (snapshot.data!.blitz.isNotEmpty)
|
record: (snapshot.data![1]['blitz'].isNotEmpty)
|
||||||
? snapshot.data!.blitz[0]
|
? snapshot.data![1]['blitz'][0]
|
||||||
: null),
|
: null),
|
||||||
_OtherThingy(
|
_OtherThingy(
|
||||||
zen: snapshot.data!.zen, bio: snapshot.data!.bio)
|
zen: snapshot.data![1]['zen'], bio: snapshot.data![0].bio)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in New Issue