A lot of things, 2.0.3 is ready to release
- Can fetch S2 history - New averages - Damage calculator fix - Icon for linux runner - We can build .deb now
This commit is contained in:
parent
0ee2b83bd7
commit
ee3bbe6369
|
@ -62,6 +62,8 @@ jobs:
|
||||||
type: 'zip'
|
type: 'zip'
|
||||||
filename: TetraStats-${{github.ref_name}}-linux.zip
|
filename: TetraStats-${{github.ref_name}}-linux.zip
|
||||||
directory: build/linux/x64/release/bundle
|
directory: build/linux/x64/release/bundle
|
||||||
|
- name: Build .deb package
|
||||||
|
run: dart run flutter_to_debian
|
||||||
- name: Push to Releases
|
- name: Push to Releases
|
||||||
uses: ncipollo/release-action@v1
|
uses: ncipollo/release-action@v1
|
||||||
with:
|
with:
|
||||||
|
@ -69,7 +71,7 @@ jobs:
|
||||||
allowUpdates: true
|
allowUpdates: true
|
||||||
replacesArtifacts: false
|
replacesArtifacts: false
|
||||||
discussionCategory: autobuilded-releases
|
discussionCategory: autobuilded-releases
|
||||||
artifacts: "build/linux/x64/release/bundle/TetraStats-${{github.ref_name}}-linux.zip"
|
artifacts: "build/linux/x64/release/bundle/TetraStats-${{github.ref_name}}-linux.zip,build/linux/x64/release/debian/*"
|
||||||
tag: Auto-${{ github.run_number }}
|
tag: Auto-${{ github.run_number }}
|
||||||
body: Build with GitHub Action workflow
|
body: Build with GitHub Action workflow
|
||||||
token: ${{ secrets.TOKEN }}
|
token: ${{ secrets.TOKEN }}
|
||||||
|
|
34
.metadata
34
.metadata
|
@ -1,11 +1,11 @@
|
||||||
# This file tracks properties of this Flutter project.
|
# This file tracks properties of this Flutter project.
|
||||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||||
#
|
#
|
||||||
# This file should be version controlled.
|
# This file should be version controlled and should not be manually edited.
|
||||||
|
|
||||||
version:
|
version:
|
||||||
revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
|
revision: "b0850beeb25f6d5b10426284f506557f66181b36"
|
||||||
channel: stable
|
channel: "stable"
|
||||||
|
|
||||||
project_type: app
|
project_type: app
|
||||||
|
|
||||||
|
@ -13,26 +13,26 @@ project_type: app
|
||||||
migration:
|
migration:
|
||||||
platforms:
|
platforms:
|
||||||
- platform: root
|
- platform: root
|
||||||
create_revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
|
create_revision: b0850beeb25f6d5b10426284f506557f66181b36
|
||||||
base_revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
|
base_revision: b0850beeb25f6d5b10426284f506557f66181b36
|
||||||
- platform: android
|
- platform: android
|
||||||
create_revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
|
create_revision: b0850beeb25f6d5b10426284f506557f66181b36
|
||||||
base_revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
|
base_revision: b0850beeb25f6d5b10426284f506557f66181b36
|
||||||
- platform: ios
|
- platform: ios
|
||||||
create_revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
|
create_revision: b0850beeb25f6d5b10426284f506557f66181b36
|
||||||
base_revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
|
base_revision: b0850beeb25f6d5b10426284f506557f66181b36
|
||||||
- platform: linux
|
- platform: linux
|
||||||
create_revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
|
create_revision: b0850beeb25f6d5b10426284f506557f66181b36
|
||||||
base_revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
|
base_revision: b0850beeb25f6d5b10426284f506557f66181b36
|
||||||
- platform: macos
|
- platform: macos
|
||||||
create_revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
|
create_revision: b0850beeb25f6d5b10426284f506557f66181b36
|
||||||
base_revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
|
base_revision: b0850beeb25f6d5b10426284f506557f66181b36
|
||||||
- platform: web
|
- platform: web
|
||||||
create_revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
|
create_revision: b0850beeb25f6d5b10426284f506557f66181b36
|
||||||
base_revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
|
base_revision: b0850beeb25f6d5b10426284f506557f66181b36
|
||||||
- platform: windows
|
- platform: windows
|
||||||
create_revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
|
create_revision: b0850beeb25f6d5b10426284f506557f66181b36
|
||||||
base_revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
|
base_revision: b0850beeb25f6d5b10426284f506557f66181b36
|
||||||
|
|
||||||
# User provided section
|
# User provided section
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
package com.dan63.tetra_stats
|
||||||
|
|
||||||
|
import io.flutter.embedding.android.FlutterActivity
|
||||||
|
|
||||||
|
class MainActivity: FlutterActivity()
|
|
@ -2,10 +2,11 @@ flutter_app:
|
||||||
command: tetra_stats
|
command: tetra_stats
|
||||||
arch: x64
|
arch: x64
|
||||||
parent: /usr/local/lib
|
parent: /usr/local/lib
|
||||||
|
nonInteractive: true
|
||||||
|
|
||||||
control:
|
control:
|
||||||
Package: tetra-stats
|
Package: tetra-stats
|
||||||
Version: 0.2.0
|
Version: 2.0.3
|
||||||
Architecture: amd64
|
Architecture: amd64
|
||||||
Essential: no
|
Essential: no
|
||||||
Priority: optional
|
Priority: optional
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[Desktop Entry]
|
[Desktop Entry]
|
||||||
Version=0.2.0
|
Version=2.0.3
|
||||||
Name=Tetra Stats
|
Name=Tetra Stats
|
||||||
GenericName=Tetra Stats
|
GenericName=Tetra Stats
|
||||||
Comment=Track your and other player stats in TETR.IO
|
Comment=Track your and other player stats in TETR.IO
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
import Flutter
|
||||||
|
import UIKit
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
class RunnerTests: XCTestCase {
|
||||||
|
|
||||||
|
func testExample() {
|
||||||
|
// If you add code to the Runner application, consider adding tests here.
|
||||||
|
// See https://developer.apple.com/documentation/xctest for more information about using XCTest.
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
const int currentSeason = 2;
|
const int currentSeason = 2;
|
||||||
final DateTime sprintAndBlitzRelevance = DateTime(2024, 8, 25);
|
final DateTime sprintAndBlitzRelevance = DateTime(2025, 1, 16);
|
||||||
const double noTrRd = 60.9;
|
const double noTrRd = 60.9;
|
||||||
const double apmWeight = 1;
|
const double apmWeight = 1;
|
||||||
const double ppsWeight = 45;
|
const double ppsWeight = 45;
|
||||||
|
@ -210,46 +210,46 @@ const List<Color> achievementColors = [
|
||||||
];
|
];
|
||||||
|
|
||||||
const Map<String, Duration> sprintAverages = {
|
const Map<String, Duration> sprintAverages = {
|
||||||
// based on https://discord.com/channels/673303546107658242/674421736162197515/1277367281264889908
|
// based on https://discord.com/channels/673303546107658242/1260605501754839060/1329448681539244094
|
||||||
'x+': Duration(seconds: 18, milliseconds: 867),
|
'x+': Duration(seconds: 19, milliseconds: 223),
|
||||||
'x': Duration(seconds: 23, milliseconds: 277),
|
'x': Duration(seconds: 24, milliseconds: 832),
|
||||||
'u': Duration(seconds: 28, milliseconds: 853),
|
'u': Duration(seconds: 32, milliseconds: 586),
|
||||||
'ss': Duration(seconds: 35, milliseconds: 173),
|
'ss': Duration(seconds: 40, milliseconds: 011),
|
||||||
's+': Duration(seconds: 39, milliseconds: 028),
|
's+': Duration(seconds: 47, milliseconds: 963),
|
||||||
's': Duration(seconds: 45, milliseconds: 807),
|
's': Duration(seconds: 54, milliseconds: 413),
|
||||||
's-': Duration(seconds: 48, milliseconds: 840),
|
's-': Duration(seconds: 61, milliseconds: 740),
|
||||||
'a+': Duration(seconds: 54, milliseconds: 975),
|
'a+': Duration(seconds: 70, milliseconds: 101),
|
||||||
'a': Duration(seconds: 60, milliseconds: 287),
|
'a': Duration(seconds: 73, milliseconds: 294),
|
||||||
'a-': Duration(seconds: 64, milliseconds: 019),
|
'a-': Duration(seconds: 81, milliseconds: 773),
|
||||||
'b+': Duration(seconds: 76, milliseconds: 531),
|
'b+': Duration(seconds: 88, milliseconds: 647),
|
||||||
'b': Duration(seconds: 77, milliseconds: 635),
|
'b': Duration(seconds: 97, milliseconds: 699),
|
||||||
'b-': Duration(seconds: 92, milliseconds: 279),
|
'b-': Duration(seconds: 105, milliseconds: 721),
|
||||||
'c+': Duration(seconds: 97, milliseconds: 911),
|
'c+': Duration(seconds: 113, milliseconds: 229),
|
||||||
'c': Duration(seconds: 104, milliseconds: 700),
|
'c': Duration(seconds: 124, milliseconds: 740),
|
||||||
'c-': Duration(seconds: 115, milliseconds: 173),
|
'c-': Duration(seconds: 129, milliseconds: 382),
|
||||||
'd+': Duration(seconds: 131, milliseconds: 486),
|
'd+': Duration(seconds: 138, milliseconds: 947),
|
||||||
'd': Duration(seconds: 158, milliseconds: 397),
|
'd': Duration(seconds: 155, milliseconds: 190),
|
||||||
};
|
};
|
||||||
|
|
||||||
const Map<String, int> blitzAverages = {
|
const Map<String, int> blitzAverages = {
|
||||||
'x+': 879378,
|
'x+': 886046,
|
||||||
'x': 677479,
|
'x': 631014,
|
||||||
'u': 485962,
|
'u': 428799,
|
||||||
'ss': 369043,
|
'ss': 296430,
|
||||||
's+': 279242,
|
's+': 212237,
|
||||||
's': 245619,
|
's': 157234,
|
||||||
's-': 199368,
|
's-': 122791,
|
||||||
'a+': 162035,
|
'a+': 103031,
|
||||||
'a': 130949,
|
'a': 90174,
|
||||||
'a-': 111505,
|
'a-': 73474,
|
||||||
'b+': 97251,
|
'b+': 60655,
|
||||||
'b': 83580,
|
'b': 52463,
|
||||||
'b-': 70511,
|
'b-': 43877,
|
||||||
'c+': 56747,
|
'c+': 36594,
|
||||||
'c': 43002,
|
'c': 34014,
|
||||||
'c-': 38925,
|
'c-': 29613,
|
||||||
'd+': 30483,
|
'd+': 31521,
|
||||||
'd': 22513,
|
'd': 23437,
|
||||||
};
|
};
|
||||||
|
|
||||||
List<DateTime> seasonStarts = [
|
List<DateTime> seasonStarts = [
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
/// Locales: 3
|
/// Locales: 3
|
||||||
/// Strings: 2295 (765 per locale)
|
/// Strings: 2295 (765 per locale)
|
||||||
///
|
///
|
||||||
/// Built on 2024-12-31 at 17:29 UTC
|
/// Built on 2025-01-14 at 21:20 UTC
|
||||||
|
|
||||||
// coverage:ignore-file
|
// coverage:ignore-file
|
||||||
// ignore_for_file: type=lint
|
// ignore_for_file: type=lint
|
||||||
|
@ -707,7 +707,7 @@ class _StringsGraphsDestinationEn {
|
||||||
final Translations _root; // ignore: unused_field
|
final Translations _root; // ignore: unused_field
|
||||||
|
|
||||||
// Translations
|
// Translations
|
||||||
String get fetchAndsaveTLHistory => 'Get player history';
|
String get fetchAndsaveTLHistory => 'Fetch History';
|
||||||
String get fetchAndSaveOldTLmatches => 'Get Tetra League matches history';
|
String get fetchAndSaveOldTLmatches => 'Get Tetra League matches history';
|
||||||
String fetchAndsaveTLHistoryResult({required Object number}) => '${number} states was found';
|
String fetchAndsaveTLHistoryResult({required Object number}) => '${number} states was found';
|
||||||
String fetchAndSaveOldTLmatchesResult({required Object number}) => '${number} matches was found';
|
String fetchAndSaveOldTLmatchesResult({required Object number}) => '${number} matches was found';
|
||||||
|
@ -2247,7 +2247,7 @@ class _StringsGraphsDestinationRuRu implements _StringsGraphsDestinationEn {
|
||||||
@override final _StringsRuRu _root; // ignore: unused_field
|
@override final _StringsRuRu _root; // ignore: unused_field
|
||||||
|
|
||||||
// Translations
|
// Translations
|
||||||
@override String get fetchAndsaveTLHistory => 'Получить историю игрока';
|
@override String get fetchAndsaveTLHistory => 'Получить историю';
|
||||||
@override String get fetchAndSaveOldTLmatches => 'Получить историю матчей Тетра Лиги';
|
@override String get fetchAndSaveOldTLmatches => 'Получить историю матчей Тетра Лиги';
|
||||||
@override String fetchAndsaveTLHistoryResult({required Object number}) => '${number} состояний было найдено';
|
@override String fetchAndsaveTLHistoryResult({required Object number}) => '${number} состояний было найдено';
|
||||||
@override String fetchAndSaveOldTLmatchesResult({required Object number}) => '${number} матчей было найдено';
|
@override String fetchAndSaveOldTLmatchesResult({required Object number}) => '${number} матчей было найдено';
|
||||||
|
@ -4925,7 +4925,7 @@ extension on Translations {
|
||||||
case 'actions.ok': return 'OK';
|
case 'actions.ok': return 'OK';
|
||||||
case 'actions.apply': return 'Apply';
|
case 'actions.apply': return 'Apply';
|
||||||
case 'actions.refresh': return 'Refresh';
|
case 'actions.refresh': return 'Refresh';
|
||||||
case 'graphsDestination.fetchAndsaveTLHistory': return 'Get player history';
|
case 'graphsDestination.fetchAndsaveTLHistory': return 'Fetch History';
|
||||||
case 'graphsDestination.fetchAndSaveOldTLmatches': return 'Get Tetra League matches history';
|
case 'graphsDestination.fetchAndSaveOldTLmatches': return 'Get Tetra League matches history';
|
||||||
case 'graphsDestination.fetchAndsaveTLHistoryResult': return ({required Object number}) => '${number} states was found';
|
case 'graphsDestination.fetchAndsaveTLHistoryResult': return ({required Object number}) => '${number} states was found';
|
||||||
case 'graphsDestination.fetchAndSaveOldTLmatchesResult': return ({required Object number}) => '${number} matches was found';
|
case 'graphsDestination.fetchAndSaveOldTLmatchesResult': return ({required Object number}) => '${number} matches was found';
|
||||||
|
@ -5739,7 +5739,7 @@ extension on _StringsRuRu {
|
||||||
case 'actions.ok': return 'ОК';
|
case 'actions.ok': return 'ОК';
|
||||||
case 'actions.apply': return 'Применить';
|
case 'actions.apply': return 'Применить';
|
||||||
case 'actions.refresh': return 'Обновить';
|
case 'actions.refresh': return 'Обновить';
|
||||||
case 'graphsDestination.fetchAndsaveTLHistory': return 'Получить историю игрока';
|
case 'graphsDestination.fetchAndsaveTLHistory': return 'Получить историю';
|
||||||
case 'graphsDestination.fetchAndSaveOldTLmatches': return 'Получить историю матчей Тетра Лиги';
|
case 'graphsDestination.fetchAndSaveOldTLmatches': return 'Получить историю матчей Тетра Лиги';
|
||||||
case 'graphsDestination.fetchAndsaveTLHistoryResult': return ({required Object number}) => '${number} состояний было найдено';
|
case 'graphsDestination.fetchAndsaveTLHistoryResult': return ({required Object number}) => '${number} состояний было найдено';
|
||||||
case 'graphsDestination.fetchAndSaveOldTLmatchesResult': return ({required Object number}) => '${number} матчей было найдено';
|
case 'graphsDestination.fetchAndSaveOldTLmatchesResult': return ({required Object number}) => '${number} матчей было найдено';
|
||||||
|
|
|
@ -88,7 +88,7 @@ class DB {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> checkImportingDB(File db) async {
|
Future<bool> checkImportingDB(File db) async {
|
||||||
final newDB = await openDatabase(db.path);
|
final newDB = await openDatabase(db.path); // TODO: Maybe i should use arguments, that this method provides?
|
||||||
var usersTable = await newDB.rawQuery("PRAGMA table_xinfo(`${tetrioUsersTable}`);");
|
var usersTable = await newDB.rawQuery("PRAGMA table_xinfo(`${tetrioUsersTable}`);");
|
||||||
List<String> usersTableRows = [for (Map<String, Object?> row in usersTable) row["name"] as String];
|
List<String> usersTableRows = [for (Map<String, Object?> row in usersTable) row["name"] as String];
|
||||||
if (!listEquals(usersTableRows, tetrioUsersTableRows)) return false;
|
if (!listEquals(usersTableRows, tetrioUsersTableRows)) return false;
|
||||||
|
|
|
@ -4,9 +4,11 @@ import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:developer' as developer;
|
import 'dart:developer' as developer;
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'dart:math';
|
||||||
import 'package:path/path.dart';
|
import 'package:path/path.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
||||||
|
import 'package:tetra_stats/data_objects/beta_record.dart';
|
||||||
import 'package:tetra_stats/data_objects/cutoff_tetrio.dart';
|
import 'package:tetra_stats/data_objects/cutoff_tetrio.dart';
|
||||||
import 'package:tetra_stats/data_objects/end_context_multi.dart';
|
import 'package:tetra_stats/data_objects/end_context_multi.dart';
|
||||||
import 'package:tetra_stats/data_objects/news.dart';
|
import 'package:tetra_stats/data_objects/news.dart';
|
||||||
|
@ -24,8 +26,6 @@ import 'package:tetra_stats/data_objects/tetrio_multiplayer_replay.dart';
|
||||||
import 'package:tetra_stats/data_objects/tetrio_player.dart';
|
import 'package:tetra_stats/data_objects/tetrio_player.dart';
|
||||||
import 'package:tetra_stats/data_objects/tetrio_player_from_leaderboard.dart';
|
import 'package:tetra_stats/data_objects/tetrio_player_from_leaderboard.dart';
|
||||||
import 'package:tetra_stats/data_objects/tetrio_players_leaderboard.dart';
|
import 'package:tetra_stats/data_objects/tetrio_players_leaderboard.dart';
|
||||||
import 'package:tetra_stats/data_objects/tetrio_zen.dart';
|
|
||||||
import 'package:tetra_stats/data_objects/user_records.dart';
|
|
||||||
import 'package:tetra_stats/main.dart' show packageInfo;
|
import 'package:tetra_stats/main.dart' show packageInfo;
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:tetra_stats/services/custom_http_client.dart';
|
import 'package:tetra_stats/services/custom_http_client.dart';
|
||||||
|
@ -372,7 +372,7 @@ class TetrioService extends DB {
|
||||||
dbPath = join(docsPath.path, dbName);
|
dbPath = join(docsPath.path, dbName);
|
||||||
}
|
}
|
||||||
var dbFile = File(dbPath);
|
var dbFile = File(dbPath);
|
||||||
var dbSize = (await dbFile.stat()).size;
|
var dbSize = kIsWeb ? -1 : (await dbFile.stat()).size;
|
||||||
var dbTLRecordsQuery = (await db.rawQuery('SELECT COUNT(*) FROM `${tetraLeagueMatchesTable}`')).first['COUNT(*)']! as int;
|
var dbTLRecordsQuery = (await db.rawQuery('SELECT COUNT(*) FROM `${tetraLeagueMatchesTable}`')).first['COUNT(*)']! as int;
|
||||||
var dbTLStatesQuery = (await db.rawQuery('SELECT COUNT(*) FROM `${tetrioLeagueTable}`')).first['COUNT(*)']! as int;
|
var dbTLStatesQuery = (await db.rawQuery('SELECT COUNT(*) FROM `${tetrioLeagueTable}`')).first['COUNT(*)']! as int;
|
||||||
return (dbSize, dbTLRecordsQuery, dbTLStatesQuery);
|
return (dbSize, dbTLRecordsQuery, dbTLStatesQuery);
|
||||||
|
@ -673,8 +673,7 @@ class TetrioService extends DB {
|
||||||
|
|
||||||
/// Retrieves Tetra League history from p1nkl0bst3r api for a player with given [id]. Returns a list of states
|
/// Retrieves Tetra League history from p1nkl0bst3r api for a player with given [id]. Returns a list of states
|
||||||
/// (state = instance of [TetrioPlayer] at some point of time). Can throw an exception if fails to retrieve data.
|
/// (state = instance of [TetrioPlayer] at some point of time). Can throw an exception if fails to retrieve data.
|
||||||
Future<List<TetraLeague>> fetchAndsaveTLHistory(String id, int season) async {
|
Future<List<TetraLeague>> fetchAndsaveS1TLHistory(String id) async {
|
||||||
// TODO: find le way to get season 2 history
|
|
||||||
Uri url;
|
Uri url;
|
||||||
if (kIsWeb) {
|
if (kIsWeb) {
|
||||||
url = Uri.https(webVersionDomain, 'oskware_bridge.php', {"endpoint": "TLHistory", "user": id});
|
url = Uri.https(webVersionDomain, 'oskware_bridge.php', {"endpoint": "TLHistory", "user": id});
|
||||||
|
@ -745,6 +744,70 @@ class TetrioService extends DB {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<List<TetraLeague>> fetchAndsaveS2TLHistory(String id) async {
|
||||||
|
final db = getDatabaseOrThrow();
|
||||||
|
List<BetaRecord> records = [];
|
||||||
|
int entries = 100;
|
||||||
|
String? prisecter;
|
||||||
|
while (entries > 0){
|
||||||
|
TetraLeagueBetaStream stream = await fetchTLStream(id, prisecter: prisecter);
|
||||||
|
if (stream.records.isEmpty) break;
|
||||||
|
records.addAll(stream.records);
|
||||||
|
prisecter = stream.records.last.prisecter.toString();
|
||||||
|
entries = stream.records.length;
|
||||||
|
}
|
||||||
|
//TetraLeague currentState = await fetchTLSummary(id);
|
||||||
|
List<TetraLeague> states = [];
|
||||||
|
//states.add(currentState);
|
||||||
|
int gp = 0;
|
||||||
|
int gw = 0;
|
||||||
|
List<double> last10apm = [];
|
||||||
|
List<double> last10pps = [];
|
||||||
|
List<double> last10vs = [];
|
||||||
|
int bestRankIndex = -1; // -1 - Z; 0 - D, 1 - D+ ... 18 - X+
|
||||||
|
Batch batch = db.batch();
|
||||||
|
for (BetaRecord match in records.reversed){
|
||||||
|
gp++;
|
||||||
|
if (match.extras.result.contains("victory")) gw++;
|
||||||
|
last10apm.add(match.results.leaderboard.firstWhere((e) => e.id == id).stats.apm);
|
||||||
|
if (last10apm.length > 10) last10apm.removeAt(0);
|
||||||
|
last10pps.add(match.results.leaderboard.firstWhere((e) => e.id == id).stats.pps);
|
||||||
|
if (last10pps.length > 10) last10pps.removeAt(0);
|
||||||
|
last10vs.add(match.results.leaderboard.firstWhere((e) => e.id == id).stats.vs);
|
||||||
|
if (last10vs.length > 10) last10vs.removeAt(0);
|
||||||
|
double apm = last10apm.reduce((v, e) => v + e) / last10apm.length;
|
||||||
|
double pps = last10pps.reduce((v, e) => v + e) / last10pps.length;
|
||||||
|
double vs = last10vs.reduce((v, e) => v + e) / last10vs.length;
|
||||||
|
TetraLeague state = TetraLeague(
|
||||||
|
id: id,
|
||||||
|
timestamp: match.ts,
|
||||||
|
gamesPlayed: gp,
|
||||||
|
gamesWon: gw,
|
||||||
|
bestRank: bestRankIndex != -1 ? ranks[bestRankIndex] : "z",
|
||||||
|
decaying: false,
|
||||||
|
tr: match.extras.league[id]?[1]?.tr??-1.0,
|
||||||
|
glicko: match.extras.league[id]?[1]?.glicko,
|
||||||
|
rd: match.extras.league[id]?[1]?.rd,
|
||||||
|
gxe: match.extras.league[id]?[1]?.glicko != null ? 10000 / (1 + pow(10, (((1500 - match.extras.league[id]![1]!.glicko) * pi / sqrt(3 * pow(ln10, 2) * pow(match.extras.league[id]![1]!.rd, 2) + 2500 * (64 * pow(pi, 2) + 147 * pow(ln10, 2))))))) / 100 : -1,
|
||||||
|
rank: match.extras.league[id]?[1]?.rank??"z",
|
||||||
|
percentileRank: match.extras.league[id]?[1]?.rank??"z",
|
||||||
|
percentile: match.extras.league[id]?[1]?.rank != null ? rankCutoffs[match.extras.league[id]![1]!.rank]! : -1,
|
||||||
|
standing: match.extras.league[id]?[1]?.placement??-1,
|
||||||
|
standingLocal: -1,
|
||||||
|
nextAt: -1,
|
||||||
|
prevAt: -1,
|
||||||
|
apm: apm,
|
||||||
|
pps: pps,
|
||||||
|
vs: vs,
|
||||||
|
season: currentSeason
|
||||||
|
);
|
||||||
|
states.add(state);
|
||||||
|
batch.insert(tetrioLeagueTable, state.toJson(), conflictAlgorithm: ConflictAlgorithm.replace);
|
||||||
|
}
|
||||||
|
batch.commit();
|
||||||
|
return states;
|
||||||
|
}
|
||||||
|
|
||||||
/// Docs later
|
/// Docs later
|
||||||
Future<TetraLeagueAlphaStream> fetchAndSaveOldTLmatches(String userID) async {
|
Future<TetraLeagueAlphaStream> fetchAndSaveOldTLmatches(String userID) async {
|
||||||
Uri url;
|
Uri url;
|
||||||
|
@ -1129,38 +1192,29 @@ class TetrioService extends DB {
|
||||||
await db.delete(tetrioTLReplayStatsTable, where: '$idCol = ?', whereArgs: [rID]);
|
await db.delete(tetrioTLReplayStatsTable, where: '$idCol = ?', whereArgs: [rID]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieves Blitz, 40 Lines and Zen records for a given [userID] from Tetra Channel api. Returns `UserRecords`.
|
Future<TetraLeague> fetchTLSummary(String id) async {
|
||||||
/// Throws an exception if fails to retrieve.
|
TetraLeague? cached = _cache.get(id, TetraLeague);
|
||||||
Future<UserRecords> fetchRecords(String userID) async {
|
|
||||||
UserRecords? cached = _cache.get(userID, UserRecords);
|
|
||||||
if (cached != null) return cached;
|
if (cached != null) return cached;
|
||||||
|
|
||||||
Uri url;
|
Uri url;
|
||||||
if (kIsWeb) {
|
if (kIsWeb) {
|
||||||
url = Uri.https(webVersionDomain, 'oskware_bridge.php', {"endpoint": "tetrioUserRecords", "user": userID.toLowerCase().trim()});
|
url = Uri.https(webVersionDomain, 'oskware_bridge.php', {"endpoint": "Summaries", "id": id});
|
||||||
} else {
|
} else {
|
||||||
url = Uri.https('ch.tetr.io', 'api/users/${userID.toLowerCase().trim()}/records');
|
url = Uri.https('ch.tetr.io', 'api/users/$id/summaries/league');
|
||||||
}
|
}
|
||||||
|
|
||||||
try{
|
try{
|
||||||
final response = await client.get(url);
|
final response = await client.get(url);
|
||||||
|
|
||||||
switch (response.statusCode) {
|
switch (response.statusCode) {
|
||||||
case 200:
|
case 200:
|
||||||
if (jsonDecode(response.body)['success']) {
|
if (jsonDecode(response.body)['success']) {
|
||||||
Map jsonRecords = jsonDecode(response.body);
|
developer.log("fetchTLSummary: $id TL state retrieved and cached", name: "services/tetrio_crud");
|
||||||
var sprint = jsonRecords['data']['records']['40l']['record'] != null
|
TetraLeague league = TetraLeague.fromJson(jsonDecode(response.body)['data'], DateTime.now(), currentSeason, id);
|
||||||
? RecordSingle.fromJson(jsonRecords['data']['records']['40l']['record'], jsonRecords['data']['records']['40l']['rank'], jsonRecords['data']['records']['40l']['rank_local'])
|
_cache.store(league, jsonDecode(response.body)['cache']['cached_until']);
|
||||||
: null;
|
return league;
|
||||||
var blitz = jsonRecords['data']['records']['blitz']['record'] != null
|
|
||||||
? RecordSingle.fromJson(jsonRecords['data']['records']['blitz']['record'], jsonRecords['data']['records']['blitz']['rank'], jsonRecords['data']['records']['blitz']['rank_local'])
|
|
||||||
: null;
|
|
||||||
var zen = TetrioZen.fromJson(jsonRecords['data']['zen']);
|
|
||||||
UserRecords result = UserRecords(userID, sprint, blitz, zen);
|
|
||||||
_cache.store(result, jsonDecode(response.body)['cache']['cached_until']);
|
|
||||||
developer.log("fetchRecords: $userID records retrieved and cached", name: "services/tetrio_crud");
|
|
||||||
return result;
|
|
||||||
} else {
|
} else {
|
||||||
developer.log("fetchRecords User dosen't exist", name: "services/tetrio_crud", error: response.body);
|
developer.log("fetchTLSummary: User dosen't exist", name: "services/tetrio_crud", error: response.body);
|
||||||
throw TetrioPlayerNotExist();
|
throw TetrioPlayerNotExist();
|
||||||
}
|
}
|
||||||
case 403:
|
case 403:
|
||||||
|
@ -1175,7 +1229,7 @@ class TetrioService extends DB {
|
||||||
case 504:
|
case 504:
|
||||||
throw TetrioInternalProblem();
|
throw TetrioInternalProblem();
|
||||||
default:
|
default:
|
||||||
developer.log("fetchRecords Failed to fetch records", name: "services/tetrio_crud", error: response.statusCode);
|
developer.log("fetchTLSummary Failed to fetch TL state", name: "services/tetrio_crud", error: response.statusCode);
|
||||||
throw ConnectionIssue(response.statusCode, response.reasonPhrase??"No reason");
|
throw ConnectionIssue(response.statusCode, response.reasonPhrase??"No reason");
|
||||||
}
|
}
|
||||||
} on http.ClientException catch (e, s) {
|
} on http.ClientException catch (e, s) {
|
||||||
|
@ -1427,15 +1481,4 @@ class TetrioService extends DB {
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Future<void> fetchTracked() async {
|
|
||||||
// for (String userID in (await getAllPlayerToTrack())) {
|
|
||||||
// TetrioPlayer player = await fetchPlayer(userID);
|
|
||||||
// storeState(player);
|
|
||||||
// sleep(Durations.extralong4);
|
|
||||||
// TetraLeagueBetaStream matches = await fetchTLStream(userID);
|
|
||||||
// saveTLMatchesFromStream(matches);
|
|
||||||
// sleep(Durations.extralong4);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,7 +76,7 @@ class ClearData{
|
||||||
|
|
||||||
if (rules.combo && rules.comboTable != ComboTables.none) {
|
if (rules.combo && rules.comboTable != ComboTables.none) {
|
||||||
if (combo >= 1){
|
if (combo >= 1){
|
||||||
if (lines == 1 && rules.comboTable != ComboTables.multiplier) damage += combotable[rules.comboTable]![max(0, min(combo - 1, combotable[rules.comboTable]!.length - 1))];
|
if (rules.comboTable != ComboTables.multiplier) damage += combotable[rules.comboTable]![max(0, min(combo - 1, combotable[rules.comboTable]!.length - 1))];
|
||||||
else damage *= (1 + COMBO_BONUS * (combo));
|
else damage *= (1 + COMBO_BONUS * (combo));
|
||||||
}
|
}
|
||||||
if (combo >= 2) {
|
if (combo >= 2) {
|
||||||
|
@ -166,7 +166,7 @@ class _DestinationCalculatorState extends State<DestinationCalculator> {
|
||||||
List<ClearData> clears = [];
|
List<ClearData> clears = [];
|
||||||
Map<String, int> customClearsChoice = {
|
Map<String, int> customClearsChoice = {
|
||||||
t.calcDestination.noSpinClears: 5,
|
t.calcDestination.noSpinClears: 5,
|
||||||
t.calcDestination.spins: 5
|
t.stats.spins: 5
|
||||||
};
|
};
|
||||||
int idCounter = 0;
|
int idCounter = 0;
|
||||||
Rules rules = Rules();
|
Rules rules = Rules();
|
||||||
|
@ -400,7 +400,13 @@ class _DestinationCalculatorState extends State<DestinationCalculator> {
|
||||||
),
|
),
|
||||||
onTap: (){
|
onTap: (){
|
||||||
setState((){
|
setState((){
|
||||||
clears.add(ClearData("${key == t.calcDestination.spins ? "${t.stats.spin} " : ""}${clearNames[min(customClearsChoice[key]!, clearNames.length-1)]} (${customClearsChoice[key]!} ${t.stats.lines})", key == t.calcDestination.spins ? Lineclears.TSPIN_PENTA : Lineclears.PENTA, customClearsChoice[key]!, false, key == t.calcDestination.spins).cloneWith(idCounter));
|
clears.add(ClearData(
|
||||||
|
"${key == t.stats.spins ? "${t.stats.spin} " : ""}${clearNames[min(customClearsChoice[key]!, clearNames.length-1)]} (${customClearsChoice[key]!} ${t.stats.lines})",
|
||||||
|
key == t.stats.spins ? Lineclears.TSPIN_PENTA : Lineclears.PENTA,
|
||||||
|
customClearsChoice[key]!,
|
||||||
|
false,
|
||||||
|
key == t.stats.spins).cloneWith(idCounter)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
idCounter++;
|
idCounter++;
|
||||||
},
|
},
|
||||||
|
|
|
@ -51,6 +51,7 @@ class _DestinationGraphsState extends State<DestinationGraphs> {
|
||||||
ValueNotifier<String> historyPlayerUsername = ValueNotifier("");
|
ValueNotifier<String> historyPlayerUsername = ValueNotifier("");
|
||||||
ValueNotifier<String> historyPlayerAvatarRevizion = ValueNotifier("");
|
ValueNotifier<String> historyPlayerAvatarRevizion = ValueNotifier("");
|
||||||
List<String> excludeRanks = [];
|
List<String> excludeRanks = [];
|
||||||
|
late Future<Map<int, Map<Stats, List<_HistoryChartSpot>>>> playerHistory = getHistoryData(fetchData);
|
||||||
late Future<List<_MyScatterSpot>> futureLeague = getTetraLeagueData(_Xchart, Ychart);
|
late Future<List<_MyScatterSpot>> futureLeague = getTetraLeagueData(_Xchart, Ychart);
|
||||||
String searchLeague = "";
|
String searchLeague = "";
|
||||||
int? TLstatePlayers;
|
int? TLstatePlayers;
|
||||||
|
@ -143,9 +144,11 @@ class _DestinationGraphsState extends State<DestinationGraphs> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Map<int, Map<Stats, List<_HistoryChartSpot>>>> getHistoryData(bool fetchHistory) async {
|
Future<Map<int, Map<Stats, List<_HistoryChartSpot>>>> getHistoryData(bool fetchHistory) async {
|
||||||
|
var playerID = (await teto.fetchPlayer(widget.searchFor)).userId;
|
||||||
if(fetchHistory){
|
if(fetchHistory){
|
||||||
try{
|
try{
|
||||||
var history = await teto.fetchAndsaveTLHistory(widget.searchFor, 1);
|
//var history = await Future.wait([teto.fetchAndsaveS1TLHistory(widget.searchFor), teto.fetchAndsaveS2TLHistory(widget.searchFor)]); // S1 history unavaliable because of certificate issue on p1nkl0bst3r side
|
||||||
|
var history = await teto.fetchAndsaveS2TLHistory(playerID);
|
||||||
if (context.mounted) ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.graphsDestination.fetchAndsaveTLHistoryResult(number: history.length))));
|
if (context.mounted) ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.graphsDestination.fetchAndsaveTLHistoryResult(number: history.length))));
|
||||||
}on TetrioHistoryNotExist{
|
}on TetrioHistoryNotExist{
|
||||||
if (context.mounted) ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.errors.noHistorySaved)));
|
if (context.mounted) ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.errors.noHistorySaved)));
|
||||||
|
@ -159,19 +162,19 @@ class _DestinationGraphsState extends State<DestinationGraphs> {
|
||||||
}
|
}
|
||||||
|
|
||||||
List<List<TetraLeague>> states = await Future.wait<List<TetraLeague>>([
|
List<List<TetraLeague>> states = await Future.wait<List<TetraLeague>>([
|
||||||
teto.getStates(widget.searchFor, season: 1), teto.getStates(widget.searchFor, season: 2),
|
teto.getStates(playerID, season: 1), teto.getStates(playerID, season: 2),
|
||||||
]);
|
]);
|
||||||
Map<int, Map<Stats, List<_HistoryChartSpot>>> historyData = {}; // [season][metric][spot]
|
Map<int, Map<Stats, List<_HistoryChartSpot>>> historyData = {}; // [season][metric][spot]
|
||||||
for (int season = 0; season < currentSeason; season++){
|
for (int season = 0; season < currentSeason; season++){
|
||||||
if (states[season].length >= 2){
|
if (states[season].length >= 2){
|
||||||
Map<Stats, List<_HistoryChartSpot>> statsMap = {};
|
Map<Stats, List<_HistoryChartSpot>> statsMap = {};
|
||||||
for (var stat in Stats.values) statsMap[stat] = [for (var tl in states[season]) if (tl.getStatByEnum(stat) != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.getStatByEnum(stat)!.toDouble())];
|
for (var stat in Stats.values) statsMap[stat] = [for (var tl in states[season]) if (tl.getStatByEnum(stat) != null && tl.getStatByEnum(stat) != -1.00) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.getStatByEnum(stat)!.toDouble())];
|
||||||
historyData[season] = statsMap;
|
historyData[season] = statsMap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fetchData = false;
|
fetchData = false;
|
||||||
|
|
||||||
historyPlayerUsername.value = await teto.getNicknameByID(widget.searchFor);
|
historyPlayerUsername.value = await teto.getNicknameByID(playerID);
|
||||||
|
|
||||||
return historyData;
|
return historyData;
|
||||||
}
|
}
|
||||||
|
@ -234,7 +237,7 @@ class _DestinationGraphsState extends State<DestinationGraphs> {
|
||||||
trendlines:<Trendline>[
|
trendlines:<Trendline>[
|
||||||
Trendline(
|
Trendline(
|
||||||
isVisible: _smooth,
|
isVisible: _smooth,
|
||||||
period: (selectedGraph.length/175).floor(),
|
period: (selectedGraph.length/100).floor(),
|
||||||
type: TrendlineType.movingAverage,
|
type: TrendlineType.movingAverage,
|
||||||
color: Theme.of(context).colorScheme.primary)
|
color: Theme.of(context).colorScheme.primary)
|
||||||
],
|
],
|
||||||
|
@ -250,7 +253,7 @@ class _DestinationGraphsState extends State<DestinationGraphs> {
|
||||||
trendlines:<Trendline>[
|
trendlines:<Trendline>[
|
||||||
Trendline(
|
Trendline(
|
||||||
isVisible: _smooth,
|
isVisible: _smooth,
|
||||||
period: (selectedGraph.length/175).floor(),
|
period: (selectedGraph.length/100).floor(),
|
||||||
type: TrendlineType.movingAverage,
|
type: TrendlineType.movingAverage,
|
||||||
color: Theme.of(context).colorScheme.primary)
|
color: Theme.of(context).colorScheme.primary)
|
||||||
],
|
],
|
||||||
|
@ -532,7 +535,16 @@ class _DestinationGraphsState extends State<DestinationGraphs> {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}, icon: Icon(Icons.filter_alt)),
|
}, icon: Icon(Icons.filter_alt)),
|
||||||
IconButton(onPressed: () => _zoomPanBehavior.reset(), icon: const Icon(Icons.refresh), alignment: Alignment.center,)
|
IconButton(onPressed: () => _zoomPanBehavior.reset(), icon: const Icon(Icons.refresh), alignment: Alignment.center,),
|
||||||
|
if (graph == Graph.history) ElevatedButton.icon(
|
||||||
|
onPressed: (){
|
||||||
|
setState(() {
|
||||||
|
fetchData = true;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
label: Text(t.graphsDestination.fetchAndsaveTLHistory),
|
||||||
|
icon: Icon(Icons.download),
|
||||||
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -581,6 +593,11 @@ class _DestinationGraphsState extends State<DestinationGraphs> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void markNeedsBuild() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class _HistoryChartSpot{
|
class _HistoryChartSpot{
|
||||||
|
|
|
@ -447,7 +447,7 @@ class _DestinationSettings extends State<DestinationSettings> with SingleTickerP
|
||||||
text: TextSpan(
|
text: TextSpan(
|
||||||
style: TextStyle(fontFamily: "Eurostile Round", color: Colors.white),
|
style: TextStyle(fontFamily: "Eurostile Round", color: Colors.white),
|
||||||
children: [
|
children: [
|
||||||
TextSpan(text: "${bytesToSize(snapshot.data!.$1)} ", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28)),
|
TextSpan(text: "${snapshot.data!.$1 == -1 ? "???" : bytesToSize(snapshot.data!.$1)} ", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28)),
|
||||||
TextSpan(text: "${t.settingsDestination.bytesOfDataStored}\n"),
|
TextSpan(text: "${t.settingsDestination.bytesOfDataStored}\n"),
|
||||||
TextSpan(text: "${intf.format(snapshot.data!.$2)} ", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28)),
|
TextSpan(text: "${intf.format(snapshot.data!.$2)} ", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28)),
|
||||||
TextSpan(text: "${t.settingsDestination.TLrecordsSaved}\n"),
|
TextSpan(text: "${t.settingsDestination.TLrecordsSaved}\n"),
|
||||||
|
@ -457,7 +457,7 @@ class _DestinationSettings extends State<DestinationSettings> with SingleTickerP
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (snapshot.hasError){ return FutureError(snapshot); }
|
if (snapshot.hasError){ return SizedBox(height: 500.0, child: FutureError(snapshot)); }
|
||||||
}
|
}
|
||||||
return Text("huh?");
|
return Text("huh?");
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,6 @@ Future<FetchResults> getData(String searchFor, {bool withHistory = false}) async
|
||||||
}else{
|
}else{
|
||||||
player = await teto.fetchPlayer(searchFor); // Otherwise it's probably a user id or username
|
player = await teto.fetchPlayer(searchFor); // Otherwise it's probably a user id or username
|
||||||
}
|
}
|
||||||
|
|
||||||
}on TetrioPlayerNotExist{
|
}on TetrioPlayerNotExist{
|
||||||
return FetchResults(false, null, [], null, null, null, null, null, false, TetrioPlayerNotExist());
|
return FetchResults(false, null, [], null, null, null, null, null, false, TetrioPlayerNotExist());
|
||||||
}
|
}
|
||||||
|
@ -62,7 +61,7 @@ Future<FetchResults> getData(String searchFor, {bool withHistory = false}) async
|
||||||
cutoffs = requests.elementAtOrNull(2);
|
cutoffs = requests.elementAtOrNull(2);
|
||||||
averages = requests.elementAtOrNull(3);
|
averages = requests.elementAtOrNull(3);
|
||||||
|
|
||||||
if(withHistory) await teto.fetchAndsaveTLHistory(player.userId, 1); // Retrieve if needed
|
if(withHistory) await teto.fetchAndsaveS1TLHistory(player.userId); // Retrieve if needed
|
||||||
} on Exception catch (e) {
|
} on Exception catch (e) {
|
||||||
return FetchResults(false, null, [], null, null, null, null, null, false, e);
|
return FetchResults(false, null, [], null, null, null, null, null, false, e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,44 +57,38 @@ class SprintAndBlitzState extends State<SprintAndBlitzView> {
|
||||||
constraints: const BoxConstraints(maxWidth: 600),
|
constraints: const BoxConstraints(maxWidth: 600),
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
child: Column(
|
child: Table(
|
||||||
mainAxisSize: MainAxisSize.min,
|
defaultVerticalAlignment: TableCellVerticalAlignment.middle,
|
||||||
|
border: TableBorder.all(color: Colors.grey.shade900),
|
||||||
|
columnWidths: const {0: FixedColumnWidth(48)},
|
||||||
children: [
|
children: [
|
||||||
Table(
|
TableRow(
|
||||||
defaultVerticalAlignment: TableCellVerticalAlignment.middle,
|
|
||||||
border: TableBorder.all(color: Colors.grey.shade900),
|
|
||||||
columnWidths: const {0: FixedColumnWidth(48)},
|
|
||||||
children: [
|
children: [
|
||||||
TableRow(
|
Text(t.rank, textAlign: TextAlign.center, style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, fontWeight: FontWeight.w500, color: Colors.white)),
|
||||||
children: [
|
Padding(
|
||||||
Text(t.rank, textAlign: TextAlign.center, style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, fontWeight: FontWeight.w500, color: Colors.white)),
|
padding: const EdgeInsets.only(right: 8.0),
|
||||||
Padding(
|
child: Text(t.gamemodes["40l"]!, textAlign: TextAlign.right, style: TextStyle(fontFamily: bigScreen ? "Eurostile Round" : "Eurostile Round Condensed", fontSize: 28, fontWeight: FontWeight.w500, color: Colors.white)),
|
||||||
padding: const EdgeInsets.only(right: 8.0),
|
|
||||||
child: Text(t.gamemodes["40l"]!, textAlign: TextAlign.right, style: TextStyle(fontFamily: bigScreen ? "Eurostile Round" : "Eurostile Round Condensed", fontSize: 28, fontWeight: FontWeight.w500, color: Colors.white)),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(right: 8.0),
|
|
||||||
child: Text(t.gamemodes["blitz"]!, textAlign: TextAlign.right, style: TextStyle(fontFamily: bigScreen ? "Eurostile Round" : "Eurostile Round Condensed", fontSize: 28, fontWeight: FontWeight.w500, color: Colors.white)),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
),
|
),
|
||||||
for (MapEntry<String, Duration> sprintEntry in sprintAverages.entries) TableRow(
|
Padding(
|
||||||
decoration: BoxDecoration(gradient: LinearGradient(colors: [rankColors[sprintEntry.key]!.withAlpha(100), rankColors[sprintEntry.key]!.withAlpha(200)])),
|
padding: const EdgeInsets.only(right: 8.0),
|
||||||
children: [
|
child: Text(t.gamemodes["blitz"]!, textAlign: TextAlign.right, style: TextStyle(fontFamily: bigScreen ? "Eurostile Round" : "Eurostile Round Condensed", fontSize: 28, fontWeight: FontWeight.w500, color: Colors.white)),
|
||||||
Container(decoration: BoxDecoration(boxShadow: [BoxShadow(color: Colors.black.withAlpha(132), blurRadius: 32.0, blurStyle: BlurStyle.inner)]), child: Image.asset("res/tetrio_tl_alpha_ranks/${sprintEntry.key}.png", height: 48)),
|
),
|
||||||
Padding(
|
]
|
||||||
padding: const EdgeInsets.only(right: 8.0),
|
|
||||||
child: Text(getALittleBitMoreNormalTime(sprintEntry.value), textAlign: TextAlign.right, style: TextStyle(fontFamily: bigScreen ? "Eurostile Round" : "Eurostile Round Condensed", fontSize: 28, fontWeight: FontWeight.w500, color: Colors.white, shadows: textShadow)),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(right: 8.0),
|
|
||||||
child: Text(NumberFormat.decimalPattern().format(blitzAverages[sprintEntry.key]), textAlign: TextAlign.right, style: TextStyle(fontFamily: bigScreen ? "Eurostile Round" : "Eurostile Round Condensed", fontSize: 28, fontWeight: FontWeight.w500, color: Colors.white, shadows: textShadow)),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
Text(t.sprintAndBlitsRelevance(date: dateFormat.format(DateTime(2024, 8, 25))))
|
for (MapEntry<String, Duration> sprintEntry in sprintAverages.entries) TableRow(
|
||||||
|
decoration: BoxDecoration(gradient: LinearGradient(colors: [rankColors[sprintEntry.key]!.withAlpha(100), rankColors[sprintEntry.key]!.withAlpha(200)])),
|
||||||
|
children: [
|
||||||
|
Container(decoration: BoxDecoration(boxShadow: [BoxShadow(color: Colors.black.withAlpha(132), blurRadius: 32.0, blurStyle: BlurStyle.inner)]), child: Image.asset("res/tetrio_tl_alpha_ranks/${sprintEntry.key}.png", height: 48)),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(right: 8.0),
|
||||||
|
child: Text(getALittleBitMoreNormalTime(sprintEntry.value), textAlign: TextAlign.right, style: TextStyle(fontFamily: bigScreen ? "Eurostile Round" : "Eurostile Round Condensed", fontSize: 28, fontWeight: FontWeight.w500, color: Colors.white, shadows: textShadow)),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(right: 8.0),
|
||||||
|
child: Text(NumberFormat.decimalPattern().format(blitzAverages[sprintEntry.key]), textAlign: TextAlign.right, style: TextStyle(fontFamily: bigScreen ? "Eurostile Round" : "Eurostile Round Condensed", fontSize: 28, fontWeight: FontWeight.w500, color: Colors.white, shadows: textShadow)),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
import 'package:tetra_stats/data_objects/beta_record.dart';
|
import 'package:tetra_stats/data_objects/beta_record.dart';
|
||||||
import 'package:tetra_stats/gen/strings.g.dart';
|
import 'package:tetra_stats/gen/strings.g.dart';
|
||||||
import 'package:tetra_stats/utils/numers_formats.dart';
|
import 'package:tetra_stats/utils/numers_formats.dart';
|
||||||
|
@ -9,8 +10,8 @@ import 'package:tetra_stats/widgets/text_timestamp.dart';
|
||||||
class BetaLeagueEntryThingy extends StatelessWidget{
|
class BetaLeagueEntryThingy extends StatelessWidget{
|
||||||
final BetaRecord record;
|
final BetaRecord record;
|
||||||
final String userID;
|
final String userID;
|
||||||
// TODO: Rating delta string is too long for small screens
|
final bool wide;
|
||||||
const BetaLeagueEntryThingy(this.record, this.userID);
|
const BetaLeagueEntryThingy(this.record, this.userID, this.wide);
|
||||||
|
|
||||||
TextSpan matchResult(String result){
|
TextSpan matchResult(String result){
|
||||||
return switch(result){
|
return switch(result){
|
||||||
|
@ -57,6 +58,7 @@ class BetaLeagueEntryThingy extends StatelessWidget{
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
NumberFormat diff = wide ? fDiff : comparef2;
|
||||||
double? deltaTR = (record.extras.league[userID]?[1]?.tr != null && record.extras.league[userID]?[0]?.tr != null) ? record.extras.league[userID]![1]!.tr - record.extras.league[userID]![0]!.tr : null;
|
double? deltaTR = (record.extras.league[userID]?[1]?.tr != null && record.extras.league[userID]?[0]?.tr != null) ? record.extras.league[userID]![1]!.tr - record.extras.league[userID]![0]!.tr : null;
|
||||||
double? deltaGlicko = (record.extras.league[userID]?[1]?.glicko != null && record.extras.league[userID]?[0]?.glicko != null) ? record.extras.league[userID]![1]!.glicko - record.extras.league[userID]![0]!.glicko : null;
|
double? deltaGlicko = (record.extras.league[userID]?[1]?.glicko != null && record.extras.league[userID]?[0]?.glicko != null) ? record.extras.league[userID]![1]!.glicko - record.extras.league[userID]![0]!.glicko : null;
|
||||||
double? deltaRD = (record.extras.league[userID]?[1]?.rd != null && record.extras.league[userID]?[0]?.rd != null) ? record.extras.league[userID]![1]!.rd - record.extras.league[userID]![0]!.rd : null;
|
double? deltaRD = (record.extras.league[userID]?[1]?.rd != null && record.extras.league[userID]?[0]?.rd != null) ? record.extras.league[userID]![1]!.rd - record.extras.league[userID]![0]!.rd : null;
|
||||||
|
@ -88,7 +90,7 @@ class BetaLeagueEntryThingy extends StatelessWidget{
|
||||||
text: ", ${timestamp(record.ts)}\n"
|
text: ", ${timestamp(record.ts)}\n"
|
||||||
),
|
),
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: deltaTR != null ? "${fDiff.format(deltaTR)} TR" : "??? TR",
|
text: deltaTR != null ? "${diff.format(deltaTR)} TR" : "??? TR",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: deltaColor(deltaTR)
|
color: deltaColor(deltaTR)
|
||||||
)
|
)
|
||||||
|
@ -97,7 +99,7 @@ class BetaLeagueEntryThingy extends StatelessWidget{
|
||||||
text: ", "
|
text: ", "
|
||||||
),
|
),
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: deltaGlicko != null ? "${fDiff.format(deltaGlicko)} Glicko" : "??? Glicko",
|
text: deltaGlicko != null ? "${diff.format(deltaGlicko)} Glicko" : "??? Glicko",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: deltaColor(deltaGlicko)
|
color: deltaColor(deltaGlicko)
|
||||||
)
|
)
|
||||||
|
@ -106,7 +108,7 @@ class BetaLeagueEntryThingy extends StatelessWidget{
|
||||||
text: ", "
|
text: ", "
|
||||||
),
|
),
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: deltaRD != null ? "${fDiff.format(deltaRD)} RD" : "??? RD",
|
text: deltaRD != null ? "${diff.format(deltaRD)} RD" : "??? RD",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.grey
|
color: Colors.grey
|
||||||
)
|
)
|
||||||
|
|
|
@ -65,7 +65,7 @@ class TLRatingThingy extends StatelessWidget{
|
||||||
} : [TextSpan(text: "---\n", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28, color: Colors.grey)), TextSpan(text: t.gamesUntilRanked(left: 10-tlData.gamesPlayed), style: const TextStyle(color: Colors.grey, fontSize: 14)),]
|
} : [TextSpan(text: "---\n", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28, color: Colors.grey)), TextSpan(text: t.gamesUntilRanked(left: 10-tlData.gamesPlayed), style: const TextStyle(color: Colors.grey, fontSize: 14)),]
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
if (oldTl != null) RichText(
|
if (oldTl != null && oldTl!.tr != -1.0) RichText(
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
softWrap: true,
|
softWrap: true,
|
||||||
text: TextSpan(
|
text: TextSpan(
|
||||||
|
|
|
@ -103,7 +103,7 @@ class _TLRecordsState extends State<TLRecords> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
itemBuilder: (BuildContext context, int index){
|
itemBuilder: (BuildContext context, int index){
|
||||||
return BetaLeagueEntryThingy(records[index], widget.userID);
|
return BetaLeagueEntryThingy(records[index], widget.userID, MediaQuery.of(context).size.width >= 768.0);
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -47,7 +47,7 @@ class TetraLeagueThingy extends StatelessWidget{
|
||||||
message: "${t.stats.glixare.full}",
|
message: "${t.stats.glixare.full}",
|
||||||
child: Tooltip(child: Text(" ${t.stats.glixare.short}", style: TextStyle(fontSize: width > 768.0 ? 21 : 18, color: league.gxe.isNegative ? Colors.grey : Colors.white)), message: "Glixare")
|
child: Tooltip(child: Text(" ${t.stats.glixare.short}", style: TextStyle(fontSize: width > 768.0 ? 21 : 18, color: league.gxe.isNegative ? Colors.grey : Colors.white)), message: "Glixare")
|
||||||
),
|
),
|
||||||
if (toCompare != null) Text(" (${comparef.format(league.gxe-toCompare!.gxe)})", textAlign: TextAlign.right, style: TextStyle(fontSize: width > 768.0 ? 21 : 18, color: getDifferenceColor(league.gxe-toCompare!.gxe))),
|
if (toCompare != null) Text(toCompare!.gxe != -1 ? " (${comparef.format(league.gxe-toCompare!.gxe)})" : "(---)", textAlign: TextAlign.right, style: TextStyle(fontSize: width > 768.0 ? 21 : 18, color: toCompare!.gxe != -1 ? getDifferenceColor(league.gxe-toCompare!.gxe) : Colors.grey)),
|
||||||
if (lbPos != null) Text(lbPos?.glixare != null ? (lbPos!.glixare!.position >= 1000 ? " (${t.top} ${f2.format(lbPos!.glixare!.percentage*100)}%)" : " (№ ${lbPos!.glixare!.position})") : "(№ ---)", style: TextStyle(color: lbPos?.glixare != null ? getColorOfRank(lbPos!.glixare!.position) : null))
|
if (lbPos != null) Text(lbPos?.glixare != null ? (lbPos!.glixare!.position >= 1000 ? " (${t.top} ${f2.format(lbPos!.glixare!.percentage*100)}%)" : " (№ ${lbPos!.glixare!.position})") : "(№ ---)", style: TextStyle(color: lbPos?.glixare != null ? getColorOfRank(lbPos!.glixare!.position) : null))
|
||||||
]),
|
]),
|
||||||
];
|
];
|
||||||
|
@ -60,7 +60,7 @@ class TetraLeagueThingy extends StatelessWidget{
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
TLRatingThingy(userID: league.id, tlData: league, oldTl: toCompare, showPositions: true),
|
TLRatingThingy(userID: league.id, tlData: league, oldTl: toCompare, showPositions: true),
|
||||||
if (league.gamesPlayed > 9) TLProgress(
|
if (league.gamesPlayed > 9 && league.percentileRank != "z") TLProgress(
|
||||||
tlData: league,
|
tlData: league,
|
||||||
previousRankTRcutoff: cutoffs != null ? cutoffs!.tr[league.rank != "z" ? league.rank : league.percentileRank] : null,
|
previousRankTRcutoff: cutoffs != null ? cutoffs!.tr[league.rank != "z" ? league.rank : league.percentileRank] : null,
|
||||||
nextRankTRcutoff: cutoffs != null ? cutoffs!.tr[ranks2[ranks2.indexOf(league.rank != "z" ? league.rank : league.percentileRank)-1]] : null,
|
nextRankTRcutoff: cutoffs != null ? cutoffs!.tr[ranks2[ranks2.indexOf(league.rank != "z" ? league.rank : league.percentileRank)-1]] : null,
|
||||||
|
|
|
@ -19,6 +19,8 @@ static void my_application_activate(GApplication* application) {
|
||||||
MyApplication* self = MY_APPLICATION(application);
|
MyApplication* self = MY_APPLICATION(application);
|
||||||
GtkWindow* window =
|
GtkWindow* window =
|
||||||
GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));
|
GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));
|
||||||
|
|
||||||
|
gtk_window_set_icon_from_file(GTK_WINDOW(window),"res/icons/app.png",NULL);
|
||||||
|
|
||||||
// Use a header bar when running in GNOME as this is the common style used
|
// Use a header bar when running in GNOME as this is the common style used
|
||||||
// by applications and is the setup most users will be using (e.g. Ubuntu
|
// by applications and is the setup most users will be using (e.g. Ubuntu
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
import Cocoa
|
||||||
|
import FlutterMacOS
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
class RunnerTests: XCTestCase {
|
||||||
|
|
||||||
|
func testExample() {
|
||||||
|
// If you add code to the Runner application, consider adding tests here.
|
||||||
|
// See https://developer.apple.com/documentation/xctest for more information about using XCTest.
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
16
pubspec.lock
16
pubspec.lock
|
@ -328,6 +328,14 @@ packages:
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
flutter_to_debian:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_to_debian
|
||||||
|
sha256: d23534407334b331ce20fbaa8395b9ecc255d0c047136b8998715f36933ee696
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.2"
|
||||||
flutter_web_plugins:
|
flutter_web_plugins:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -509,6 +517,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.5"
|
version: "1.0.5"
|
||||||
|
mime_type:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: mime_type
|
||||||
|
sha256: d652b613e84dac1af28030a9fba82c0999be05b98163f9e18a0849c6e63838bb
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.1"
|
||||||
node_preamble:
|
node_preamble:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -2,7 +2,7 @@ name: tetra_stats
|
||||||
description: Track your and other player stats in TETR.IO
|
description: Track your and other player stats in TETR.IO
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
|
|
||||||
version: 2.0.2+43
|
version: 2.0.3+44
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=3.0.0'
|
sdk: '>=3.0.0'
|
||||||
|
@ -46,6 +46,7 @@ dependencies:
|
||||||
flutter_layout_grid: ^2.0.0
|
flutter_layout_grid: ^2.0.0
|
||||||
go_router: ^13.0.0
|
go_router: ^13.0.0
|
||||||
syncfusion_flutter_charts: ^24.2.9
|
syncfusion_flutter_charts: ^24.2.9
|
||||||
|
flutter_to_debian: ^2.0.2
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
|
@ -172,7 +172,7 @@
|
||||||
"refresh": "Обновить"
|
"refresh": "Обновить"
|
||||||
},
|
},
|
||||||
"graphsDestination": {
|
"graphsDestination": {
|
||||||
"fetchAndsaveTLHistory": "Получить историю игрока",
|
"fetchAndsaveTLHistory": "Получить историю",
|
||||||
"fetchAndSaveOldTLmatches": "Получить историю матчей Тетра Лиги",
|
"fetchAndSaveOldTLmatches": "Получить историю матчей Тетра Лиги",
|
||||||
"fetchAndsaveTLHistoryResult": "${number} состояний было найдено",
|
"fetchAndsaveTLHistoryResult": "${number} состояний было найдено",
|
||||||
"fetchAndSaveOldTLmatchesResult": "${number} матчей было найдено",
|
"fetchAndSaveOldTLmatchesResult": "${number} матчей было найдено",
|
||||||
|
|
|
@ -131,7 +131,7 @@
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<!-- This script adds the flutter initialization JS code -->
|
<!-- This script adds the flutter initialization JS code -->
|
||||||
<script src="flutter.js?version=2.0.2" defer></script>
|
<script src="flutter.js?version=2.0.3" defer></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="preloader">
|
<div id="preloader">
|
||||||
|
|
Loading…
Reference in New Issue