Tetra League Leaderboard
This commit is contained in:
parent
eae9bbedee
commit
9fb74f051c
|
@ -845,10 +845,11 @@ class TetrioPlayersLeaderboard {
|
|||
|
||||
TetrioPlayersLeaderboard(this.type, this.leaderboard);
|
||||
|
||||
TetrioPlayersLeaderboard.fromJson(Map<String, dynamic> json, String type, DateTime ts) {
|
||||
type = type;
|
||||
TetrioPlayersLeaderboard.fromJson(List<dynamic> json, String t, DateTime ts) {
|
||||
type = t;
|
||||
timestamp = ts;
|
||||
for (Map<String, dynamic> entry in json['users']) {
|
||||
leaderboard = [];
|
||||
for (Map<String, dynamic> entry in json) {
|
||||
leaderboard.add(TetrioPlayerFromLeaderboard.fromJson(entry, ts));
|
||||
}
|
||||
}
|
||||
|
@ -862,9 +863,42 @@ class TetrioPlayerFromLeaderboard {
|
|||
String? country;
|
||||
late bool supporter;
|
||||
late bool verified;
|
||||
late TetraLeagueAlpha league;
|
||||
late DateTime timestamp;
|
||||
late int gamesPlayed;
|
||||
late int gamesWon;
|
||||
late double rating;
|
||||
late double glicko;
|
||||
late double rd;
|
||||
late String rank;
|
||||
late String bestRank;
|
||||
late double apm;
|
||||
late double pps;
|
||||
late double vs;
|
||||
late bool decaying;
|
||||
|
||||
TetrioPlayerFromLeaderboard(this.userId, this.username, this.role, this.xp, this.country, this.supporter, this.verified, this.league);
|
||||
TetrioPlayerFromLeaderboard(
|
||||
this.userId,
|
||||
this.username,
|
||||
this.role,
|
||||
this.xp,
|
||||
this.country,
|
||||
this.supporter,
|
||||
this.verified,
|
||||
this.timestamp,
|
||||
this.gamesPlayed,
|
||||
this.gamesWon,
|
||||
this.rating,
|
||||
this.glicko,
|
||||
this.rd,
|
||||
this.rank,
|
||||
this.bestRank,
|
||||
this.apm,
|
||||
this.pps,
|
||||
this.vs,
|
||||
this.decaying);
|
||||
|
||||
get app => apm / (pps * 60);
|
||||
get vsapm => vs / apm;
|
||||
|
||||
TetrioPlayerFromLeaderboard.fromJson(Map<String, dynamic> json, DateTime ts) {
|
||||
userId = json['_id'];
|
||||
|
@ -874,6 +908,17 @@ class TetrioPlayerFromLeaderboard {
|
|||
country = json['country '];
|
||||
supporter = json['supporter'];
|
||||
verified = json['verified'];
|
||||
league = TetraLeagueAlpha.fromJson(json['league'], ts);
|
||||
timestamp = ts;
|
||||
gamesPlayed = json['league']['gamesplayed'];
|
||||
gamesWon = json['league']['gameswon'];
|
||||
rating = json['league']['rating'].toDouble();
|
||||
glicko = json['league']['glicko'].toDouble();
|
||||
rd = json['league']['rd'].toDouble();
|
||||
rank = json['league']['rank'];
|
||||
bestRank = json['league']['bestrank'];
|
||||
apm = json['league']['apm'].toDouble();
|
||||
pps = json['league']['pps'].toDouble();
|
||||
vs = json['league']['vs'].toDouble();
|
||||
decaying = json['league']['decaying'];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:developer' as developer;
|
||||
import 'dart:io';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
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';
|
||||
|
@ -49,6 +52,7 @@ class TetrioService extends DB {
|
|||
Map<String, List<TetrioPlayer>> _players = {};
|
||||
final Map<String, TetrioPlayer> _playersCache = {};
|
||||
final Map<String, Map<String, dynamic>> _recordsCache = {};
|
||||
final Map<String, TetrioPlayersLeaderboard> _leaderboardsCache = {};
|
||||
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;
|
||||
|
@ -91,6 +95,39 @@ class TetrioService extends DB {
|
|||
return player.username;
|
||||
}
|
||||
|
||||
Future<TetrioPlayersLeaderboard> fetchTLLeaderboard() async {
|
||||
try{
|
||||
var cached = _leaderboardsCache.entries.firstWhere((element) => element.value.type == "league");
|
||||
if (DateTime.fromMillisecondsSinceEpoch(int.parse(cached.key.toString()), isUtc: true).isAfter(DateTime.now())){
|
||||
developer.log("fetchTLLeaderboard: Leaderboard retrieved from cache, that expires ${DateTime.fromMillisecondsSinceEpoch(int.parse(cached.key.toString()), isUtc: true)}", name: "services/tetrio_crud");
|
||||
return cached.value;
|
||||
}else{
|
||||
_leaderboardsCache.remove(cached.key);
|
||||
developer.log("fetchTLLeaderboard: Leaderboard expired (${DateTime.fromMillisecondsSinceEpoch(int.parse(cached.key.toString()), isUtc: true)})", name: "services/tetrio_crud");
|
||||
}
|
||||
}catch(e){
|
||||
developer.log("fetchTLLeaderboard: Trying to retrieve leaderboard", name: "services/tetrio_crud");
|
||||
}
|
||||
|
||||
var url = Uri.https('ch.tetr.io', 'api/users/lists/league/all');
|
||||
final response = await http.get(url);
|
||||
if (response.statusCode == 200) {
|
||||
var rawJson = jsonDecode(response.body);
|
||||
if (rawJson['success']) {
|
||||
TetrioPlayersLeaderboard leaderboard = TetrioPlayersLeaderboard.fromJson(rawJson['data']['users'], "league", DateTime.fromMillisecondsSinceEpoch(rawJson['cache']['cached_at']));
|
||||
developer.log("fetchTLLeaderboard: Leaderboard retrieved and cached", name: "services/tetrio_crud");
|
||||
_leaderboardsCache[rawJson['cache']['cached_until'].toString()] = leaderboard;
|
||||
return leaderboard;
|
||||
} else {
|
||||
developer.log("fetchTLLeaderboard: Bruh", name: "services/tetrio_crud", error: rawJson);
|
||||
throw Exception("User doesn't exist");
|
||||
}
|
||||
} else {
|
||||
developer.log("fetchTLLeaderboard: Failed to fetch leaderboard", name: "services/tetrio_crud", error: response.statusCode);
|
||||
throw Exception('Failed to fetch player');
|
||||
}
|
||||
}
|
||||
|
||||
Future<TetraLeagueAlphaStream> getTLStream(String userID) async {
|
||||
try{
|
||||
var cached = _tlStreamsCache.entries.firstWhere((element) => element.value.userId == userID);
|
||||
|
|
|
@ -8,6 +8,7 @@ import 'package:flutter/services.dart';
|
|||
import 'package:tetra_stats/data_objects/tetrio.dart';
|
||||
import 'package:tetra_stats/services/tetrio_crud.dart';
|
||||
import 'package:tetra_stats/services/crud_exceptions.dart';
|
||||
import 'package:tetra_stats/views/tl_leaderboard_view.dart' show TLLeaderboardView;
|
||||
import 'package:tetra_stats/views/tl_match_view.dart' show TlMatchResultView;
|
||||
import 'package:tetra_stats/widgets/stat_sell_num.dart';
|
||||
import 'package:tetra_stats/widgets/tl_thingy.dart';
|
||||
|
@ -235,6 +236,8 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
|||
),
|
||||
],
|
||||
onSelected: (value) {
|
||||
if (value == "tll") {teto.fetchTLLeaderboard();
|
||||
return;}
|
||||
Navigator.pushNamed(context, value);
|
||||
},
|
||||
),
|
||||
|
@ -420,6 +423,20 @@ class _NavDrawerState extends State<NavDrawer> {
|
|||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: ListTile(
|
||||
leading: const Icon(Icons.leaderboard),
|
||||
title: const Text("Tetra League leaderboard"),
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const TLLeaderboardView(),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
];
|
||||
},
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:tetra_stats/data_objects/tetrio.dart';
|
||||
import 'package:tetra_stats/services/tetrio_crud.dart';
|
||||
import 'package:tetra_stats/views/states_view.dart';
|
||||
|
||||
final TetrioService teto = TetrioService();
|
||||
|
||||
class TLLeaderboardView extends StatefulWidget {
|
||||
const TLLeaderboardView({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => TLLeaderboardState();
|
||||
}
|
||||
|
||||
final DateFormat dateFormat = DateFormat.yMMMd().add_Hms();
|
||||
final NumberFormat f2 = NumberFormat.decimalPatternDigits(decimalDigits: 2);
|
||||
|
||||
class TLLeaderboardState extends State<TLLeaderboardView> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text("Tetra League Leaderboard"),
|
||||
),
|
||||
backgroundColor: Colors.black,
|
||||
body: SafeArea(
|
||||
child: FutureBuilder(
|
||||
future: teto.fetchTLLeaderboard(),
|
||||
builder: (context, snapshot) {
|
||||
switch (snapshot.connectionState) {
|
||||
case ConnectionState.none:
|
||||
case ConnectionState.waiting:
|
||||
case ConnectionState.active:
|
||||
return const Center(child: Text('Fetching...'));
|
||||
case ConnectionState.done:
|
||||
final allPlayers = snapshot.data?.leaderboard;
|
||||
return NestedScrollView(
|
||||
headerSliverBuilder: (context, value) {
|
||||
String howManyPlayers(int numberOfPlayers) => Intl.plural(
|
||||
numberOfPlayers,
|
||||
zero: 'Empty list. Press "Track" button in previous view to add current player here',
|
||||
one: 'There is only one player',
|
||||
other: 'There are $numberOfPlayers players',
|
||||
name: 'howManyPeople',
|
||||
args: [numberOfPlayers],
|
||||
desc: 'Description of how many people are seen in a place.',
|
||||
examples: const {'numberOfPeople': 3},
|
||||
);
|
||||
return [
|
||||
SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 16),
|
||||
child: Text(
|
||||
howManyPlayers(allPlayers.length),
|
||||
style: const TextStyle(color: Colors.white, fontSize: 25),
|
||||
),
|
||||
)),
|
||||
const SliverToBoxAdapter(child: Divider())
|
||||
];
|
||||
},
|
||||
body: ListView.builder(
|
||||
itemCount: allPlayers!.length,
|
||||
itemBuilder: (context, index) {
|
||||
return ListTile(
|
||||
leading: Text((index+1).toString(), style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28)),
|
||||
title: Text("${allPlayers[index].username}", style: const TextStyle(fontFamily: "Eurostile Round Extended")),
|
||||
subtitle: Text(
|
||||
"${f2.format(allPlayers[index].apm)} APM, ${f2.format(allPlayers[index].pps)} PPS, ${f2.format(allPlayers[index].vs)} VS, ${f2.format(allPlayers[index].app)} APP, ${f2.format(allPlayers[index].vsapm)} VS/APM"),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text("${f2.format(allPlayers[index].rating)} TR", style: const TextStyle(fontSize: 28)),
|
||||
Image.asset("res/tetrio_tl_alpha_ranks/${allPlayers[index].rank}.png", height: 48),
|
||||
],
|
||||
),
|
||||
onTap: () {
|
||||
// Navigator.push(
|
||||
// context,
|
||||
// MaterialPageRoute(
|
||||
// builder: (context) => StatesView(states: allPlayers!),
|
||||
// ),
|
||||
// );
|
||||
},
|
||||
);
|
||||
}));
|
||||
}
|
||||
})),
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue