Merge pull request #139 from dan63047/master
Put 1.6.9 into stable, so i can refactor without the fear that i will not be able fix 1.6.9 issues
|
@ -18,7 +18,7 @@ jobs:
|
|||
- uses: subosito/flutter-action@v1
|
||||
with:
|
||||
channel: 'stable'
|
||||
flutter-version: '3.16.5'
|
||||
flutter-version: '3.22.3'
|
||||
- name: Install project dependencies
|
||||
run: flutter pub get
|
||||
- name: Build artifacts
|
||||
|
@ -40,51 +40,28 @@ jobs:
|
|||
tag: Auto-${{ github.run_number }}
|
||||
body: Builded with GitHub Action workflow
|
||||
token: ${{ secrets.TOKEN }}
|
||||
# build-and-release-linux:
|
||||
# name: Build Linux App
|
||||
# runs-on: ubuntu-latest
|
||||
# steps:
|
||||
# - uses: actions/checkout@v2
|
||||
# - uses: subosito/flutter-action@v1
|
||||
# - uses: ashutoshvarma/setup-ninja@master
|
||||
# with:
|
||||
# channel: 'stable'
|
||||
# flutter-version: '3.16.5'
|
||||
# - name: Install project dependencies
|
||||
# run: flutter pub get
|
||||
# - name: Build artifacts
|
||||
# run: flutter build linux --release
|
||||
# - name: Archive Release
|
||||
# uses: thedoctor0/zip-release@master
|
||||
# with:
|
||||
# type: 'zip'
|
||||
# filename: TetraStats-${{github.ref_name}}-windows.zip
|
||||
# directory: build/linux/x64/runner/Release/bundle
|
||||
# - name: Push to Releases
|
||||
# uses: ncipollo/release-action@v1
|
||||
# with:
|
||||
# prerelease: true
|
||||
# allowUpdates: true
|
||||
# replacesArtifacts: false
|
||||
# discussionCategory: autobuilded-releases
|
||||
# artifacts: "build/linux/x64/runner/Release/bundle/TetraStats-${{github.ref_name}}-linux.zip"
|
||||
# tag: Auto-${{ github.run_number }}
|
||||
# body: Builded with GitHub Action workflow
|
||||
# token: ${{ secrets.TOKEN }}
|
||||
build-and-release-android:
|
||||
name: Build Android App
|
||||
build-and-release-linux:
|
||||
name: Build Linux App
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: '12.x'
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ashutoshvarma/setup-ninja@master
|
||||
- uses: subosito/flutter-action@v1
|
||||
with:
|
||||
flutter-version: '3.16.5'
|
||||
- run: flutter pub get
|
||||
# - run: flutter test // lmao. Tests? Who needs it?
|
||||
- run: flutter build apk --split-per-abi
|
||||
channel: 'stable'
|
||||
flutter-version: '3.22.3'
|
||||
- name: Install project dependencies
|
||||
run: |
|
||||
flutter pub get
|
||||
sudo apt-get install -y ninja-build libgtk-3-dev
|
||||
- name: Build artifacts
|
||||
run: flutter build linux --release
|
||||
- name: Archive Release
|
||||
uses: thedoctor0/zip-release@master
|
||||
with:
|
||||
type: 'zip'
|
||||
filename: TetraStats-${{github.ref_name}}-linux.zip
|
||||
directory: build/linux/x64/release/bundle
|
||||
- name: Push to Releases
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
|
@ -92,7 +69,32 @@ jobs:
|
|||
allowUpdates: true
|
||||
replacesArtifacts: false
|
||||
discussionCategory: autobuilded-releases
|
||||
artifacts: "build/app/outputs/flutter-apk/*"
|
||||
artifacts: "build/linux/x64/release/bundle/TetraStats-${{github.ref_name}}-linux.zip"
|
||||
tag: Auto-${{ github.run_number }}
|
||||
body: Builded with GitHub Action workflow
|
||||
token: ${{ secrets.TOKEN }}
|
||||
token: ${{ secrets.TOKEN }}
|
||||
# build-and-release-android:
|
||||
# name: Build Android App
|
||||
# runs-on: ubuntu-latest
|
||||
# steps:
|
||||
# - uses: actions/checkout@v1
|
||||
# - uses: actions/setup-java@v1
|
||||
# with:
|
||||
# java-version: '12.x'
|
||||
# - uses: subosito/flutter-action@v1
|
||||
# with:
|
||||
# flutter-version: '3.16.5'
|
||||
# - run: flutter pub get
|
||||
# # - run: flutter test // lmao. Tests? Who needs it?
|
||||
# - run: flutter build apk --split-per-abi
|
||||
# - name: Push to Releases
|
||||
# uses: ncipollo/release-action@v1
|
||||
# with:
|
||||
# prerelease: true
|
||||
# allowUpdates: true
|
||||
# replacesArtifacts: false
|
||||
# discussionCategory: autobuilded-releases
|
||||
# artifacts: "build/app/outputs/flutter-apk/*"
|
||||
# tag: Auto-${{ github.run_number }}
|
||||
# body: Builded with GitHub Action workflow
|
||||
# token: ${{ secrets.TOKEN }}
|
|
@ -1,83 +1,83 @@
|
|||
def localProperties = new Properties()
|
||||
def localPropertiesFile = rootProject.file('local.properties')
|
||||
if (localPropertiesFile.exists()) {
|
||||
localPropertiesFile.withReader('UTF-8') { reader ->
|
||||
localProperties.load(reader)
|
||||
}
|
||||
}
|
||||
|
||||
def flutterRoot = localProperties.getProperty('flutter.sdk')
|
||||
if (flutterRoot == null) {
|
||||
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
|
||||
}
|
||||
|
||||
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
|
||||
if (flutterVersionCode == null) {
|
||||
flutterVersionCode = '1'
|
||||
}
|
||||
|
||||
def flutterVersionName = localProperties.getProperty('flutter.versionName')
|
||||
if (flutterVersionName == null) {
|
||||
flutterVersionName = '1.0'
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||
|
||||
def keystoreProperties = new Properties()
|
||||
def keystorePropertiesFile = rootProject.file('key.properties')
|
||||
if (keystorePropertiesFile.exists()) {
|
||||
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion flutter.compileSdkVersion
|
||||
ndkVersion flutter.ndkVersion
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main.java.srcDirs += 'src/main/kotlin'
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||
applicationId "com.dan63.tetra_stats"
|
||||
// You can update the following values to match your application needs.
|
||||
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
|
||||
minSdkVersion 19
|
||||
targetSdkVersion flutter.targetSdkVersion
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
release {
|
||||
keyAlias keystoreProperties['keyAlias']
|
||||
keyPassword keystoreProperties['keyPassword']
|
||||
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
|
||||
storePassword keystoreProperties['storePassword']
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
signingConfig signingConfigs.release
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flutter {
|
||||
source '../..'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
}
|
||||
def localProperties = new Properties()
|
||||
def localPropertiesFile = rootProject.file('local.properties')
|
||||
if (localPropertiesFile.exists()) {
|
||||
localPropertiesFile.withReader('UTF-8') { reader ->
|
||||
localProperties.load(reader)
|
||||
}
|
||||
}
|
||||
|
||||
def flutterRoot = localProperties.getProperty('flutter.sdk')
|
||||
if (flutterRoot == null) {
|
||||
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
|
||||
}
|
||||
|
||||
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
|
||||
if (flutterVersionCode == null) {
|
||||
flutterVersionCode = '1'
|
||||
}
|
||||
|
||||
def flutterVersionName = localProperties.getProperty('flutter.versionName')
|
||||
if (flutterVersionName == null) {
|
||||
flutterVersionName = '1.0'
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||
|
||||
def keystoreProperties = new Properties()
|
||||
def keystorePropertiesFile = rootProject.file('key.properties')
|
||||
if (keystorePropertiesFile.exists()) {
|
||||
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion flutter.compileSdkVersion
|
||||
ndkVersion flutter.ndkVersion
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main.java.srcDirs += 'src/main/kotlin'
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||
applicationId "com.dan63.tetra_stats"
|
||||
// You can update the following values to match your application needs.
|
||||
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
|
||||
minSdkVersion flutter.minSdkVersion
|
||||
targetSdkVersion flutter.targetSdkVersion
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
release {
|
||||
keyAlias keystoreProperties['keyAlias']
|
||||
keyPassword keystoreProperties['keyPassword']
|
||||
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
|
||||
storePassword keystoreProperties['storePassword']
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
signingConfig signingConfigs.release
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flutter {
|
||||
source '../..'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
}
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
// p1nkl0bst3r data objects
|
||||
|
||||
class Cutoffs{
|
||||
DateTime ts;
|
||||
Map<String, double> tr;
|
||||
Map<String, double> glicko;
|
||||
Map<String, double> gxe;
|
||||
|
||||
Cutoffs(this.tr, this.glicko);
|
||||
Cutoffs(this.ts, this.tr, this.glicko, this.gxe);
|
||||
}
|
||||
|
||||
class TopTr{
|
||||
|
|
|
@ -134,7 +134,7 @@ class ReplayStats{
|
|||
topSpike = 0;
|
||||
tspins = 0;
|
||||
roundLength = 0.0;
|
||||
clears = Clears(singles: 0, doubles: 0, triples: 0, quads: 0, pentas: 0, allClears: 0, tSpinZeros: 0, tSpinSingles: 0, tSpinDoubles: 0, tSpinTriples: 0, tSpinPentas: 0, tSpinQuads: 0, tSpinMiniZeros: 0, tSpinMiniSingles: 0, tSpinMiniDoubles: 0);
|
||||
clears = Clears(singles: 0, doubles: 0, triples: 0, quads: 0, pentas: 0, allClears: 0, tSpinZeros: 0, tSpinSingles: 0, tSpinDoubles: 0, tSpinTriples: 0, tSpinPentas: 0, tSpinQuads: 0, tSpinMiniZeros: 0, tSpinMiniSingles: 0, tSpinMiniDoubles: 0, tSpinMiniTriples: 0, tSpinMiniQuads: 0);
|
||||
garbage = Garbage(sent: 0, recived: 0, attack: 0, cleared: 0);
|
||||
finesse = Finesse(combo: 0, faults: 0, perfectPieces: 0);
|
||||
}
|
||||
|
@ -208,12 +208,12 @@ class ReplayData{
|
|||
stats = [];
|
||||
roundWinners = [];
|
||||
int roundID = 0;
|
||||
List<double> APMmultipliedByWeights = [0, 0];
|
||||
List<double> PPSmultipliedByWeights = [0, 0];
|
||||
List<double> VSmultipliedByWeights = [0, 0];
|
||||
List<double> SPPmultipliedByWeights = [0, 0];
|
||||
List<double> KPPmultipliedByWeights = [0, 0];
|
||||
List<double> KPSmultipliedByWeights = [0, 0];
|
||||
List<double> apmMultipliedByWeights = [0, 0];
|
||||
List<double> ppsMultipliedByWeights = [0, 0];
|
||||
List<double> vsMultipliedByWeights = [0, 0];
|
||||
List<double> sppMultipliedByWeights = [0, 0];
|
||||
List<double> kppMultipliedByWeights = [0, 0];
|
||||
List<double> kpsMultipliedByWeights = [0, 0];
|
||||
totalStats = [ReplayStats.createEmpty(), ReplayStats.createEmpty()];
|
||||
for(var round in json['data']) {
|
||||
int firstInEndContext = round['replays'][0]["events"].last['data']['export']['options']['username'].startsWith(endcontext[0].username) ? 0 : 1;
|
||||
|
@ -221,30 +221,30 @@ class ReplayData{
|
|||
int roundLength = max(round['replays'][0]['frames'], round['replays'][1]['frames']);
|
||||
roundLengths.add(roundLength);
|
||||
totalLength = totalLength + max(round['replays'][0]['frames'], round['replays'][1]['frames']);
|
||||
APMmultipliedByWeights[0] += endcontext[0].secondaryTracking[roundID]*roundLength;
|
||||
APMmultipliedByWeights[1] += endcontext[1].secondaryTracking[roundID]*roundLength;
|
||||
PPSmultipliedByWeights[0] += endcontext[0].tertiaryTracking[roundID]*roundLength;
|
||||
PPSmultipliedByWeights[1] += endcontext[1].tertiaryTracking[roundID]*roundLength;
|
||||
VSmultipliedByWeights[0] += endcontext[0].extraTracking[roundID]*roundLength;
|
||||
VSmultipliedByWeights[1] += endcontext[1].extraTracking[roundID]*roundLength;
|
||||
apmMultipliedByWeights[0] += endcontext[0].secondaryTracking[roundID]*roundLength;
|
||||
apmMultipliedByWeights[1] += endcontext[1].secondaryTracking[roundID]*roundLength;
|
||||
ppsMultipliedByWeights[0] += endcontext[0].tertiaryTracking[roundID]*roundLength;
|
||||
ppsMultipliedByWeights[1] += endcontext[1].tertiaryTracking[roundID]*roundLength;
|
||||
vsMultipliedByWeights[0] += endcontext[0].extraTracking[roundID]*roundLength;
|
||||
vsMultipliedByWeights[1] += endcontext[1].extraTracking[roundID]*roundLength;
|
||||
int winner = round['board'].indexWhere((element) => element['success'] == true);
|
||||
roundWinners.add([round['board'][winner]['id']??round['board'][winner]['user']['_id'], round['board'][winner]['username']??round['board'][winner]['user']['username']]);
|
||||
ReplayStats playerOne = ReplayStats.fromJson(round['replays'][firstInEndContext]['events'].last['data']['export']['stats'], biggestSpikeFromReplay(round['replays'][secondInEndContext]['events']), round['replays'][firstInEndContext]['frames']); // (events contain recived attacks)
|
||||
ReplayStats playerTwo = ReplayStats.fromJson(round['replays'][secondInEndContext]['events'].last['data']['export']['stats'], biggestSpikeFromReplay(round['replays'][firstInEndContext]['events']), round['replays'][secondInEndContext]['frames']);
|
||||
SPPmultipliedByWeights[0] += playerOne.spp*roundLength;
|
||||
SPPmultipliedByWeights[1] += playerTwo.spp*roundLength;
|
||||
KPPmultipliedByWeights[0] += playerOne.kpp*roundLength;
|
||||
KPPmultipliedByWeights[1] += playerTwo.kpp*roundLength;
|
||||
KPSmultipliedByWeights[0] += playerOne.kps*roundLength;
|
||||
KPSmultipliedByWeights[1] += playerTwo.kps*roundLength;
|
||||
sppMultipliedByWeights[0] += playerOne.spp*roundLength;
|
||||
sppMultipliedByWeights[1] += playerTwo.spp*roundLength;
|
||||
kppMultipliedByWeights[0] += playerOne.kpp*roundLength;
|
||||
kppMultipliedByWeights[1] += playerTwo.kpp*roundLength;
|
||||
kpsMultipliedByWeights[0] += playerOne.kps*roundLength;
|
||||
kpsMultipliedByWeights[1] += playerTwo.kps*roundLength;
|
||||
stats.add([playerOne, playerTwo]);
|
||||
totalStats[0] = totalStats[0] + playerOne;
|
||||
totalStats[1] = totalStats[1] + playerTwo;
|
||||
roundID ++;
|
||||
}
|
||||
timeWeightedStats = [
|
||||
AggregateStats(APMmultipliedByWeights[0]/totalLength, PPSmultipliedByWeights[0]/totalLength, VSmultipliedByWeights[0]/totalLength, SPPmultipliedByWeights[0]/totalLength, KPPmultipliedByWeights[0]/totalLength, KPSmultipliedByWeights[0]/totalLength),
|
||||
AggregateStats(APMmultipliedByWeights[1]/totalLength, PPSmultipliedByWeights[1]/totalLength, VSmultipliedByWeights[1]/totalLength, SPPmultipliedByWeights[1]/totalLength, KPPmultipliedByWeights[1]/totalLength, KPSmultipliedByWeights[1]/totalLength)
|
||||
AggregateStats(apmMultipliedByWeights[0]/totalLength, ppsMultipliedByWeights[0]/totalLength, vsMultipliedByWeights[0]/totalLength, sppMultipliedByWeights[0]/totalLength, kppMultipliedByWeights[0]/totalLength, kpsMultipliedByWeights[0]/totalLength),
|
||||
AggregateStats(apmMultipliedByWeights[1]/totalLength, ppsMultipliedByWeights[1]/totalLength, vsMultipliedByWeights[1]/totalLength, sppMultipliedByWeights[1]/totalLength, kppMultipliedByWeights[1]/totalLength, kpsMultipliedByWeights[1]/totalLength)
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
/// To regenerate, run: `dart run slang`
|
||||
///
|
||||
/// Locales: 2
|
||||
/// Strings: 1186 (593 per locale)
|
||||
/// Strings: 1210 (605 per locale)
|
||||
///
|
||||
/// Built on 2024-07-20 at 13:24 UTC
|
||||
/// Built on 2024-09-04 at 20:41 UTC
|
||||
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint
|
||||
|
@ -222,6 +222,9 @@ class Translations implements BaseTranslations<AppLocale, Translations> {
|
|||
String get verdictBetter => 'better';
|
||||
String get verdictWorse => 'worse';
|
||||
String get smooth => 'Smooth';
|
||||
String get postSeason => 'Off-season';
|
||||
String get seasonStarts => 'Season starts in:';
|
||||
String get nanow => 'Not avaliable for now...';
|
||||
String seasonEnds({required Object countdown}) => 'Season ends in ${countdown}';
|
||||
String get seasonEnded => 'Season has ended';
|
||||
String gamesUntilRanked({required Object left}) => '${left} games until being ranked';
|
||||
|
@ -236,6 +239,17 @@ class Translations implements BaseTranslations<AppLocale, Translations> {
|
|||
String get neverPlayedTL => 'That user never played Tetra League';
|
||||
String get botTL => 'Bots are not allowed to play Tetra League';
|
||||
String get anonTL => 'Guests are not allowed to play Tetra League';
|
||||
String get quickPlay => 'Quick Play';
|
||||
String get expert => 'Expert';
|
||||
String get withMods => 'With mods';
|
||||
String withModsPlural({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('en'))(n,
|
||||
zero: 'with ${n} mods',
|
||||
one: 'with ${n} mod',
|
||||
two: 'with ${n} mods',
|
||||
few: 'with ${n} mods',
|
||||
many: 'with ${n} mods',
|
||||
other: 'with ${n} mods',
|
||||
);
|
||||
String get exportDB => 'Export local database';
|
||||
String get exportDBDescription => 'It contains states and Tetra League records of the tracked players and list of tracked players.';
|
||||
String get desktopExportAlertTitle => 'Desktop export';
|
||||
|
@ -276,7 +290,7 @@ class Translations implements BaseTranslations<AppLocale, Translations> {
|
|||
String stateViewTitle({required Object nickname, required Object date}) => '${nickname} account on ${date}';
|
||||
String statesViewTitle({required Object number, required Object nickname}) => '${number} states of ${nickname} account';
|
||||
String matchesViewTitle({required Object nickname}) => '${nickname} TL matches';
|
||||
String statesViewEntry({required Object level, required Object gameTime, required Object friends, required Object rd}) => 'Level ${level}, ${gameTime} of gametime, ${friends} friends, ${rd} RD';
|
||||
String statesViewEntry({required Object level, required Object glicko, required Object rd, required Object games}) => '${level} TR, ${glicko}±${rd} Glicko, ${games} игр сыграно';
|
||||
String stateRemoved({required Object date}) => '${date} state was removed from database!';
|
||||
String matchRemoved({required Object date}) => '${date} match was removed from database!';
|
||||
String get viewAllMatches => 'View all matches';
|
||||
|
@ -703,7 +717,7 @@ class _StringsStatCellNumEn {
|
|||
String get lbpcShort => '№ in local LB';
|
||||
String get gamesPlayed => 'Games\nplayed';
|
||||
String get gamesWonTL => 'Games\nWon';
|
||||
String get winrate => 'Winrate\nprecentage';
|
||||
String get winrate => 'Winrate';
|
||||
String get level => 'Level';
|
||||
String get score => 'Score';
|
||||
String get spp => 'Score\nPer Piece';
|
||||
|
@ -919,6 +933,9 @@ class _StringsRu implements Translations {
|
|||
@override String get verdictBetter => 'Лучше';
|
||||
@override String get verdictWorse => 'Хуже';
|
||||
@override String get smooth => 'Гладкий';
|
||||
@override String get postSeason => 'Внесезонье';
|
||||
@override String get seasonStarts => 'Сезон начнётся через:';
|
||||
@override String get nanow => 'Пока недоступно...';
|
||||
@override String seasonEnds({required Object countdown}) => 'Сезон закончится через ${countdown}';
|
||||
@override String get seasonEnded => 'Сезон закончился';
|
||||
@override String gamesUntilRanked({required Object left}) => '${left} матчей до получения рейтинга';
|
||||
|
@ -933,6 +950,17 @@ class _StringsRu implements Translations {
|
|||
@override String get neverPlayedTL => 'Этот игрок никогда не играл в Тетра Лигу';
|
||||
@override String get botTL => 'Ботам нельзя играть в Тетра Лигу';
|
||||
@override String get anonTL => 'Гостям нельзя играть в Тетра Лигу';
|
||||
@override String get quickPlay => 'Быстрая Игра';
|
||||
@override String get expert => 'Эксперт';
|
||||
@override String get withMods => 'С модами';
|
||||
@override String withModsPlural({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('ru'))(n,
|
||||
zero: 'с ${n} модами',
|
||||
one: 'с ${n} модом',
|
||||
two: 'с ${n} модами',
|
||||
few: 'с ${n} модами',
|
||||
many: 'с ${n} модами',
|
||||
other: 'с ${n} модами',
|
||||
);
|
||||
@override String get exportDB => 'Экспортировать локальную базу данных';
|
||||
@override String get exportDBDescription => 'Она содержит состояния аккаунтов и их матчей в Тетра Лиге для отслеживаемых игроков и список таких игроков.';
|
||||
@override String get desktopExportAlertTitle => 'Экспорт на десктопе';
|
||||
|
@ -973,7 +1001,7 @@ class _StringsRu implements Translations {
|
|||
@override String stateViewTitle({required Object nickname, required Object date}) => 'Аккаунт ${nickname} ${date}';
|
||||
@override String statesViewTitle({required Object number, required Object nickname}) => '${number} состояний аккаунта ${nickname}';
|
||||
@override String matchesViewTitle({required Object nickname}) => 'Матчи аккаунта ${nickname}';
|
||||
@override String statesViewEntry({required Object level, required Object gameTime, required Object friends, required Object rd}) => '${level} уровень, ${gameTime} сыграно, ${friends} друзей, ${rd} RD';
|
||||
@override String statesViewEntry({required Object level, required Object glicko, required Object rd, required Object games}) => '${level} TR, ${glicko}±${rd} Glicko, ${games} игр сыграно';
|
||||
@override String stateRemoved({required Object date}) => 'Состояние от ${date} было удалено из локальной базы данных!';
|
||||
@override String matchRemoved({required Object date}) => 'Матч от ${date} был удален из локальной базы данных!';
|
||||
@override String get viewAllMatches => 'Все матчи';
|
||||
|
@ -1608,6 +1636,9 @@ extension on Translations {
|
|||
case 'verdictBetter': return 'better';
|
||||
case 'verdictWorse': return 'worse';
|
||||
case 'smooth': return 'Smooth';
|
||||
case 'postSeason': return 'Off-season';
|
||||
case 'seasonStarts': return 'Season starts in:';
|
||||
case 'nanow': return 'Not avaliable for now...';
|
||||
case 'seasonEnds': return ({required Object countdown}) => 'Season ends in ${countdown}';
|
||||
case 'seasonEnded': return 'Season has ended';
|
||||
case 'gamesUntilRanked': return ({required Object left}) => '${left} games until being ranked';
|
||||
|
@ -1622,6 +1653,17 @@ extension on Translations {
|
|||
case 'neverPlayedTL': return 'That user never played Tetra League';
|
||||
case 'botTL': return 'Bots are not allowed to play Tetra League';
|
||||
case 'anonTL': return 'Guests are not allowed to play Tetra League';
|
||||
case 'quickPlay': return 'Quick Play';
|
||||
case 'expert': return 'Expert';
|
||||
case 'withMods': return 'With mods';
|
||||
case 'withModsPlural': return ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('en'))(n,
|
||||
zero: 'with ${n} mods',
|
||||
one: 'with ${n} mod',
|
||||
two: 'with ${n} mods',
|
||||
few: 'with ${n} mods',
|
||||
many: 'with ${n} mods',
|
||||
other: 'with ${n} mods',
|
||||
);
|
||||
case 'exportDB': return 'Export local database';
|
||||
case 'exportDBDescription': return 'It contains states and Tetra League records of the tracked players and list of tracked players.';
|
||||
case 'desktopExportAlertTitle': return 'Desktop export';
|
||||
|
@ -1662,7 +1704,7 @@ extension on Translations {
|
|||
case 'stateViewTitle': return ({required Object nickname, required Object date}) => '${nickname} account on ${date}';
|
||||
case 'statesViewTitle': return ({required Object number, required Object nickname}) => '${number} states of ${nickname} account';
|
||||
case 'matchesViewTitle': return ({required Object nickname}) => '${nickname} TL matches';
|
||||
case 'statesViewEntry': return ({required Object level, required Object gameTime, required Object friends, required Object rd}) => 'Level ${level}, ${gameTime} of gametime, ${friends} friends, ${rd} RD';
|
||||
case 'statesViewEntry': return ({required Object level, required Object glicko, required Object rd, required Object games}) => '${level} TR, ${glicko}±${rd} Glicko, ${games} игр сыграно';
|
||||
case 'stateRemoved': return ({required Object date}) => '${date} state was removed from database!';
|
||||
case 'matchRemoved': return ({required Object date}) => '${date} match was removed from database!';
|
||||
case 'viewAllMatches': return 'View all matches';
|
||||
|
@ -1773,7 +1815,7 @@ extension on Translations {
|
|||
case 'statCellNum.lbpcShort': return '№ in local LB';
|
||||
case 'statCellNum.gamesPlayed': return 'Games\nplayed';
|
||||
case 'statCellNum.gamesWonTL': return 'Games\nWon';
|
||||
case 'statCellNum.winrate': return 'Winrate\nprecentage';
|
||||
case 'statCellNum.winrate': return 'Winrate';
|
||||
case 'statCellNum.level': return 'Level';
|
||||
case 'statCellNum.score': return 'Score';
|
||||
case 'statCellNum.spp': return 'Score\nPer Piece';
|
||||
|
@ -2221,6 +2263,9 @@ extension on _StringsRu {
|
|||
case 'verdictBetter': return 'Лучше';
|
||||
case 'verdictWorse': return 'Хуже';
|
||||
case 'smooth': return 'Гладкий';
|
||||
case 'postSeason': return 'Внесезонье';
|
||||
case 'seasonStarts': return 'Сезон начнётся через:';
|
||||
case 'nanow': return 'Пока недоступно...';
|
||||
case 'seasonEnds': return ({required Object countdown}) => 'Сезон закончится через ${countdown}';
|
||||
case 'seasonEnded': return 'Сезон закончился';
|
||||
case 'gamesUntilRanked': return ({required Object left}) => '${left} матчей до получения рейтинга';
|
||||
|
@ -2235,6 +2280,17 @@ extension on _StringsRu {
|
|||
case 'neverPlayedTL': return 'Этот игрок никогда не играл в Тетра Лигу';
|
||||
case 'botTL': return 'Ботам нельзя играть в Тетра Лигу';
|
||||
case 'anonTL': return 'Гостям нельзя играть в Тетра Лигу';
|
||||
case 'quickPlay': return 'Быстрая Игра';
|
||||
case 'expert': return 'Эксперт';
|
||||
case 'withMods': return 'С модами';
|
||||
case 'withModsPlural': return ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('ru'))(n,
|
||||
zero: 'с ${n} модами',
|
||||
one: 'с ${n} модом',
|
||||
two: 'с ${n} модами',
|
||||
few: 'с ${n} модами',
|
||||
many: 'с ${n} модами',
|
||||
other: 'с ${n} модами',
|
||||
);
|
||||
case 'exportDB': return 'Экспортировать локальную базу данных';
|
||||
case 'exportDBDescription': return 'Она содержит состояния аккаунтов и их матчей в Тетра Лиге для отслеживаемых игроков и список таких игроков.';
|
||||
case 'desktopExportAlertTitle': return 'Экспорт на десктопе';
|
||||
|
@ -2275,7 +2331,7 @@ extension on _StringsRu {
|
|||
case 'stateViewTitle': return ({required Object nickname, required Object date}) => 'Аккаунт ${nickname} ${date}';
|
||||
case 'statesViewTitle': return ({required Object number, required Object nickname}) => '${number} состояний аккаунта ${nickname}';
|
||||
case 'matchesViewTitle': return ({required Object nickname}) => 'Матчи аккаунта ${nickname}';
|
||||
case 'statesViewEntry': return ({required Object level, required Object gameTime, required Object friends, required Object rd}) => '${level} уровень, ${gameTime} сыграно, ${friends} друзей, ${rd} RD';
|
||||
case 'statesViewEntry': return ({required Object level, required Object glicko, required Object rd, required Object games}) => '${level} TR, ${glicko}±${rd} Glicko, ${games} игр сыграно';
|
||||
case 'stateRemoved': return ({required Object date}) => 'Состояние от ${date} было удалено из локальной базы данных!';
|
||||
case 'matchRemoved': return ({required Object date}) => 'Матч от ${date} был удален из локальной базы данных!';
|
||||
case 'viewAllMatches': return 'Все матчи';
|
||||
|
|
|
@ -25,39 +25,33 @@ import 'package:go_router/go_router.dart';
|
|||
late final PackageInfo packageInfo;
|
||||
late SharedPreferences prefs;
|
||||
late TetrioService teto;
|
||||
ThemeData theme = ThemeData(fontFamily: 'Eurostile Round', colorScheme: const ColorScheme.dark(primary: Colors.cyanAccent, secondary: Colors.white), scaffoldBackgroundColor: Colors.black);
|
||||
|
||||
// Future<dynamic> computeIsolate(Future Function() function) async {
|
||||
// final receivePort = ReceivePort();
|
||||
// var rootToken = RootIsolateToken.instance!;
|
||||
// await Isolate.spawn<_IsolateData>(
|
||||
// _isolateEntry,
|
||||
// _IsolateData(
|
||||
// token: rootToken,
|
||||
// function: function,
|
||||
// answerPort: receivePort.sendPort,
|
||||
// ),
|
||||
// );
|
||||
// return await receivePort.first;
|
||||
// }
|
||||
|
||||
// void _isolateEntry(_IsolateData isolateData) async {
|
||||
// BackgroundIsolateBinaryMessenger.ensureInitialized(isolateData.token);
|
||||
// final answer = await isolateData.function();
|
||||
// isolateData.answerPort.send(answer);
|
||||
// }
|
||||
|
||||
// class _IsolateData {
|
||||
// final RootIsolateToken token;
|
||||
// final Function function;
|
||||
// final SendPort answerPort;
|
||||
|
||||
// _IsolateData({
|
||||
// required this.token,
|
||||
// required this.function,
|
||||
// required this.answerPort,
|
||||
// });
|
||||
// }
|
||||
ThemeData theme = ThemeData(
|
||||
fontFamily: 'Eurostile Round',
|
||||
colorScheme: const ColorScheme.dark(
|
||||
primary: Colors.cyanAccent,
|
||||
surface: Color.fromARGB(255, 10, 10, 10),
|
||||
secondary: Color(0xFF00838F),
|
||||
),
|
||||
cardTheme: const CardTheme(surfaceTintColor: Color.fromARGB(255, 10, 10, 10)),
|
||||
drawerTheme: const DrawerThemeData(surfaceTintColor: Color.fromARGB(255, 10, 10, 10)),
|
||||
searchBarTheme: const SearchBarThemeData(
|
||||
shadowColor: WidgetStatePropertyAll(Colors.black),
|
||||
shape: WidgetStatePropertyAll(RoundedRectangleBorder(borderRadius: BorderRadius.horizontal(left: Radius.circular(12.0), right: Radius.circular(12.0)))),
|
||||
elevation: WidgetStatePropertyAll(8.0)
|
||||
),
|
||||
chipTheme: const ChipThemeData(
|
||||
side: BorderSide(color: Colors.transparent),
|
||||
),
|
||||
segmentedButtonTheme: SegmentedButtonThemeData(
|
||||
style: ButtonStyle(
|
||||
side: const WidgetStatePropertyAll(BorderSide(color: Colors.transparent)),
|
||||
surfaceTintColor: const WidgetStatePropertyAll(Colors.cyanAccent),
|
||||
iconColor: const WidgetStatePropertyAll(Colors.cyanAccent),
|
||||
shadowColor: WidgetStatePropertyAll(Colors.cyanAccent.shade200),
|
||||
)
|
||||
),
|
||||
scaffoldBackgroundColor: Colors.black
|
||||
);
|
||||
|
||||
final router = GoRouter(
|
||||
initialLocation: "/",
|
||||
|
@ -189,4 +183,4 @@ class MyAppState extends State<MyApp> {
|
|||
theme: theme
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +1,6 @@
|
|||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
class UserAgentClient extends http.BaseClient {
|
||||
|
@ -9,6 +12,7 @@ class UserAgentClient extends http.BaseClient {
|
|||
@override
|
||||
Future<http.StreamedResponse> send(http.BaseRequest request) {
|
||||
request.headers['user-agent'] = userAgent;
|
||||
if (!kIsWeb) request.headers['X-Session-ID'] = "${Random().nextInt(1<<32)}";
|
||||
return _inner.send(request);
|
||||
}
|
||||
}
|
|
@ -33,6 +33,7 @@ class DB {
|
|||
await db.execute(createTetrioUsersToTrack);
|
||||
await db.execute(createTetrioTLRecordsTable);
|
||||
await db.execute(createTetrioTLReplayStats);
|
||||
await db.execute(createTetrioLeagueTable);
|
||||
} on MissingPlatformDirectoryException {
|
||||
throw UnableToGetDocuments();
|
||||
}
|
||||
|
|
|
@ -4,8 +4,9 @@ import 'dart:async';
|
|||
import 'dart:convert';
|
||||
import 'dart:developer' as developer;
|
||||
import 'dart:io';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:sqflite/sql.dart';
|
||||
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
||||
import 'package:tetra_stats/data_objects/tetra_stats.dart';
|
||||
import 'package:tetra_stats/data_objects/tetrio_multiplayer_replay.dart';
|
||||
import 'package:tetra_stats/main.dart' show packageInfo;
|
||||
|
@ -22,6 +23,7 @@ const String tetrioUsersTable = "tetrioUsers";
|
|||
const String tetrioUsersToTrackTable = "tetrioUsersToTrack";
|
||||
const String tetraLeagueMatchesTable = "tetrioAlphaLeagueMathces";
|
||||
const String tetrioTLReplayStatsTable = "tetrioTLReplayStats";
|
||||
const String tetrioLeagueTable = "tetrioLeague";
|
||||
const String idCol = "id";
|
||||
const String replayID = "replayId";
|
||||
const String nickCol = "nickname";
|
||||
|
@ -68,6 +70,33 @@ const String createTetrioTLReplayStats = '''
|
|||
PRIMARY KEY("id")
|
||||
)
|
||||
''';
|
||||
const String createTetrioLeagueTable = '''
|
||||
CREATE TABLE IF NOT EXISTS "tetrioLeague" (
|
||||
"id" TEXT NOT NULL,
|
||||
"gamesplayed" INTEGER NOT NULL DEFAULT 0,
|
||||
"gameswon" INTEGER NOT NULL DEFAULT 0,
|
||||
"tr" REAL,
|
||||
"glicko" REAL,
|
||||
"rd" REAL,
|
||||
"gxe" REAL,
|
||||
"rank" TEXT NOT NULL DEFAULT 'z',
|
||||
"bestrank" TEXT NOT NULL DEFAULT 'z',
|
||||
"apm" REAL,
|
||||
"pps" REAL,
|
||||
"vs" REAL,
|
||||
"decaying" INTEGER NOT NULL DEFAULT 0,
|
||||
"standing" INTEGER NOT NULL DEFAULT -1,
|
||||
"standing_local" INTEGER NOT NULL DEFAULT -1,
|
||||
"percentile" REAL NOT NULL,
|
||||
"prev_rank" TEXT,
|
||||
"prev_at" INTEGER NOT NULL DEFAULT -1,
|
||||
"next_rank" TEXT,
|
||||
"next_at" INTEGER NOT NULL DEFAULT -1,
|
||||
"percentile_rank" TEXT NOT NULL DEFAULT 'z',
|
||||
"season" INTEGER NOT NULL DEFAULT 1,
|
||||
PRIMARY KEY("id")
|
||||
)
|
||||
''';
|
||||
|
||||
class CacheController {
|
||||
late Map<String, dynamic> _cache;
|
||||
|
@ -90,8 +119,6 @@ class CacheController {
|
|||
return object.runtimeType.toString();
|
||||
case TetrioPlayerFromLeaderboard: // i may be a little stupid
|
||||
return "${object.runtimeType}topone";
|
||||
case TetraLeagueAlphaStream:
|
||||
return object.runtimeType.toString()+object.userId;
|
||||
case SingleplayerStream:
|
||||
return object.type+object.userId;
|
||||
default:
|
||||
|
@ -99,8 +126,8 @@ class CacheController {
|
|||
}
|
||||
}
|
||||
|
||||
void store(dynamic object, int? cachedUntil) async {
|
||||
String key = _getObjectId(object) + cachedUntil!.toString();
|
||||
void store(dynamic object, int cachedUntil) async {
|
||||
String key = _getObjectId(object) + cachedUntil.toString();
|
||||
_cache[key] = object;
|
||||
}
|
||||
|
||||
|
@ -113,6 +140,8 @@ class CacheController {
|
|||
objectEntry = id.length <= 16 ? _cache.entries.firstWhere((element) => element.key.startsWith(_nicknames[id]??"huh?")) : _cache.entries.firstWhere((element) => element.key.startsWith(id));
|
||||
if (id.length <= 16) id = _nicknames[id]??"huh?";
|
||||
break;
|
||||
case SingleplayerStream:
|
||||
objectEntry = _cache.entries.firstWhere((element) => element.key.startsWith(id));
|
||||
default:
|
||||
objectEntry = _cache.entries.firstWhere((element) => element.key.startsWith(datatype.toString()+id));
|
||||
id = datatype.toString()+id;
|
||||
|
@ -184,6 +213,7 @@ class TetrioService extends DB {
|
|||
_players.removeWhere((key, value) => key == id);
|
||||
_tetrioStreamController.add(_players);
|
||||
}
|
||||
await db.delete(tetrioLeagueTable, where: "id LIKE ?", whereArgs: ["$id%"]);
|
||||
}
|
||||
|
||||
/// Gets nickname from database or requests it from API if missing.
|
||||
|
@ -309,15 +339,15 @@ class TetrioService extends DB {
|
|||
|
||||
/// Retrieves avaliable Tetra League matches from Tetra Channel api. Returns stream object (fake stream).
|
||||
/// Throws an exception if fails to retrieve.
|
||||
Future<SingleplayerStream> fetchSingleplayerStream(String userID, String stream) async {
|
||||
SingleplayerStream? cached = _cache.get(userID, SingleplayerStream);
|
||||
Future<SingleplayerStream> fetchStream(String userID, String stream) async {
|
||||
SingleplayerStream? cached = _cache.get(stream+userID, SingleplayerStream);
|
||||
if (cached != null) return cached;
|
||||
|
||||
Uri url;
|
||||
if (kIsWeb) {
|
||||
url = Uri.https('ts.dan63.by', 'oskware_bridge.php', {"endpoint": "singleplayerStream", "user": userID.toLowerCase().trim(), "stream": stream});
|
||||
} else {
|
||||
url = Uri.https('ch.tetr.io', 'api/streams/${stream}_${userID.toLowerCase().trim()}');
|
||||
url = Uri.https('ch.tetr.io', 'api/users/${userID.toLowerCase().trim()}/records/$stream');
|
||||
}
|
||||
try {
|
||||
final response = await client.get(url);
|
||||
|
@ -325,7 +355,7 @@ class TetrioService extends DB {
|
|||
switch (response.statusCode) {
|
||||
case 200:
|
||||
if (jsonDecode(response.body)['success']) {
|
||||
SingleplayerStream records = SingleplayerStream.fromJson(jsonDecode(response.body)['data']['records'], userID, stream);
|
||||
SingleplayerStream records = SingleplayerStream.fromJson(jsonDecode(response.body)['data']['entries'], userID, stream);
|
||||
_cache.store(records, jsonDecode(response.body)['cache']['cached_until']);
|
||||
developer.log("fetchSingleplayerStream: $stream $userID stream retrieved and cached", name: "services/tetrio_crud");
|
||||
return records;
|
||||
|
@ -408,15 +438,15 @@ class TetrioService extends DB {
|
|||
// Sidenote: as you can see, fetch functions looks and works pretty much same way, as described above,
|
||||
// so i'm going to document only unique differences between them
|
||||
|
||||
Future<Cutoffs?> fetchCutoffs() async {
|
||||
Cutoffs? cached = _cache.get("", Cutoffs);
|
||||
Future<CutoffsTetrio?> fetchCutoffsTetrio() async {
|
||||
CutoffsTetrio? cached = _cache.get("league_ranks", CutoffsTetrio);
|
||||
if (cached != null) return cached;
|
||||
|
||||
Uri url;
|
||||
if (kIsWeb) {
|
||||
url = Uri.https('ts.dan63.by', 'oskware_bridge.php', {"endpoint": "TLCutoffs"});
|
||||
url = Uri.https('ts.dan63.by', 'oskware_bridge.php', {"endpoint": "cutoffs"});
|
||||
} else {
|
||||
url = Uri.https('api.p1nkl0bst3r.xyz', 'rankcutoff', {"users": null});
|
||||
url = Uri.https('ch.tetr.io', 'api/labs/league_ranks');
|
||||
}
|
||||
|
||||
try{
|
||||
|
@ -425,16 +455,58 @@ class TetrioService extends DB {
|
|||
switch (response.statusCode) {
|
||||
case 200:
|
||||
Map<String, dynamic> rawData = jsonDecode(response.body);
|
||||
Map<String, dynamic> data = rawData["cutoffs"] as Map<String, dynamic>;
|
||||
Cutoffs result = Cutoffs({}, {});
|
||||
for (String rank in data.keys){
|
||||
result.tr[rank] = data[rank]["rating"];
|
||||
result.glicko[rank] = data[rank]["glicko"];
|
||||
}
|
||||
_cache.store(result, rawData["ts"] + 300000);
|
||||
CutoffsTetrio result = CutoffsTetrio.fromJson(rawData['data']);
|
||||
_cache.store(result, rawData["cache"]["cached_until"]);
|
||||
return result;
|
||||
case 404:
|
||||
developer.log("fetchCutoffs: Cutoffs are gone", name: "services/tetrio_crud", error: response.statusCode);
|
||||
developer.log("fetchCutoffsTetrio: Cutoffs are gone", name: "services/tetrio_crud", error: response.statusCode);
|
||||
return null;
|
||||
// if not 200 or 404 - throw a unique for each code exception
|
||||
case 403:
|
||||
throw TetrioForbidden();
|
||||
case 429:
|
||||
throw TetrioTooManyRequests();
|
||||
case 418:
|
||||
throw TetrioOskwareBridgeProblem();
|
||||
case 500:
|
||||
case 502:
|
||||
case 503:
|
||||
case 504:
|
||||
developer.log("fetchCutoffsTetrio: Cutoffs are unavalable (${response.statusCode})", name: "services/tetrio_crud", error: response.statusCode);
|
||||
return null;
|
||||
default:
|
||||
developer.log("fetchCutoffsTetrio: Failed to fetch top Cutoffs", name: "services/tetrio_crud", error: response.statusCode);
|
||||
throw ConnectionIssue(response.statusCode, response.reasonPhrase??"No reason");
|
||||
}
|
||||
} on http.ClientException catch (e, s) { // If local http client fails
|
||||
developer.log("$e, $s");
|
||||
throw http.ClientException(e.message, e.uri); // just assuming, that our end user don't have acess to the internet
|
||||
}
|
||||
}
|
||||
|
||||
Future<Cutoffs?> fetchCutoffsBeanserver() async {
|
||||
Cutoffs? cached = _cache.get("", Cutoffs);
|
||||
if (cached != null) return cached;
|
||||
|
||||
Uri url = Uri.https('ts.dan63.by', 'beanserver_blaster/cutoffs.json');
|
||||
|
||||
try{
|
||||
final response = await client.get(url);
|
||||
|
||||
switch (response.statusCode) {
|
||||
case 200:
|
||||
Map<String, dynamic> rawData = jsonDecode(response.body);
|
||||
Map<String, dynamic> data = rawData["data"] as Map<String, dynamic>;
|
||||
Cutoffs result = Cutoffs(DateTime.fromMillisecondsSinceEpoch(rawData["created"]), {}, {}, {});
|
||||
for (String rank in data.keys){
|
||||
result.tr[rank] = data[rank]["tr"];
|
||||
result.glicko[rank] = data[rank]["glicko"];
|
||||
result.gxe[rank] = data[rank]["gxe"];
|
||||
}
|
||||
_cache.store(result, rawData["cache_until"]);
|
||||
return result;
|
||||
case 404:
|
||||
developer.log("fetchCutoffsBeanserver: Cutoffs are gone", name: "services/tetrio_crud", error: response.statusCode);
|
||||
return null;
|
||||
// if not 200 or 404 - throw a unique for each code exception
|
||||
case 403:
|
||||
|
@ -447,10 +519,10 @@ class TetrioService extends DB {
|
|||
case 502:
|
||||
case 503:
|
||||
case 504:
|
||||
developer.log("fetchCutoffs: Cutoffs are unavalable (${response.statusCode})", name: "services/tetrio_crud", error: response.statusCode);
|
||||
developer.log("fetchCutoffsBeanserver: Cutoffs are unavalable (${response.statusCode})", name: "services/tetrio_crud", error: response.statusCode);
|
||||
return null;
|
||||
default:
|
||||
developer.log("fetchCutoffs: Failed to fetch top Cutoffs", name: "services/tetrio_crud", error: response.statusCode);
|
||||
developer.log("fetchCutoffsBeanserver: Failed to fetch top Cutoffs", name: "services/tetrio_crud", error: response.statusCode);
|
||||
throw ConnectionIssue(response.statusCode, response.reasonPhrase??"No reason");
|
||||
}
|
||||
} on http.ClientException catch (e, s) { // If local http client fails
|
||||
|
@ -467,7 +539,7 @@ class TetrioService extends DB {
|
|||
if (kIsWeb) {
|
||||
url = Uri.https('ts.dan63.by', 'oskware_bridge.php', {"endpoint": "TLTopOne"});
|
||||
} else {
|
||||
url = Uri.https('ch.tetr.io', 'api/users/lists/league', {"after": "25000", "limit": "1"});
|
||||
url = Uri.https('ch.tetr.io', 'api/users/by/league', {"after": "25000:0:0", "limit": "1"});
|
||||
}
|
||||
|
||||
try{
|
||||
|
@ -476,7 +548,7 @@ class TetrioService extends DB {
|
|||
switch (response.statusCode) {
|
||||
case 200:
|
||||
var rawJson = jsonDecode(response.body);
|
||||
TetrioPlayerFromLeaderboard result = TetrioPlayerFromLeaderboard.fromJson(rawJson["data"]["users"][0], DateTime.fromMillisecondsSinceEpoch(rawJson["cache"]["cached_at"]));
|
||||
TetrioPlayerFromLeaderboard result = TetrioPlayerFromLeaderboard.fromJson(rawJson["data"]["entries"][0], DateTime.fromMillisecondsSinceEpoch(rawJson["cache"]["cached_at"]));
|
||||
_cache.store(result, rawJson["cache"]["cached_until"]);
|
||||
return result;
|
||||
case 404:
|
||||
|
@ -505,7 +577,7 @@ class TetrioService extends DB {
|
|||
|
||||
/// 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.
|
||||
Future<List<TetrioPlayer>> fetchAndsaveTLHistory(String id) async {
|
||||
Future<List<TetraLeague>> fetchAndsaveTLHistory(String id) async {
|
||||
Uri url;
|
||||
if (kIsWeb) {
|
||||
url = Uri.https('ts.dan63.by', 'oskware_bridge.php', {"endpoint": "TLHistory", "user": id});
|
||||
|
@ -517,27 +589,15 @@ class TetrioService extends DB {
|
|||
|
||||
switch (response.statusCode) {
|
||||
case 200:
|
||||
await ensureDbIsOpen();
|
||||
final db = getDatabaseOrThrow();
|
||||
// that one api returns csv instead of json
|
||||
List<List<dynamic>> csv = const CsvToListConverter().convert(response.body)..removeAt(0);
|
||||
List<TetrioPlayer> history = [];
|
||||
// doesn't return nickname, need to retrieve it separately
|
||||
String nick = await getNicknameByID(id);
|
||||
List<TetraLeague> history = [];
|
||||
Batch batch = db.batch();
|
||||
for (List<dynamic> entry in csv){ // each entry is one state
|
||||
TetrioPlayer state = TetrioPlayer(
|
||||
userId: id,
|
||||
username: nick,
|
||||
role: "p1nkl0bst3r",
|
||||
state: DateTime.parse(entry[9]),
|
||||
badges: [],
|
||||
friendCount: -1,
|
||||
gamesPlayed: -1,
|
||||
gamesWon: -1,
|
||||
gameTime: const Duration(seconds: -1),
|
||||
xp: -1,
|
||||
supporterTier: 0,
|
||||
verified: false,
|
||||
connections: null,
|
||||
tlSeason1: TetraLeagueAlpha(
|
||||
TetraLeague state = TetraLeague(
|
||||
id: id,
|
||||
timestamp: DateTime.parse(entry[9]),
|
||||
apm: entry[6] != '' ? entry[6] : null,
|
||||
pps: entry[7] != '' ? entry[7] : null,
|
||||
|
@ -548,33 +608,21 @@ class TetrioService extends DB {
|
|||
gamesWon: entry[2],
|
||||
bestRank: "z",
|
||||
decaying: false,
|
||||
rating: entry[3],
|
||||
tr: entry[3],
|
||||
gxe: -1,
|
||||
rank: entry[5],
|
||||
percentileRank: entry[5],
|
||||
percentile: rankCutoffs[entry[5]]!,
|
||||
standing: -1,
|
||||
standingLocal: -1,
|
||||
nextAt: -1,
|
||||
prevAt: -1
|
||||
),
|
||||
sprint: [],
|
||||
blitz: []
|
||||
prevAt: -1,
|
||||
season: 1
|
||||
);
|
||||
history.add(state);
|
||||
batch.insert(tetrioLeagueTable, state.toJson(), conflictAlgorithm: ConflictAlgorithm.replace);
|
||||
}
|
||||
|
||||
// trying to dump it to local DB
|
||||
await ensureDbIsOpen();
|
||||
final db = getDatabaseOrThrow();
|
||||
List<TetrioPlayer> states = await getPlayer(id);
|
||||
if (states.isEmpty) await createPlayer(history.first);
|
||||
states.insertAll(0, history.reversed);
|
||||
final Map<String, dynamic> statesJson = {};
|
||||
for (var e in states) { // making one big json out of this list
|
||||
statesJson.addEntries({(e.state.millisecondsSinceEpoch ~/ 1000).toString(): e.toJson()}.entries);
|
||||
}
|
||||
// and putting it to local DB
|
||||
await db.update(tetrioUsersTable, {idCol: id, nickCol: nick, statesCol: jsonEncode(statesJson)}, where: '$idCol = ?', whereArgs: [id]);
|
||||
batch.commit();
|
||||
return history;
|
||||
case 404:
|
||||
developer.log("fetchTLHistory: Probably, history doesn't exist", name: "services/tetrio_crud", error: response.statusCode);
|
||||
|
@ -601,7 +649,7 @@ class TetrioService extends DB {
|
|||
}
|
||||
|
||||
/// Docs later
|
||||
Future<List<TetraLeagueAlphaRecord>> fetchAndSaveOldTLmatches(String userID) async {
|
||||
Future<TetraLeagueAlphaStream> fetchAndSaveOldTLmatches(String userID) async {
|
||||
Uri url;
|
||||
if (kIsWeb) {
|
||||
url = Uri.https('ts.dan63.by', 'oskware_bridge.php', {"endpoint": "TLMatches", "user": userID});
|
||||
|
@ -616,7 +664,7 @@ class TetrioService extends DB {
|
|||
case 200:
|
||||
TetraLeagueAlphaStream stream = TetraLeagueAlphaStream.fromJson(jsonDecode(response.body)['data']['records'], userID);
|
||||
saveTLMatchesFromStream(stream);
|
||||
return stream.records;
|
||||
return stream;
|
||||
case 404:
|
||||
developer.log("fetchAndSaveOldTLmatches: Probably, history doesn't exist", name: "services/tetrio_crud", error: response.statusCode);
|
||||
throw TetrioHistoryNotExist();
|
||||
|
@ -645,13 +693,9 @@ class TetrioService extends DB {
|
|||
Future<TetrioPlayersLeaderboard> fetchTLLeaderboard() async {
|
||||
TetrioPlayersLeaderboard? cached = _cache.get("league", TetrioPlayersLeaderboard);
|
||||
if (cached != null) return cached;
|
||||
|
||||
Uri url;
|
||||
if (kIsWeb) {
|
||||
url = Uri.https('ts.dan63.by', 'oskware_bridge.php', {"endpoint": "TLLeaderboard"});
|
||||
} else {
|
||||
url = Uri.https('ch.tetr.io', 'api/users/lists/league/all');
|
||||
}
|
||||
|
||||
Uri url = Uri.https('ts.dan63.by', 'beanserver_blaster/leaderboard.json');
|
||||
|
||||
try{
|
||||
final response = await client.get(url);
|
||||
|
||||
|
@ -659,16 +703,10 @@ class TetrioService extends DB {
|
|||
case 200:
|
||||
_lbPositions.clear();
|
||||
var rawJson = jsonDecode(response.body);
|
||||
if (rawJson['success']) { // if api confirmed that everything ok
|
||||
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;
|
||||
_cache.store(leaderboard, rawJson['cache']['cached_until']);
|
||||
return leaderboard;
|
||||
} else { // idk how to hit that one
|
||||
developer.log("fetchTLLeaderboard: Bruh", name: "services/tetrio_crud", error: rawJson);
|
||||
throw Exception("Failed to get leaderboard (problems on the tetr.io side)"); // will it be on tetr.io side?
|
||||
}
|
||||
TetrioPlayersLeaderboard leaderboard = TetrioPlayersLeaderboard.fromJson(rawJson['data'], "league", DateTime.fromMillisecondsSinceEpoch(rawJson['created']));
|
||||
developer.log("fetchTLLeaderboard: Leaderboard retrieved and cached", name: "services/tetrio_crud");
|
||||
_cache.store(leaderboard, rawJson['cache_until']);
|
||||
return leaderboard;
|
||||
case 403:
|
||||
throw TetrioForbidden();
|
||||
case 429:
|
||||
|
@ -690,6 +728,20 @@ class TetrioService extends DB {
|
|||
}
|
||||
}
|
||||
|
||||
// Stream<TetrioPlayersLeaderboard> fetchFullLeaderboard() async* {
|
||||
// late double after;
|
||||
// int lbLength = 100;
|
||||
// TetrioPlayersLeaderboard leaderboard = await fetchTLLeaderboard();
|
||||
// after = leaderboard.leaderboard.last.tr;
|
||||
// while (lbLength == 100){
|
||||
// TetrioPlayersLeaderboard pseudoLb = await fetchTLLeaderboard(after: after);
|
||||
// leaderboard.addPlayers(pseudoLb.leaderboard);
|
||||
// lbLength = pseudoLb.leaderboard.length;
|
||||
// after = pseudoLb.leaderboard.last.tr;
|
||||
// yield leaderboard;
|
||||
// }
|
||||
// }
|
||||
|
||||
// i want to know progress, so i trying to figure out this thing:
|
||||
// Stream<TetrioPlayersLeaderboard> fetchTLLeaderboardAsStream() async {
|
||||
// TetrioPlayersLeaderboard? cached = _cache.get("league", TetrioPlayersLeaderboard);
|
||||
|
@ -711,7 +763,7 @@ class TetrioService extends DB {
|
|||
|
||||
/// Retrieves and returns 100 latest news entries from Tetra Channel api for given [userID]. Throws an exception if fails to retrieve.
|
||||
Future<News> fetchNews(String userID) async{
|
||||
News? cached = _cache.get(userID, News);
|
||||
News? cached = _cache.get("user_$userID", News);
|
||||
if (cached != null) return cached;
|
||||
|
||||
Uri url;
|
||||
|
@ -758,15 +810,15 @@ class TetrioService extends DB {
|
|||
|
||||
/// Retrieves avaliable Tetra League matches from Tetra Channel api. Returns stream object (fake stream).
|
||||
/// Throws an exception if fails to retrieve.
|
||||
Future<TetraLeagueAlphaStream> fetchTLStream(String userID) async {
|
||||
TetraLeagueAlphaStream? cached = _cache.get(userID, TetraLeagueAlphaStream);
|
||||
Future<TetraLeagueBetaStream> fetchTLStream(String userID) async {
|
||||
TetraLeagueBetaStream? cached = _cache.get(userID, TetraLeagueBetaStream);
|
||||
if (cached != null) return cached;
|
||||
|
||||
Uri url;
|
||||
if (kIsWeb) {
|
||||
url = Uri.https('ts.dan63.by', 'oskware_bridge.php', {"endpoint": "tetrioUserTL", "user": userID.toLowerCase().trim()});
|
||||
} else {
|
||||
url = Uri.https('ch.tetr.io', 'api/streams/league_userrecent_${userID.toLowerCase().trim()}');
|
||||
url = Uri.https('ch.tetr.io', 'api/users/${userID.toLowerCase().trim()}/records/league/recent');
|
||||
}
|
||||
try {
|
||||
final response = await client.get(url);
|
||||
|
@ -774,7 +826,7 @@ class TetrioService extends DB {
|
|||
switch (response.statusCode) {
|
||||
case 200:
|
||||
if (jsonDecode(response.body)['success']) {
|
||||
TetraLeagueAlphaStream stream = TetraLeagueAlphaStream.fromJson(jsonDecode(response.body)['data']['records'], userID);
|
||||
TetraLeagueBetaStream stream = TetraLeagueBetaStream.fromJson(jsonDecode(response.body)['data']['entries'], userID);
|
||||
_cache.store(stream, jsonDecode(response.body)['cache']['cached_until']);
|
||||
developer.log("fetchTLStream: $userID stream retrieved and cached", name: "services/tetrio_crud");
|
||||
return stream;
|
||||
|
@ -906,10 +958,10 @@ class TetrioService extends DB {
|
|||
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'])
|
||||
? RecordSingle.fromJson(jsonRecords['data']['records']['40l']['record'], jsonRecords['data']['records']['40l']['rank'], jsonRecords['data']['records']['40l']['rank_local'])
|
||||
: null;
|
||||
var blitz = jsonRecords['data']['records']['blitz']['record'] != null
|
||||
? RecordSingle.fromJson(jsonRecords['data']['records']['blitz']['record'], jsonRecords['data']['records']['blitz']['rank'])
|
||||
? 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);
|
||||
|
@ -941,6 +993,52 @@ class TetrioService extends DB {
|
|||
}
|
||||
}
|
||||
|
||||
Future<Summaries> fetchSummaries(String id) async {
|
||||
Summaries? cached = _cache.get(id, Summaries);
|
||||
if (cached != null) return cached;
|
||||
|
||||
Uri url;
|
||||
if (kIsWeb) {
|
||||
url = Uri.https('ts.dan63.by', 'oskware_bridge.php', {"endpoint": "Summaries", "id": id});
|
||||
} else {
|
||||
url = Uri.https('ch.tetr.io', 'api/users/$id/summaries');
|
||||
}
|
||||
|
||||
try{
|
||||
final response = await client.get(url);
|
||||
|
||||
switch (response.statusCode) {
|
||||
case 200:
|
||||
if (jsonDecode(response.body)['success']) {
|
||||
developer.log("fetchSummaries: $id summaries retrieved and cached", name: "services/tetrio_crud");
|
||||
Summaries summaries = Summaries.fromJson(jsonDecode(response.body)['data'], id);
|
||||
_cache.store(summaries, jsonDecode(response.body)['cache']['cached_until']);
|
||||
return summaries;
|
||||
} else {
|
||||
developer.log("fetchSummaries: User dosen't exist", name: "services/tetrio_crud", error: response.body);
|
||||
throw TetrioPlayerNotExist();
|
||||
}
|
||||
case 403:
|
||||
throw TetrioForbidden();
|
||||
case 429:
|
||||
throw TetrioTooManyRequests();
|
||||
case 418:
|
||||
throw TetrioOskwareBridgeProblem();
|
||||
case 500:
|
||||
case 502:
|
||||
case 503:
|
||||
case 504:
|
||||
throw TetrioInternalProblem();
|
||||
default:
|
||||
developer.log("fetchRecords Failed to fetch records", name: "services/tetrio_crud", error: response.statusCode);
|
||||
throw ConnectionIssue(response.statusCode, response.reasonPhrase??"No reason");
|
||||
}
|
||||
} on http.ClientException catch (e, s) {
|
||||
developer.log("$e, $s");
|
||||
throw http.ClientException(e.message, e.uri);
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates an entry in local DB for [tetrioPlayer]. Throws an exception if that player already here.
|
||||
Future<void> createPlayer(TetrioPlayer tetrioPlayer) async {
|
||||
await ensureDbIsOpen();
|
||||
|
@ -967,7 +1065,10 @@ class TetrioService extends DB {
|
|||
if (results.isNotEmpty) {
|
||||
throw TetrioPlayerAlreadyExist();
|
||||
}
|
||||
await db.insert(tetrioUsersTable, {idCol: tetrioPlayer.userId, nickCol: tetrioPlayer.username}, conflictAlgorithm: ConflictAlgorithm.replace);
|
||||
db.insert(tetrioUsersToTrackTable, {idCol: tetrioPlayer.userId});
|
||||
_players[tetrioPlayer.userId] = tetrioPlayer.username;
|
||||
_tetrioStreamController.add(_players);
|
||||
}
|
||||
|
||||
/// Returns bool, which tells whether is given [id] is in [tetrioUsersToTrackTable].
|
||||
|
@ -991,6 +1092,7 @@ class TetrioService extends DB {
|
|||
await ensureDbIsOpen();
|
||||
final db = getDatabaseOrThrow();
|
||||
final deletedPlayer = await db.delete(tetrioUsersToTrackTable, where: '$idCol = ?', whereArgs: [id.toLowerCase()]);
|
||||
await db.delete(tetrioUsersTable, where: '$idCol = ?', whereArgs: [id.toLowerCase()]);
|
||||
if (deletedPlayer != 1) {
|
||||
throw CouldNotDeletePlayer();
|
||||
} else {
|
||||
|
@ -999,71 +1101,30 @@ class TetrioService extends DB {
|
|||
}
|
||||
}
|
||||
|
||||
/// Saves state (which is [tetrioPlayer]) to the local database.
|
||||
Future<void> storeState(TetrioPlayer tetrioPlayer) async {
|
||||
// if tetrio player doesn't have entry in database - just calling different function
|
||||
List<TetrioPlayer> states = await getPlayer(tetrioPlayer.userId);
|
||||
if (states.isEmpty) {
|
||||
await createPlayer(tetrioPlayer);
|
||||
return;
|
||||
}
|
||||
|
||||
// we not going to add state, that is same, as the previous
|
||||
if (!states.last.isSameState(tetrioPlayer)) states.add(tetrioPlayer);
|
||||
|
||||
// Making map of the states
|
||||
final Map<String, dynamic> statesJson = {};
|
||||
for (var e in states) {
|
||||
// Saving in format: {"unix_seconds": json_of_state}
|
||||
statesJson.addEntries({(e.state.millisecondsSinceEpoch ~/ 1000).toString(): e.toJson()}.entries);
|
||||
}
|
||||
|
||||
// Rewrite our database
|
||||
Future<List<TetraLeague>> getStates(String userID, {int? season}) async {
|
||||
await ensureDbIsOpen();
|
||||
final db = getDatabaseOrThrow();
|
||||
await db.update(tetrioUsersTable, {idCol: tetrioPlayer.userId, nickCol: tetrioPlayer.username, statesCol: jsonEncode(statesJson)},
|
||||
where: '$idCol = ?', whereArgs: [tetrioPlayer.userId]);
|
||||
List<Map> query = await db.query(tetrioLeagueTable, where: season != null ? '"id" LIKE ? AND "season" = ?' : '"id" LIKE ?', whereArgs: season != null ? ["${userID}%", season] : ["${userID}%"], orderBy: '"id" ASC');
|
||||
return [for (var entry in query) TetraLeague.fromJson(entry as Map<String, dynamic>, DateTime.fromMillisecondsSinceEpoch(int.parse(entry["id"].substring(24), radix: 16)), entry["season"], entry["id"].substring(0, 24))];
|
||||
}
|
||||
|
||||
/// Remove state (which is [tetrioPlayer]) from the local database
|
||||
Future<void> deleteState(TetrioPlayer tetrioPlayer) async {
|
||||
/// Saves state (which is [TetraLeague]) to the local database.
|
||||
Future<void> storeState(TetraLeague league) async {
|
||||
await ensureDbIsOpen();
|
||||
final db = getDatabaseOrThrow();
|
||||
List<TetrioPlayer> states = await getPlayer(tetrioPlayer.userId);
|
||||
// removing state from map that contain every state of each user
|
||||
states.removeWhere((element) => element.state == tetrioPlayer.state);
|
||||
|
||||
// Making map of the states (without deleted one)
|
||||
final Map<String, dynamic> statesJson = {};
|
||||
for (var e in states) {
|
||||
statesJson.addEntries({(e.state.millisecondsSinceEpoch ~/ 1000).toString(): e.toJson()}.entries);
|
||||
List<Map> test = await db.query(tetrioLeagueTable, where: '"id" LIKE ? AND "gamesplayed" = ? AND "rd" = ?', whereArgs: ["${league.id}%", league.gamesPlayed, league.rd]);
|
||||
if (test.isEmpty) {
|
||||
await db.insert(tetrioLeagueTable, league.toJson());
|
||||
}
|
||||
// Rewriting database entry with new json
|
||||
await db.update(tetrioUsersTable, {idCol: tetrioPlayer.userId, nickCol: tetrioPlayer.username, statesCol: jsonEncode(statesJson)},
|
||||
where: '$idCol = ?', whereArgs: [tetrioPlayer.userId]);
|
||||
_tetrioStreamController.add(_players);
|
||||
}
|
||||
|
||||
/// Returns list of all states of player with given [id] from database. Can return empty list if player
|
||||
/// was not found.
|
||||
Future<List<TetrioPlayer>> getPlayer(String id) async {
|
||||
/// Remove state, which has [dbID] from the local database
|
||||
/// ([dbid] is a concatenation of player id and UINX milliseconds in hex)
|
||||
Future<void> deleteState(String dbID) async {
|
||||
await ensureDbIsOpen();
|
||||
final db = getDatabaseOrThrow();
|
||||
List<TetrioPlayer> states = [];
|
||||
final results = await db.query(tetrioUsersTable, limit: 1, where: '$idCol = ?', whereArgs: [id.toLowerCase()]);
|
||||
if (results.isEmpty) {
|
||||
return states; // it empty
|
||||
} else {
|
||||
dynamic rawStates = results.first['jsonStates'] as String;
|
||||
rawStates = json.decode(rawStates);
|
||||
// recreating objects of states
|
||||
rawStates.forEach((k, v) => states.add(TetrioPlayer.fromJson(v, DateTime.fromMillisecondsSinceEpoch(int.parse(k) * 1000), id, results.first[nickCol] as String)));
|
||||
// updating the stream
|
||||
_players.removeWhere((key, value) => key == id);
|
||||
_players.addEntries({states.last.userId: states.last.username}.entries);
|
||||
_tetrioStreamController.add(_players);
|
||||
return states;
|
||||
}
|
||||
int result = await db.delete(tetrioLeagueTable, where: "id = ?", whereArgs: [dbID]);
|
||||
if (result == 0) throw Exception("Failed to remove a row $dbID - it's probably not exist");
|
||||
}
|
||||
|
||||
/// Retrieves general stats of [user] (nickname or id) from Tetra Channel api. Returns [TetrioPlayer] object of this user.
|
||||
|
@ -1097,6 +1158,8 @@ class TetrioService extends DB {
|
|||
// more exceptions to god of exceptions
|
||||
case 403:
|
||||
throw TetrioForbidden();
|
||||
case 404:
|
||||
throw TetrioPlayerNotExist();
|
||||
case 429:
|
||||
throw TetrioTooManyRequests();
|
||||
case 418:
|
||||
|
@ -1131,7 +1194,7 @@ class TetrioService extends DB {
|
|||
var json = jsonDecode(response.body);
|
||||
if (json['success']) {
|
||||
// parse and count stats
|
||||
TetrioPlayer player = TetrioPlayer.fromJson(json['data']['user'], DateTime.fromMillisecondsSinceEpoch(json['cache']['cached_at'], isUtc: true), json['data']['user']['_id'], json['data']['user']['username'], DateTime.fromMillisecondsSinceEpoch(json['cache']['cached_until'], isUtc: true));
|
||||
TetrioPlayer player = TetrioPlayer.fromJson(json['data'], DateTime.fromMillisecondsSinceEpoch(json['cache']['cached_at'], isUtc: true), json['data']['_id'], json['data']['username'], DateTime.fromMillisecondsSinceEpoch(json['cache']['cached_until'], isUtc: true));
|
||||
_cache.store(player, json['cache']['cached_until']);
|
||||
developer.log("fetchPlayer: $user retrieved and cached", name: "services/tetrio_crud");
|
||||
return player;
|
||||
|
@ -1141,6 +1204,8 @@ class TetrioService extends DB {
|
|||
}
|
||||
case 403:
|
||||
throw TetrioForbidden();
|
||||
case 404:
|
||||
throw TetrioPlayerNotExist();
|
||||
case 429:
|
||||
throw TetrioTooManyRequests();
|
||||
case 418:
|
||||
|
@ -1160,29 +1225,26 @@ class TetrioService extends DB {
|
|||
}
|
||||
}
|
||||
|
||||
/// Retrieves whole [tetrioUsersTable] and returns Map with [TetrioPlayer] objects of everyone in database
|
||||
Future<Map<String, List<TetrioPlayer>>> getAllPlayers() async {
|
||||
/// Retrieves whole [tetrioUsersTable] and returns Map {id: nickname} of everyone in database
|
||||
Future<Map<String, String>> getAllPlayers() async {
|
||||
await ensureDbIsOpen();
|
||||
final db = getDatabaseOrThrow();
|
||||
final players = await db.query(tetrioUsersTable);
|
||||
Map<String, List<TetrioPlayer>> data = {};
|
||||
Map<String, String> data = {};
|
||||
for (var entry in players){
|
||||
var test = json.decode(entry['jsonStates'] as String);
|
||||
List<TetrioPlayer> states = [];
|
||||
test.forEach((k, v) => states.add(TetrioPlayer.fromJson(v, DateTime.fromMillisecondsSinceEpoch(int.parse(k) * 1000), entry[idCol] as String, entry[nickCol] as String)));
|
||||
data.addEntries({states.last.userId: states}.entries);
|
||||
data[entry[idCol] as String] = entry[nickCol] as String;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
Future<void> fetchTracked() async {
|
||||
for (String userID in (await getAllPlayerToTrack())) {
|
||||
TetrioPlayer player = await fetchPlayer(userID);
|
||||
storeState(player);
|
||||
sleep(Durations.extralong4);
|
||||
TetraLeagueAlphaStream matches = await fetchTLStream(userID);
|
||||
saveTLMatchesFromStream(matches);
|
||||
sleep(Durations.extralong4);
|
||||
}
|
||||
}
|
||||
// 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);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
// ignore_for_file: curly_braces_in_flow_control_structures
|
||||
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:tetra_stats/gen/strings.g.dart';
|
||||
import 'package:tetra_stats/utils/numers_formats.dart';
|
||||
|
||||
final NumberFormat secs = NumberFormat("00.###", LocaleSettings.currentLocale.languageCode);
|
||||
final NumberFormat nonsecs = NumberFormat("00.###", LocaleSettings.currentLocale.languageCode);
|
||||
final NumberFormat fixedSecs = NumberFormat("00.000", LocaleSettings.currentLocale.languageCode);
|
||||
final NumberFormat nonsecs = NumberFormat("00", LocaleSettings.currentLocale.languageCode);
|
||||
final NumberFormat nonsecs3 = NumberFormat("000", LocaleSettings.currentLocale.languageCode);
|
||||
final NumberFormat _timeInSec = NumberFormat("#,###.###s.", LocaleSettings.currentLocale.languageCode);
|
||||
|
||||
/// Returns string, that represents time difference between [dateTime] and now
|
||||
|
@ -69,6 +73,10 @@ String get40lTime(int microseconds){
|
|||
return microseconds > 60000000 ? "${(microseconds/1000000/60).floor()}:${(secs.format(microseconds /1000000 % 60))}" : _timeInSec.format(microseconds / 1000000);
|
||||
}
|
||||
|
||||
String getMoreNormalTime(Duration time){
|
||||
return "${nonsecs.format(time.inMinutes)}:${(fixedSecs.format(time.inMilliseconds/1000%60))}";
|
||||
}
|
||||
|
||||
/// Readable [a] - [b], without sign
|
||||
String readableTimeDifference(Duration a, Duration b){
|
||||
Duration result = a - b;
|
||||
|
@ -77,5 +85,12 @@ String readableTimeDifference(Duration a, Duration b){
|
|||
}
|
||||
|
||||
String countdown(Duration difference){
|
||||
return "${difference.inDays}:${nonsecs.format(difference.inHours%24)}:${nonsecs.format(difference.inMinutes%60)}:${secs.format(difference.inSeconds%60)}";
|
||||
return "${difference.inDays}d ${nonsecs.format(difference.inHours%24)}h ${nonsecs.format(difference.inMinutes%60)}m ${secs.format(difference.inSeconds%60)}s";
|
||||
}
|
||||
|
||||
String playtime(Duration difference){
|
||||
if (difference.inHours > 0) {
|
||||
return "${intf.format(difference.inHours)}h ${nonsecs.format(difference.inMinutes%60)}m";
|
||||
} else if (difference.inMinutes > 0) return "${difference.inMinutes}m ${nonsecs.format(difference.inSeconds%60)}s";
|
||||
else return "${secs.format(difference.inMilliseconds/1000)}s";
|
||||
}
|
|
@ -22,6 +22,7 @@ import 'package:tetra_stats/utils/relative_timestamps.dart';
|
|||
import 'package:tetra_stats/utils/text_shadow.dart';
|
||||
import 'package:tetra_stats/views/singleplayer_record_view.dart';
|
||||
import 'package:tetra_stats/views/tl_match_view.dart' show TlMatchResultView;
|
||||
import 'package:tetra_stats/views/zenith_record_view.dart';
|
||||
import 'package:tetra_stats/widgets/finesse_thingy.dart';
|
||||
import 'package:tetra_stats/widgets/lineclears_thingy.dart';
|
||||
import 'package:tetra_stats/widgets/list_tile_trailing_stats.dart';
|
||||
|
@ -33,11 +34,13 @@ import 'package:tetra_stats/widgets/stat_sell_num.dart';
|
|||
import 'package:tetra_stats/widgets/text_timestamp.dart';
|
||||
import 'package:tetra_stats/widgets/tl_thingy.dart';
|
||||
import 'package:tetra_stats/widgets/user_thingy.dart';
|
||||
import 'package:tetra_stats/widgets/zenith_thingy.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
int _chartsIndex = 0;
|
||||
int _season = currentSeason-1;
|
||||
bool _gamesPlayedInsteadOfDateAndTime = false;
|
||||
late ZoomPanBehavior _zoomPanBehavior;
|
||||
bool _smooth = false;
|
||||
|
@ -63,7 +66,7 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
|
|||
Future<List> me = Future.delayed(const Duration(seconds: 60), () => [null, null, null, null, null, null]); // I love lists shut up
|
||||
TetrioPlayersLeaderboard? everyone;
|
||||
PlayerLeaderboardPosition? meAmongEveryone;
|
||||
TetraLeagueAlpha? rankAverages;
|
||||
TetraLeague? rankAverages;
|
||||
double? thatRankCutoff;
|
||||
double? nextRankCutoff;
|
||||
double? thatRankGlickoCutoff;
|
||||
|
@ -71,7 +74,8 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
|
|||
String _searchFor = "6098518e3d5155e6ec429cdc"; // who we looking for
|
||||
String _titleNickname = "";
|
||||
/// Each dropdown menu item contains list of dots for the graph
|
||||
List<DropdownMenuItem<List<_HistoryChartSpot>>> chartsData = [];
|
||||
/// chartsData[season-1][chart]
|
||||
List<List<DropdownMenuItem<List<_HistoryChartSpot>>>> chartsData = [];
|
||||
//var tableData = <TableRow>[];
|
||||
final bodyGlobalKey = GlobalKey();
|
||||
bool _showSearchBar = false;
|
||||
|
@ -79,6 +83,7 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
|
|||
bool _TLHistoryWasFetched = false;
|
||||
late TabController _tabController;
|
||||
late TabController _wideScreenTabController;
|
||||
bool zenithEX = false;
|
||||
|
||||
String get title => "Tetra Stats: $_titleNickname";
|
||||
|
||||
|
@ -86,8 +91,8 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
|
|||
void initState() {
|
||||
initDB();
|
||||
_scrollController = ScrollController();
|
||||
_tabController = TabController(length: 7, vsync: this);
|
||||
_wideScreenTabController = TabController(length: 4, vsync: this);
|
||||
_tabController = TabController(length: 9, vsync: this);
|
||||
_wideScreenTabController = TabController(length: 5, vsync: this);
|
||||
_zoomPanBehavior = ZoomPanBehavior(
|
||||
enablePinching: true,
|
||||
enableSelectionZooming: true,
|
||||
|
@ -154,76 +159,80 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
|
|||
if (!kIsWeb && !Platform.isAndroid && !Platform.isIOS) await windowManager.setTitle(title);
|
||||
|
||||
// Requesting Tetra League (alpha), records, news and top TR of player
|
||||
late List<dynamic> requests;
|
||||
late TetraLeagueAlphaStream tlStream;
|
||||
late UserRecords records;
|
||||
List<dynamic> requests;
|
||||
Summaries summaries = await teto.fetchSummaries(_searchFor);
|
||||
late TetraLeagueBetaStream tlStream;
|
||||
late News news;
|
||||
late SingleplayerStream recent;
|
||||
late SingleplayerStream sprint;
|
||||
late SingleplayerStream blitz;
|
||||
// late SingleplayerStream recentSprint;
|
||||
// late SingleplayerStream recentBlitz;
|
||||
// late SingleplayerStream sprint;
|
||||
// late SingleplayerStream blitz;
|
||||
late SingleplayerStream recentZenith;
|
||||
late SingleplayerStream recentZenithEX;
|
||||
late TetrioPlayerFromLeaderboard? topOne;
|
||||
late TopTr? topTR;
|
||||
requests = await Future.wait([ // all at once (7 requests to oskware lmao)
|
||||
// late TopTr? topTR;
|
||||
requests = await Future.wait([
|
||||
teto.fetchSummaries(_searchFor),
|
||||
teto.fetchTLStream(_searchFor),
|
||||
teto.fetchRecords(_searchFor),
|
||||
teto.fetchNews(_searchFor),
|
||||
teto.fetchSingleplayerStream(_searchFor, "any_userrecent"),
|
||||
teto.fetchSingleplayerStream(_searchFor, "40l_userbest"),
|
||||
teto.fetchSingleplayerStream(_searchFor, "blitz_userbest"),
|
||||
prefs.getBool("showPositions") != true ? teto.fetchCutoffs() : Future.delayed(Duration.zero, ()=><Map<String, double>>[]),
|
||||
(me.tlSeason1.rank != "z" ? me.tlSeason1.rank == "x" : me.tlSeason1.percentileRank == "x") ? teto.fetchTopOneFromTheLeaderboard() : Future.delayed(Duration.zero, ()=>null),
|
||||
(me.tlSeason1.gamesPlayed > 9) ? teto.fetchTopTR(_searchFor) : Future.delayed(Duration.zero, () => null) // can retrieve this only if player has TR
|
||||
teto.fetchStream(_searchFor, "zenith/recent"),
|
||||
teto.fetchStream(_searchFor, "zenithex/recent"),
|
||||
teto.fetchCutoffsBeanserver(),
|
||||
(summaries.league.rank != "z" ? summaries.league.rank == "x+" : summaries.league.percentileRank == "x+") ? teto.fetchTopOneFromTheLeaderboard() : Future.delayed(Duration.zero, ()=>null),
|
||||
]);
|
||||
tlStream = requests[0] as TetraLeagueAlphaStream;
|
||||
records = requests[1] as UserRecords;
|
||||
//prefs.getBool("showPositions") != true ? teto.fetchCutoffsBeanserver() : Future.delayed(Duration.zero, ()=><Map<String, double>>[]),
|
||||
|
||||
//(summaries.league.gamesPlayed > 9) ? teto.fetchTopTR(_searchFor) : Future.delayed(Duration.zero, () => null) // can retrieve this only if player has TR
|
||||
summaries = requests[0] as Summaries;
|
||||
tlStream = requests[1] as TetraLeagueBetaStream;
|
||||
// records = requests[1] as UserRecords;
|
||||
news = requests[2] as News;
|
||||
recent = requests[3] as SingleplayerStream;
|
||||
sprint = requests[4] as SingleplayerStream;
|
||||
blitz = requests[5] as SingleplayerStream;
|
||||
topOne = requests[7] as TetrioPlayerFromLeaderboard?;
|
||||
topTR = requests[8] as TopTr?; // No TR - no Top TR
|
||||
recentZenith = requests[3] as SingleplayerStream;
|
||||
recentZenithEX = requests[4] as SingleplayerStream;
|
||||
// recent = requests[3] as SingleplayerStream;
|
||||
// sprint = requests[4] as SingleplayerStream;
|
||||
// blitz = requests[5] as SingleplayerStream;
|
||||
topOne = requests[6] as TetrioPlayerFromLeaderboard?;
|
||||
// topTR = requests[8] as TopTr?; // No TR - no Top TR
|
||||
|
||||
meAmongEveryone = teto.getCachedLeaderboardPositions(me.userId);
|
||||
if (prefs.getBool("showPositions") == true){
|
||||
// Get tetra League leaderboard
|
||||
everyone = teto.getCachedLeaderboard();
|
||||
everyone ??= await teto.fetchTLLeaderboard();
|
||||
if (meAmongEveryone == null){
|
||||
meAmongEveryone = await compute(everyone!.getLeaderboardPosition, me);
|
||||
if (meAmongEveryone == null && everyone!.leaderboard.isNotEmpty){
|
||||
meAmongEveryone = await compute(everyone!.getLeaderboardPosition, {me.userId: summaries.league});
|
||||
if (meAmongEveryone != null) teto.cacheLeaderboardPositions(me.userId, meAmongEveryone!);
|
||||
}
|
||||
}
|
||||
Map<String, double>? cutoffs = prefs.getBool("showPositions") == true ? everyone!.cutoffs : (requests[6] as Cutoffs?)?.tr;
|
||||
Map<String, double>? cutoffsGlicko = prefs.getBool("showPositions") == true ? everyone!.cutoffsGlicko : (requests[6] as Cutoffs?)?.glicko;
|
||||
Map<String, double>? cutoffs = (requests[5] as Cutoffs?)?.tr;
|
||||
Map<String, double>? cutoffsGlicko = (requests[5] as Cutoffs?)?.glicko;
|
||||
|
||||
if (me.tlSeason1.gamesPlayed > 9) {
|
||||
thatRankCutoff = cutoffs?[me.tlSeason1.rank != "z" ? me.tlSeason1.rank : me.tlSeason1.percentileRank];
|
||||
thatRankGlickoCutoff = cutoffsGlicko?[me.tlSeason1.rank != "z" ? me.tlSeason1.rank : me.tlSeason1.percentileRank];
|
||||
nextRankCutoff = (me.tlSeason1.rank != "z" ? me.tlSeason1.rank == "x" : me.tlSeason1.percentileRank == "x") ? topOne?.rating??25000 : cutoffs?[ranks.elementAtOrNull(ranks.indexOf(me.tlSeason1.rank != "z" ? me.tlSeason1.rank : me.tlSeason1.percentileRank)+1)];
|
||||
nextRankGlickoCutoff = (me.tlSeason1.rank != "z" ? me.tlSeason1.rank == "x" : me.tlSeason1.percentileRank == "x") ? topOne?.glicko??double.infinity : cutoffsGlicko?[ranks.elementAtOrNull(ranks.indexOf(me.tlSeason1.rank != "z" ? me.tlSeason1.rank : me.tlSeason1.percentileRank)+1)];
|
||||
if (summaries.league.gamesPlayed > 9) {
|
||||
thatRankCutoff = cutoffs?[summaries.league.rank != "z" ? summaries.league.rank : summaries.league.percentileRank];
|
||||
thatRankGlickoCutoff = cutoffsGlicko?[summaries.league.rank != "z" ? summaries.league.rank : summaries.league.percentileRank];
|
||||
nextRankCutoff = (summaries.league.rank != "z" ? summaries.league.rank == "x+" : summaries.league.percentileRank == "x+") ? topOne?.tr??25000 : cutoffs?[ranks.elementAtOrNull(ranks.indexOf(summaries.league.rank != "z" ? summaries.league.rank : summaries.league.percentileRank)+1)];
|
||||
nextRankGlickoCutoff = (summaries.league.rank != "z" ? summaries.league.rank == "x+" : summaries.league.percentileRank == "x+") ? topOne?.glicko??double.infinity : cutoffsGlicko?[ranks.elementAtOrNull(ranks.indexOf(summaries.league.rank != "z" ? summaries.league.rank : summaries.league.percentileRank)+1)];
|
||||
}
|
||||
|
||||
if (everyone != null && me.tlSeason1.gamesPlayed > 9) rankAverages = everyone?.averages[me.tlSeason1.percentileRank]?[0];
|
||||
if (everyone != null && summaries.league.gamesPlayed > 9) rankAverages = everyone?.averages[summaries.league.percentileRank]?[0];
|
||||
|
||||
// Making list of Tetra League matches
|
||||
List<TetraLeagueAlphaRecord> tlMatches = [];
|
||||
bool isTracking = await teto.isPlayerTracking(me.userId);
|
||||
List<TetrioPlayer> states = [];
|
||||
TetraLeagueAlpha? compareWith;
|
||||
Set<TetraLeagueAlpha> uniqueTL = {};
|
||||
tlMatches = tlStream.records;
|
||||
List<List<TetraLeague>> states = await Future.wait<List<TetraLeague>>([
|
||||
teto.getStates(me.userId, season: 1), teto.getStates(me.userId, season: 2),
|
||||
]);
|
||||
List<TetraLeagueAlphaRecord> storedRecords = await teto.getTLMatchesbyPlayerID(me.userId); // get old matches
|
||||
if (isTracking){ // if tracked - save data to local DB
|
||||
await teto.storeState(me);
|
||||
await teto.saveTLMatchesFromStream(tlStream);
|
||||
await teto.storeState(summaries.league);
|
||||
//await teto.saveTLMatchesFromStream(tlStream);
|
||||
}
|
||||
|
||||
TetraLeagueAlphaStream? oldMatches;
|
||||
// building list of TL matches
|
||||
if(fetchTLmatches) {
|
||||
try{
|
||||
List<TetraLeagueAlphaRecord> oldMatches = await teto.fetchAndSaveOldTLmatches(_searchFor);
|
||||
storedRecords.addAll(oldMatches);
|
||||
if (context.mounted) ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.fetchAndSaveOldTLmatchesResult(number: oldMatches.length))));
|
||||
oldMatches = await teto.fetchAndSaveOldTLmatches(_searchFor);
|
||||
if (context.mounted) ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.fetchAndSaveOldTLmatchesResult(number: oldMatches.records.length))));
|
||||
}on TetrioHistoryNotExist{
|
||||
if (context.mounted) ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.errors.p1nkl0bst3rTLmatches)));
|
||||
}on P1nkl0bst3rForbidden {
|
||||
|
@ -236,17 +245,17 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
|
|||
_TLHistoryWasFetched = true;
|
||||
}
|
||||
}
|
||||
if (storedRecords.isNotEmpty) _TLHistoryWasFetched = true;
|
||||
for (var match in storedRecords) {
|
||||
// add stored match to list only if it missing from retrived ones
|
||||
if (!tlMatches.contains(match)) tlMatches.add(match);
|
||||
if (storedRecords.isNotEmpty) {
|
||||
_TLHistoryWasFetched = true;
|
||||
tlStream.addFromAlphaStream(storedRecords);
|
||||
}
|
||||
tlMatches.sort((a, b) { // Newest matches gonna be shown at the top of the list
|
||||
if(a.timestamp.isBefore(b.timestamp)) return 1;
|
||||
if(a.timestamp.isAtSameMomentAs(b.timestamp)) return 0;
|
||||
if(a.timestamp.isAfter(b.timestamp)) return -1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
// tlMatches.sort((a, b) { // Newest matches gonna be shown at the top of the list
|
||||
// if(a.ts.isBefore(b.ts)) return 1;
|
||||
// if(a.ts.isAtSameMomentAs(b.ts)) return 0;
|
||||
// if(a.ts.isAfter(b.ts)) return -1;
|
||||
// return 0;
|
||||
// });
|
||||
|
||||
// Handling history
|
||||
if(fetchHistory){
|
||||
|
@ -264,37 +273,38 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
|
|||
}
|
||||
}
|
||||
|
||||
states.addAll(await teto.getPlayer(me.userId));
|
||||
for (var element in states) { // For graphs I need only unique entries
|
||||
if (uniqueTL.isNotEmpty && uniqueTL.last != element.tlSeason1) uniqueTL.add(element.tlSeason1);
|
||||
if (uniqueTL.isEmpty) uniqueTL.add(element.tlSeason1);
|
||||
}
|
||||
//states.addAll(await teto.getPlayer(me.userId));
|
||||
// for (var element in states) { // For graphs I need only unique entries
|
||||
// if (element.tlSeason1 != null && uniqueTL.isNotEmpty && uniqueTL.last != element.tlSeason1) uniqueTL.add(element.tlSeason1!);
|
||||
// if (uniqueTL.isEmpty) uniqueTL.add(summaries.league);
|
||||
// }
|
||||
// Also i need previous Tetra League State for comparison if avaliable
|
||||
if (uniqueTL.length >= 2){
|
||||
compareWith = uniqueTL.toList().elementAtOrNull(uniqueTL.length - 2);
|
||||
chartsData = <DropdownMenuItem<List<_HistoryChartSpot>>>[ // Dumping charts data into dropdown menu items, while cheking if every entry is valid
|
||||
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.gamesPlayed > 9) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.rating)], child: Text(t.statCellNum.tr)),
|
||||
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.gamesPlayed > 9) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.glicko!)], child: const Text("Glicko")),
|
||||
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.gamesPlayed > 9) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.rd!)], child: const Text("Rating Deviation")),
|
||||
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.apm != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.apm!)], child: Text(t.statCellNum.apm.replaceAll(RegExp(r'\n'), " "))),
|
||||
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.pps != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.pps!)], child: Text(t.statCellNum.pps.replaceAll(RegExp(r'\n'), " "))),
|
||||
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.vs != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.vs!)], child: Text(t.statCellNum.vs.replaceAll(RegExp(r'\n'), " "))),
|
||||
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.app)], child: Text(t.statCellNum.app.replaceAll(RegExp(r'\n'), " "))),
|
||||
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.dss)], child: Text(t.statCellNum.dss.replaceAll(RegExp(r'\n'), " "))),
|
||||
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.dsp)], child: Text(t.statCellNum.dsp.replaceAll(RegExp(r'\n'), " "))),
|
||||
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.appdsp)], child: const Text("APP + DS/P")),
|
||||
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.vsapm)], child: const Text("VS/APM")),
|
||||
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.cheese)], child: Text(t.statCellNum.cheese.replaceAll(RegExp(r'\n'), " "))),
|
||||
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.gbe)], child: Text(t.statCellNum.gbe.replaceAll(RegExp(r'\n'), " "))),
|
||||
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.nyaapp)], child: Text(t.statCellNum.nyaapp.replaceAll(RegExp(r'\n'), " "))),
|
||||
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.area)], child: Text(t.statCellNum.area.replaceAll(RegExp(r'\n'), " "))),
|
||||
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.estTr != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.estTr!.esttr)], child: Text(t.statCellNum.estOfTR.replaceAll(RegExp(r'\n'), " "))),
|
||||
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.esttracc != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.esttracc!)], child: Text(t.statCellNum.accOfEst.replaceAll(RegExp(r'\n'), " "))),
|
||||
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.playstyle != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.playstyle!.opener)], child: const Text("Opener")),
|
||||
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.playstyle != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.playstyle!.plonk)], child: const Text("Plonk")),
|
||||
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.playstyle != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.playstyle!.infds)], child: const Text("Inf. DS")),
|
||||
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.playstyle != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.playstyle!.stride)], child: const Text("Stride")),
|
||||
];
|
||||
TetraLeague? compareWith;
|
||||
if (states[1].length >= 2 || states[0].length >= 2){
|
||||
compareWith = states[1].length >= 2 ? states[1].elementAtOrNull(states.length - 2) : null;
|
||||
chartsData = [for (List<TetraLeague> s in states) <DropdownMenuItem<List<_HistoryChartSpot>>>[ // Dumping charts data into dropdown menu items, while cheking if every entry is valid
|
||||
DropdownMenuItem(value: [for (var tl in s) if (tl.gamesPlayed > 9) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.tr)], child: Text(t.statCellNum.tr)),
|
||||
DropdownMenuItem(value: [for (var tl in s) if (tl.gamesPlayed > 9) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.glicko!)], child: const Text("Glicko")),
|
||||
DropdownMenuItem(value: [for (var tl in s) if (tl.gamesPlayed > 9) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.rd!)], child: const Text("Rating Deviation")),
|
||||
DropdownMenuItem(value: [for (var tl in s) if (tl.apm != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.apm!)], child: Text(t.statCellNum.apm.replaceAll(RegExp(r'\n'), " "))),
|
||||
DropdownMenuItem(value: [for (var tl in s) if (tl.pps != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.pps!)], child: Text(t.statCellNum.pps.replaceAll(RegExp(r'\n'), " "))),
|
||||
DropdownMenuItem(value: [for (var tl in s) if (tl.vs != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.vs!)], child: Text(t.statCellNum.vs.replaceAll(RegExp(r'\n'), " "))),
|
||||
DropdownMenuItem(value: [for (var tl in s) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.app)], child: Text(t.statCellNum.app.replaceAll(RegExp(r'\n'), " "))),
|
||||
DropdownMenuItem(value: [for (var tl in s) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.dss)], child: Text(t.statCellNum.dss.replaceAll(RegExp(r'\n'), " "))),
|
||||
DropdownMenuItem(value: [for (var tl in s) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.dsp)], child: Text(t.statCellNum.dsp.replaceAll(RegExp(r'\n'), " "))),
|
||||
DropdownMenuItem(value: [for (var tl in s) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.appdsp)], child: const Text("APP + DS/P")),
|
||||
DropdownMenuItem(value: [for (var tl in s) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.vsapm)], child: const Text("VS/APM")),
|
||||
DropdownMenuItem(value: [for (var tl in s) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.cheese)], child: Text(t.statCellNum.cheese.replaceAll(RegExp(r'\n'), " "))),
|
||||
DropdownMenuItem(value: [for (var tl in s) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.gbe)], child: Text(t.statCellNum.gbe.replaceAll(RegExp(r'\n'), " "))),
|
||||
DropdownMenuItem(value: [for (var tl in s) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.nyaapp)], child: Text(t.statCellNum.nyaapp.replaceAll(RegExp(r'\n'), " "))),
|
||||
DropdownMenuItem(value: [for (var tl in s) if (tl.nerdStats != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.nerdStats!.area)], child: Text(t.statCellNum.area.replaceAll(RegExp(r'\n'), " "))),
|
||||
DropdownMenuItem(value: [for (var tl in s) if (tl.estTr != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.estTr!.esttr)], child: Text(t.statCellNum.estOfTR.replaceAll(RegExp(r'\n'), " "))),
|
||||
DropdownMenuItem(value: [for (var tl in s) if (tl.esttracc != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.esttracc!)], child: Text(t.statCellNum.accOfEst.replaceAll(RegExp(r'\n'), " "))),
|
||||
DropdownMenuItem(value: [for (var tl in s) if (tl.playstyle != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.playstyle!.opener)], child: const Text("Opener")),
|
||||
DropdownMenuItem(value: [for (var tl in s) if (tl.playstyle != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.playstyle!.plonk)], child: const Text("Plonk")),
|
||||
DropdownMenuItem(value: [for (var tl in s) if (tl.playstyle != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.playstyle!.infds)], child: const Text("Inf. DS")),
|
||||
DropdownMenuItem(value: [for (var tl in s) if (tl.playstyle != null) _HistoryChartSpot(tl.timestamp, tl.gamesPlayed, tl.rank, tl.playstyle!.stride)], child: const Text("Stride")),
|
||||
]];
|
||||
}else{
|
||||
compareWith = null;
|
||||
chartsData = [];
|
||||
|
@ -305,8 +315,8 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
|
|||
changePlayer(me.userId);
|
||||
});
|
||||
}
|
||||
|
||||
return [me, records, states, tlMatches, compareWith, isTracking, news, topTR, recent, sprint, blitz, tlMatches.elementAtOrNull(0)?.timestamp];
|
||||
return [me, summaries, news, tlStream, recentZenith, recentZenithEX, states[currentSeason-1]];
|
||||
//return [me, records, states, tlMatches, compareWith, isTracking, news, topTR, recent, sprint, blitz, tlMatches.elementAtOrNull(0)?.timestamp];
|
||||
}
|
||||
|
||||
/// Triggers widgets rebuild
|
||||
|
@ -314,6 +324,10 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
|
|||
setState(() {});
|
||||
}
|
||||
|
||||
void toggleZenith(){
|
||||
setState(() {zenithEX = !zenithEX;});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final t = Translations.of(context);
|
||||
|
@ -430,12 +444,15 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
|
|||
tabs: bigScreen ? [
|
||||
Tab(text: t.tetraLeague,),
|
||||
Tab(text: t.history),
|
||||
Tab(text: t.quickPlay),
|
||||
Tab(text: "${t.sprint} & ${t.blitz}"),
|
||||
Tab(text: t.other),
|
||||
] : [
|
||||
Tab(text: t.tetraLeague),
|
||||
Tab(text: t.tlRecords),
|
||||
Tab(text: t.history),
|
||||
Tab(text: t.quickPlay),
|
||||
Tab(text: "${t.quickPlay} ${t.recent}"),
|
||||
Tab(text: t.sprint),
|
||||
Tab(text: t.blitz),
|
||||
Tab(text: t.recentRuns),
|
||||
|
@ -455,54 +472,71 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
|
|||
width: MediaQuery.of(context).size.width-450,
|
||||
constraints: const BoxConstraints(maxWidth: 1024),
|
||||
child: TLThingy(
|
||||
tl: snapshot.data![0].tlSeason1,
|
||||
tl: snapshot.data![1].league,
|
||||
userID: snapshot.data![0].userId,
|
||||
states: snapshot.data![2],
|
||||
topTR: snapshot.data![7]?.tr,
|
||||
lastMatchPlayed: snapshot.data![11],
|
||||
states: snapshot.data![6],
|
||||
//topTR: snapshot.data![7]?.tr,
|
||||
//lastMatchPlayed: snapshot.data![11],
|
||||
bot: snapshot.data![0].role == "bot",
|
||||
guest: snapshot.data![0].role == "anon",
|
||||
thatRankCutoff: thatRankCutoff,
|
||||
thatRankCutoffGlicko: thatRankGlickoCutoff,
|
||||
thatRankTarget: snapshot.data![0].tlSeason1.rank != "z" ? rankTargets[snapshot.data![0].tlSeason1.rank] : null,
|
||||
thatRankTarget: snapshot.data![1].league.rank != "z" ? rankTargets[snapshot.data![1].league.rank] : null,
|
||||
nextRankCutoff: nextRankCutoff,
|
||||
nextRankCutoffGlicko: nextRankGlickoCutoff,
|
||||
nextRankTarget: (snapshot.data![0].tlSeason1.rank != "z" && snapshot.data![0].tlSeason1.rank != "x") ? rankTargets[ranks.elementAtOrNull(ranks.indexOf(snapshot.data![0].tlSeason1.rank)+1)] : null,
|
||||
nextRankTarget: (snapshot.data![1].league.rank != "z" && snapshot.data![1].league.rank != "x") ? rankTargets[ranks.elementAtOrNull(ranks.indexOf(snapshot.data![1].league.rank)+1)] : null,
|
||||
averages: rankAverages,
|
||||
lbPositions: meAmongEveryone
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 450,
|
||||
child: _TLRecords(userID: snapshot.data![0].userId, changePlayer: changePlayer, data: snapshot.data![3], wasActiveInTL: snapshot.data![0].tlSeason1.gamesPlayed > 0, oldMathcesHere: _TLHistoryWasFetched, separateScrollController: true,)
|
||||
child: _TLRecords(userID: snapshot.data![0].userId, changePlayer: changePlayer, data: snapshot.data![3].records, wasActiveInTL: true, oldMathcesHere: _TLHistoryWasFetched, separateScrollController: true)
|
||||
),
|
||||
],),
|
||||
_History(chartsData: chartsData, changePlayer: changePlayer, userID: _searchFor, update: _justUpdate, wasActiveInTL: snapshot.data![0].tlSeason1.gamesPlayed > 0),
|
||||
_TwoRecordsThingy(sprint: snapshot.data![1].sprint, blitz: snapshot.data![1].blitz, rank: snapshot.data![0].tlSeason1.percentileRank, recent: snapshot.data![8], sprintStream: snapshot.data![9], blitzStream: snapshot.data![10]),
|
||||
_OtherThingy(zen: snapshot.data![1].zen, bio: snapshot.data![0].bio, distinguishment: snapshot.data![0].distinguishment, newsletter: snapshot.data![6],)
|
||||
_History(chartsData: chartsData, changePlayer: changePlayer, userID: _searchFor, update: _justUpdate, wasActiveInTL: snapshot.data![1].league.gamesPlayed > 0),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
width: MediaQuery.of(context).size.width-450,
|
||||
constraints: const BoxConstraints(maxWidth: 1024),
|
||||
child: SingleChildScrollView(child: ZenithThingy(record: snapshot.data![1].zenith, recordEX: snapshot.data![1].zenithEx, parentZenithToggle: toggleZenith, initEXvalue: zenithEX))
|
||||
),
|
||||
SizedBox(
|
||||
width: 450.0,
|
||||
child: _ZenithRecords(userID: snapshot.data![0].userId, data: snapshot.data![zenithEX ? 5 : 4], separateScrollController: true),
|
||||
)
|
||||
],
|
||||
),
|
||||
_TwoRecordsThingy(sprint: snapshot.data![1].sprint, blitz: snapshot.data![1].blitz, rank: snapshot.data![1].league.percentileRank, recent: SingleplayerStream(userId: "userId", records: [], type: "recent"), sprintStream: SingleplayerStream(userId: "userId", records: [], type: "40l"), blitzStream: SingleplayerStream(userId: "userId", records: [], type: "blitz")),
|
||||
_OtherThingy(zen: snapshot.data![1].zen, bio: snapshot.data![0].bio, distinguishment: snapshot.data![0].distinguishment, newsletter: snapshot.data![2])
|
||||
] : [
|
||||
TLThingy(
|
||||
tl: snapshot.data![0].tlSeason1,
|
||||
tl: snapshot.data![1].league,
|
||||
userID: snapshot.data![0].userId,
|
||||
states: snapshot.data![2],
|
||||
topTR: snapshot.data![7]?.tr,
|
||||
states: const [], //snapshot.data![2],
|
||||
//topTR: snapshot.data![7]?.tr,
|
||||
//lastMatchPlayed: snapshot.data![11],
|
||||
bot: snapshot.data![0].role == "bot",
|
||||
guest: snapshot.data![0].role == "anon",
|
||||
thatRankCutoff: thatRankCutoff,
|
||||
thatRankCutoffGlicko: thatRankGlickoCutoff,
|
||||
thatRankTarget: snapshot.data![0].tlSeason1.rank != "z" ? rankTargets[snapshot.data![0].tlSeason1.rank] : null,
|
||||
thatRankTarget: snapshot.data![1].league.rank != "z" ? rankTargets[snapshot.data![1].league.rank] : null,
|
||||
nextRankCutoff: nextRankCutoff,
|
||||
nextRankCutoffGlicko: nextRankGlickoCutoff,
|
||||
nextRankTarget: (snapshot.data![0].tlSeason1.rank != "z" && snapshot.data![0].tlSeason1.rank != "x") ? rankTargets[ranks.elementAtOrNull(ranks.indexOf(snapshot.data![0].tlSeason1.rank)+1)] : null,
|
||||
nextRankTarget: (snapshot.data![1].league.rank != "z" && snapshot.data![1].league.rank != "x") ? rankTargets[ranks.elementAtOrNull(ranks.indexOf(snapshot.data![1].league.rank)+1)] : null,
|
||||
averages: rankAverages,
|
||||
lbPositions: meAmongEveryone
|
||||
),
|
||||
_TLRecords(userID: snapshot.data![0].userId, changePlayer: changePlayer, data: snapshot.data![3], wasActiveInTL: snapshot.data![0].tlSeason1.gamesPlayed > 0, oldMathcesHere: _TLHistoryWasFetched),
|
||||
_History(chartsData: chartsData, changePlayer: changePlayer, userID: _searchFor, update: _justUpdate, wasActiveInTL: snapshot.data![0].tlSeason1.gamesPlayed > 0),
|
||||
SingleplayerRecord(record: snapshot.data![1].sprint, rank: snapshot.data![0].tlSeason1.percentileRank, stream: snapshot.data![9]),
|
||||
SingleplayerRecord(record: snapshot.data![1].blitz, rank: snapshot.data![0].tlSeason1.percentileRank, stream: snapshot.data![10]),
|
||||
_RecentSingleplayersThingy(snapshot.data![8]),
|
||||
_OtherThingy(zen: snapshot.data![1].zen, bio: snapshot.data![0].bio, distinguishment: snapshot.data![0].distinguishment, newsletter: snapshot.data![6])
|
||||
_TLRecords(userID: snapshot.data![0].userId, changePlayer: changePlayer, data: snapshot.data![3].records, wasActiveInTL: true, oldMathcesHere: _TLHistoryWasFetched, separateScrollController: true),
|
||||
_History(chartsData: chartsData, changePlayer: changePlayer, userID: _searchFor, update: _justUpdate, wasActiveInTL: snapshot.data![1].league.gamesPlayed > 0),
|
||||
SingleChildScrollView(child: ZenithThingy(record: snapshot.data![1].zenith, recordEX: snapshot.data![1].zenithEx, parentZenithToggle: toggleZenith, initEXvalue: zenithEX)),
|
||||
_ZenithRecords(userID: snapshot.data![0].userId, data: snapshot.data![zenithEX ? 5 : 4], separateScrollController: true),
|
||||
SingleplayerRecord(record: snapshot.data![1].sprint, rank: snapshot.data![1].league.percentileRank, stream: SingleplayerStream(userId: "userId", records: [], type: "40l")),
|
||||
SingleplayerRecord(record: snapshot.data![1].blitz, rank: snapshot.data![1].league.percentileRank, stream: SingleplayerStream(userId: "userId", records: [], type: "Blitz")),
|
||||
_RecentSingleplayersThingy(SingleplayerStream(userId: "userId", records: [], type: "recent")),
|
||||
_OtherThingy(zen: snapshot.data![1].zen, bio: snapshot.data![0].bio, distinguishment: snapshot.data![0].distinguishment, newsletter: snapshot.data![2])
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -543,6 +577,7 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
|
|||
break;
|
||||
default:
|
||||
errText = snapshot.error.toString();
|
||||
subText = snapshot.stackTrace.toString();
|
||||
}
|
||||
return Center(child:
|
||||
Column(
|
||||
|
@ -693,7 +728,7 @@ class _NavDrawerState extends State<NavDrawer> {
|
|||
class _TLRecords extends StatelessWidget {
|
||||
final String userID;
|
||||
final Function changePlayer;
|
||||
final List<TetraLeagueAlphaRecord> data;
|
||||
final List<BetaRecord> data;
|
||||
final bool wasActiveInTL;
|
||||
final bool oldMathcesHere;
|
||||
final bool separateScrollController;
|
||||
|
@ -732,7 +767,7 @@ class _TLRecords extends StatelessWidget {
|
|||
));
|
||||
}
|
||||
|
||||
var accentColor = data[index].endContext.firstWhere((element) => element.userId == userID).success ? Colors.green : Colors.red;
|
||||
var accentColor = data[index].results.leaderboard.firstWhere((element) => element.id == userID).wins > data[index].results.leaderboard.firstWhere((element) => element.id != userID).wins ? Colors.green : Colors.red;
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
|
@ -741,19 +776,76 @@ class _TLRecords extends StatelessWidget {
|
|||
)
|
||||
),
|
||||
child: ListTile(
|
||||
leading: Text("${data[index].endContext.firstWhere((element) => element.userId == userID).points} : ${data[index].endContext.firstWhere((element) => element.userId != userID).points}",
|
||||
leading: Text("${data[index].results.leaderboard.firstWhere((element) => element.id == userID).wins} : ${data[index].results.leaderboard.firstWhere((element) => element.id != userID).wins}",
|
||||
style: bigScreen ? const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28, shadows: textShadow) : const TextStyle(fontSize: 28, shadows: textShadow)),
|
||||
title: Text("vs. ${data[index].endContext.firstWhere((element) => element.userId != userID).username}"),
|
||||
subtitle: Text(timestamp(data[index].timestamp), style: const TextStyle(color: Colors.grey)),
|
||||
title: Text("vs. ${data[index].results.leaderboard.firstWhere((element) => element.id != userID).username}"),
|
||||
subtitle: Text(timestamp(data[index].ts), style: const TextStyle(color: Colors.grey)),
|
||||
trailing: TrailingStats(
|
||||
data[index].endContext.firstWhere((element) => element.userId == userID).secondary,
|
||||
data[index].endContext.firstWhere((element) => element.userId == userID).tertiary,
|
||||
data[index].endContext.firstWhere((element) => element.userId == userID).extra,
|
||||
data[index].endContext.firstWhere((element) => element.userId != userID).secondary,
|
||||
data[index].endContext.firstWhere((element) => element.userId != userID).tertiary,
|
||||
data[index].endContext.firstWhere((element) => element.userId != userID).extra
|
||||
data[index].results.leaderboard.firstWhere((element) => element.id == userID).stats.apm,
|
||||
data[index].results.leaderboard.firstWhere((element) => element.id == userID).stats.pps,
|
||||
data[index].results.leaderboard.firstWhere((element) => element.id == userID).stats.vs,
|
||||
data[index].results.leaderboard.firstWhere((element) => element.id != userID).stats.apm,
|
||||
data[index].results.leaderboard.firstWhere((element) => element.id != userID).stats.pps,
|
||||
data[index].results.leaderboard.firstWhere((element) => element.id != userID).stats.vs,
|
||||
),
|
||||
onTap: () => Navigator.push(context, MaterialPageRoute(builder: (context) => TlMatchResultView(record: data[index], initPlayerId: userID))),
|
||||
onTap: () => Navigator.push(context, MaterialPageRoute(builder: (context) => TlMatchResultView(record: data[index], initPlayerId: userID))) //Navigator.push(context, MaterialPageRoute(builder: (context) => TlMatchResultView(record: data[index], initPlayerId: userID))),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class _ZenithRecords extends StatelessWidget {
|
||||
final String userID;
|
||||
final SingleplayerStream data;
|
||||
final bool separateScrollController;
|
||||
|
||||
/// Widget, that displays Quick Play records.
|
||||
/// Accepts list of TL records ([data]) and [userID] of player from the view
|
||||
const _ZenithRecords({required this.userID, required this.data, this.separateScrollController = false});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (data.records.isEmpty) {
|
||||
return Center(child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(t.noRecords, style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28)),
|
||||
],
|
||||
));
|
||||
}
|
||||
bool bigScreen = MediaQuery.of(context).size.width >= 768;
|
||||
int length = data.records.length;
|
||||
return ListView.builder(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
controller: separateScrollController ? ScrollController() : null,
|
||||
itemCount: length + 1,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
if (index == length) {
|
||||
return Center(child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(t.noOldRecords(n: length), style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28)),
|
||||
],
|
||||
));
|
||||
}
|
||||
const TextStyle style = TextStyle(height: 1.1, fontWeight: FontWeight.w100, fontSize: 13);
|
||||
return Container(
|
||||
child: ListTile(
|
||||
leading: Text("QP",
|
||||
style: bigScreen ? const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28, shadows: textShadow) : const TextStyle(fontSize: 28, shadows: textShadow)),
|
||||
title: Text("${f2.format(data.records[index].stats.zenith!.altitude)} m${(data.records[index].extras as ZenithExtras).mods.isNotEmpty ? " (${t.withModsPlural(n: (data.records[index].extras as ZenithExtras).mods.length)})" : ""}"),
|
||||
subtitle: Text(timestamp(data.records[index].timestamp), style: const TextStyle(color: Colors.grey)),
|
||||
trailing: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Text("${f2.format(data.records[index].aggregateStats.apm)} APM, ${f2.format(data.records[index].aggregateStats.pps)} PPS", style: style, textAlign: TextAlign.right),
|
||||
Text("${f2.format(data.records[index].stats.cps)} CSP (${f2.format(data.records[index].stats.zenith!.peakrank)} peak)", style: style, textAlign: TextAlign.right),
|
||||
Text("${data.records[index].stats.kills} KO's, ${getMoreNormalTime(data.records[index].stats.finalTime)}", style: style, textAlign: TextAlign.right)
|
||||
],
|
||||
),
|
||||
onTap: () => Navigator.push(context, MaterialPageRoute(builder: (context) => ZenithRecordView(record: data.records[index]))) //Navigator.push(context, MaterialPageRoute(builder: (context) => TlMatchResultView(record: data[index], initPlayerId: userID))),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
@ -761,7 +853,7 @@ class _TLRecords extends StatelessWidget {
|
|||
}
|
||||
|
||||
class _History extends StatelessWidget{
|
||||
final List<DropdownMenuItem<List<_HistoryChartSpot>>> chartsData;
|
||||
final List<List<DropdownMenuItem<List<_HistoryChartSpot>>>> chartsData;
|
||||
final String userID;
|
||||
final Function update;
|
||||
final Function changePlayer;
|
||||
|
@ -784,8 +876,7 @@ class _History extends StatelessWidget{
|
|||
));
|
||||
}
|
||||
bool bigScreen = MediaQuery.of(context).size.width > 768;
|
||||
//List<_HistoryChartSpot> selectedGraph = _gamesPlayedInsteadOfDateAndTime ? chartsDataGamesPlayed[_chartsIndex].value! : chartsData[_chartsIndex].value!;
|
||||
List<_HistoryChartSpot> selectedGraph = chartsData[_chartsIndex].value!;
|
||||
List<_HistoryChartSpot> selectedGraph = chartsData[_season][_chartsIndex].value!;
|
||||
return SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: Column(
|
||||
|
@ -795,6 +886,20 @@ class _History extends StatelessWidget{
|
|||
spacing: 20,
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Padding(padding: EdgeInsets.all(8.0), child: Text("Season:", style: TextStyle(fontSize: 22))),
|
||||
DropdownButton(
|
||||
items: [for (int i = 1; i <= currentSeason; i++) DropdownMenuItem(value: i-1, child: Text("$i"))],
|
||||
value: _season,
|
||||
onChanged: (value) {
|
||||
_season = value!;
|
||||
update();
|
||||
}
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
|
@ -814,10 +919,10 @@ class _History extends StatelessWidget{
|
|||
children: [
|
||||
const Padding(padding: EdgeInsets.all(8.0), child: Text("Y:", style: TextStyle(fontSize: 22))),
|
||||
DropdownButton(
|
||||
items: chartsData,
|
||||
value: chartsData[_chartsIndex].value,
|
||||
items: chartsData[_season],
|
||||
value: chartsData[_season][_chartsIndex].value,
|
||||
onChanged: (value) {
|
||||
_chartsIndex = chartsData.indexWhere((element) => element.value == value);
|
||||
_chartsIndex = chartsData[_season].indexWhere((element) => element.value == value);
|
||||
update();
|
||||
}
|
||||
),
|
||||
|
@ -838,13 +943,13 @@ class _History extends StatelessWidget{
|
|||
IconButton(onPressed: () => _zoomPanBehavior.reset(), icon: const Icon(Icons.refresh), alignment: Alignment.center,)
|
||||
],
|
||||
),
|
||||
if(chartsData[_chartsIndex].value!.length > 1) _HistoryChartThigy(data: selectedGraph, smooth: _smooth, yAxisTitle: _historyShortTitles[_chartsIndex], bigScreen: bigScreen, leftSpace: bigScreen? 80 : 45, yFormat: bigScreen? f2 : NumberFormat.compact(), xFormat: NumberFormat.compact())
|
||||
else if (chartsData[_chartsIndex].value!.length <= 1) Center(child: Column(
|
||||
if(chartsData[_season][_chartsIndex].value!.length > 1) _HistoryChartThigy(data: selectedGraph, smooth: _smooth, yAxisTitle: _historyShortTitles[_chartsIndex], bigScreen: bigScreen, leftSpace: bigScreen? 80 : 45, yFormat: bigScreen? f2 : NumberFormat.compact(), xFormat: NumberFormat.compact())
|
||||
else if (chartsData[_season][_chartsIndex].value!.length <= 1) Center(child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(t.notEnoughData, style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28)),
|
||||
if (wasActiveInTL) Text(t.errors.actionSuggestion),
|
||||
if (wasActiveInTL) TextButton(onPressed: (){changePlayer(userID, fetchHistory: true);}, child: Text(t.fetchAndsaveTLHistory))
|
||||
if (wasActiveInTL && _season == 0) Text(t.errors.actionSuggestion),
|
||||
if (wasActiveInTL && _season == 0) TextButton(onPressed: (){changePlayer(userID, fetchHistory: true);}, child: Text(t.fetchAndsaveTLHistory))
|
||||
],
|
||||
))
|
||||
],
|
||||
|
@ -1015,17 +1120,17 @@ class _TwoRecordsThingy extends StatelessWidget {
|
|||
Widget build(BuildContext context) {
|
||||
late MapEntry closestAverageBlitz;
|
||||
late bool blitzBetterThanClosestAverage;
|
||||
bool? blitzBetterThanRankAverage = (rank != null && rank != "z" && blitz != null) ? blitz!.endContext.score > blitzAverages[rank]! : null;
|
||||
bool? blitzBetterThanRankAverage = (rank != null && rank != "z" && rank != "x+" && blitz != null) ? blitz!.stats.score > blitzAverages[rank]! : null;
|
||||
late MapEntry closestAverageSprint;
|
||||
late bool sprintBetterThanClosestAverage;
|
||||
bool? sprintBetterThanRankAverage = (rank != null && rank != "z" && sprint != null) ? sprint!.endContext.finalTime < sprintAverages[rank]! : null;
|
||||
bool? sprintBetterThanRankAverage = (rank != null && rank != "z" && rank != "x+" && sprint != null) ? sprint!.stats.finalTime < sprintAverages[rank]! : null;
|
||||
if (sprint != null) {
|
||||
closestAverageSprint = sprintAverages.entries.singleWhere((element) => element.value == sprintAverages.values.reduce((a, b) => (a-sprint!.endContext.finalTime).abs() < (b -sprint!.endContext.finalTime).abs() ? a : b));
|
||||
sprintBetterThanClosestAverage = sprint!.endContext.finalTime < closestAverageSprint.value;
|
||||
closestAverageSprint = sprintAverages.entries.singleWhere((element) => element.value == sprintAverages.values.reduce((a, b) => (a-sprint!.stats.finalTime).abs() < (b -sprint!.stats.finalTime).abs() ? a : b));
|
||||
sprintBetterThanClosestAverage = sprint!.stats.finalTime < closestAverageSprint.value;
|
||||
}
|
||||
if (blitz != null){
|
||||
closestAverageBlitz = blitzAverages.entries.singleWhere((element) => element.value == blitzAverages.values.reduce((a, b) => (a-blitz!.endContext.score).abs() < (b -blitz!.endContext.score).abs() ? a : b));
|
||||
blitzBetterThanClosestAverage = blitz!.endContext.score > closestAverageBlitz.value;
|
||||
closestAverageBlitz = blitzAverages.entries.singleWhere((element) => element.value == blitzAverages.values.reduce((a, b) => (a-blitz!.stats.score).abs() < (b -blitz!.stats.score).abs() ? a : b));
|
||||
blitzBetterThanClosestAverage = blitz!.stats.score > closestAverageBlitz.value;
|
||||
}
|
||||
return SingleChildScrollView(child: Padding(
|
||||
padding: const EdgeInsets.only(top: 20.0),
|
||||
|
@ -1047,23 +1152,23 @@ class _TwoRecordsThingy extends StatelessWidget {
|
|||
children: [
|
||||
Text(t.sprint, style: const TextStyle(height: 0.1, fontFamily: "Eurostile Round Extended", fontSize: 18)),
|
||||
RichText(text: TextSpan(
|
||||
text: sprint != null ? get40lTime(sprint!.endContext.finalTime.inMicroseconds) : "---",
|
||||
text: sprint != null ? get40lTime(sprint!.stats.finalTime.inMicroseconds) : "---",
|
||||
style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 36, fontWeight: FontWeight.w500, color: sprint != null ? Colors.white : Colors.grey),
|
||||
//children: [TextSpan(text: get40lTime(record!.endContext.finalTime.inMicroseconds), style: TextStyle(fontFamily: "Eurostile Round", fontSize: 14, fontWeight: FontWeight.w100))]
|
||||
//children: [TextSpan(text: get40lTime(record!.stats.finalTime.inMicroseconds), style: TextStyle(fontFamily: "Eurostile Round", fontSize: 14, fontWeight: FontWeight.w100))]
|
||||
),
|
||||
),
|
||||
if (sprint != null) RichText(text: TextSpan(
|
||||
text: "",
|
||||
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, color: Colors.grey),
|
||||
children: [
|
||||
if (rank != null && rank != "z") TextSpan(text: "${t.verdictGeneral(n: readableTimeDifference(sprint!.endContext.finalTime, sprintAverages[rank]!), verdict: sprintBetterThanRankAverage??false ? t.verdictBetter : t.verdictWorse, rank: rank!.toUpperCase())}\n", style: TextStyle(
|
||||
if (rank != null && rank != "z" && rank != "x+") TextSpan(text: "${t.verdictGeneral(n: readableTimeDifference(sprint!.stats.finalTime, sprintAverages[rank]!), verdict: sprintBetterThanRankAverage??false ? t.verdictBetter : t.verdictWorse, rank: rank!.toUpperCase())}\n", style: TextStyle(
|
||||
color: sprintBetterThanRankAverage??false ? Colors.greenAccent : Colors.redAccent
|
||||
))
|
||||
else TextSpan(text: "${t.verdictGeneral(n: readableTimeDifference(sprint!.endContext.finalTime, closestAverageSprint.value), verdict: sprintBetterThanClosestAverage ? t.verdictBetter : t.verdictWorse, rank: closestAverageSprint.key.toUpperCase())}\n", style: TextStyle(
|
||||
else TextSpan(text: "${t.verdictGeneral(n: readableTimeDifference(sprint!.stats.finalTime, closestAverageSprint.value), verdict: sprintBetterThanClosestAverage ? t.verdictBetter : t.verdictWorse, rank: closestAverageSprint.key.toUpperCase())}\n", style: TextStyle(
|
||||
color: sprintBetterThanClosestAverage ? Colors.greenAccent : Colors.redAccent
|
||||
)),
|
||||
if (sprint!.rank != null) TextSpan(text: "№${sprint!.rank}", style: TextStyle(color: getColorOfRank(sprint!.rank!))),
|
||||
if (sprint!.rank != null) const TextSpan(text: " • "),
|
||||
TextSpan(text: "№${sprint!.rank}", style: TextStyle(color: getColorOfRank(sprint!.rank))),
|
||||
const TextSpan(text: " • "),
|
||||
TextSpan(text: timestamp(sprint!.timestamp)),
|
||||
]
|
||||
),
|
||||
|
@ -1076,14 +1181,14 @@ class _TwoRecordsThingy extends StatelessWidget {
|
|||
alignment: WrapAlignment.spaceBetween,
|
||||
spacing: 20,
|
||||
children: [
|
||||
StatCellNum(playerStat: sprint!.endContext.piecesPlaced, playerStatLabel: t.statCellNum.pieces, isScreenBig: true, higherIsBetter: true, smallDecimal: false),
|
||||
StatCellNum(playerStat: sprint!.endContext.pps, playerStatLabel: t.statCellNum.pps, fractionDigits: 2, isScreenBig: true, higherIsBetter: true, smallDecimal: false),
|
||||
StatCellNum(playerStat: sprint!.endContext.kpp, playerStatLabel: t.statCellNum.kpp, fractionDigits: 2, isScreenBig: true, higherIsBetter: true, smallDecimal: false),
|
||||
StatCellNum(playerStat: sprint!.stats.piecesPlaced, playerStatLabel: t.statCellNum.pieces, isScreenBig: true, higherIsBetter: true, smallDecimal: false),
|
||||
StatCellNum(playerStat: sprint!.stats.pps, playerStatLabel: t.statCellNum.pps, fractionDigits: 2, isScreenBig: true, higherIsBetter: true, smallDecimal: false),
|
||||
StatCellNum(playerStat: sprint!.stats.kpp, playerStatLabel: t.statCellNum.kpp, fractionDigits: 2, isScreenBig: true, higherIsBetter: true, smallDecimal: false),
|
||||
],
|
||||
),
|
||||
if (sprint != null) FinesseThingy(sprint?.endContext.finesse, sprint?.endContext.finessePercentage),
|
||||
if (sprint != null) LineclearsThingy(sprint!.endContext.clears, sprint!.endContext.lines, sprint!.endContext.holds, sprint!.endContext.tSpins),
|
||||
if (sprint != null) Text("${sprint!.endContext.inputs} KP • ${f2.format(sprint!.endContext.kps)} KPS"),
|
||||
if (sprint != null) FinesseThingy(sprint?.stats.finesse, sprint?.stats.finessePercentage),
|
||||
if (sprint != null) LineclearsThingy(sprint!.stats.clears, sprint!.stats.lines, sprint!.stats.holds, sprint!.stats.tSpins),
|
||||
if (sprint != null) Text("${sprint!.stats.inputs} KP • ${f2.format(sprint!.stats.kps)} KPS"),
|
||||
if (sprint != null) Wrap(
|
||||
alignment: WrapAlignment.spaceBetween,
|
||||
crossAxisAlignment: WrapCrossAlignment.start,
|
||||
|
@ -1101,10 +1206,10 @@ class _TwoRecordsThingy extends StatelessWidget {
|
|||
for (int i = 1; i < sprintStream.records.length; i++) ListTile(
|
||||
onTap: () => Navigator.push(context, MaterialPageRoute(builder: (context) => SingleplayerRecordView(record: sprintStream.records[i]))),
|
||||
leading: Text("#${i+1}", style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28, shadows: textShadow, height: 0.9) ),
|
||||
title: Text(get40lTime(sprintStream.records[i].endContext.finalTime.inMicroseconds),
|
||||
title: Text(get40lTime(sprintStream.records[i].stats.finalTime.inMicroseconds),
|
||||
style: const TextStyle(fontSize: 18)),
|
||||
subtitle: Text(timestamp(sprintStream.records[i].timestamp), style: const TextStyle(color: Colors.grey, height: 0.85)),
|
||||
trailing: SpTrailingStats(sprintStream.records[i].endContext)
|
||||
trailing: SpTrailingStats(sprintStream.records[i], sprintStream.records[i].gamemode)
|
||||
)
|
||||
],
|
||||
),
|
||||
|
@ -1128,7 +1233,7 @@ class _TwoRecordsThingy extends StatelessWidget {
|
|||
text: "",
|
||||
style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 36, fontWeight: FontWeight.w500, color: Colors.white),
|
||||
children: [
|
||||
TextSpan(text: blitz != null ? NumberFormat.decimalPattern().format(blitz!.endContext.score) : "---"),
|
||||
TextSpan(text: blitz != null ? NumberFormat.decimalPattern().format(blitz!.stats.score) : "---"),
|
||||
//WidgetSpan(child: Image.asset("res/icons/kagari.png", height: 48))
|
||||
]
|
||||
),
|
||||
|
@ -1139,15 +1244,15 @@ class _TwoRecordsThingy extends StatelessWidget {
|
|||
text: "",
|
||||
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, color: Colors.grey),
|
||||
children: [
|
||||
if (rank != null && rank != "z") TextSpan(text: "${t.verdictGeneral(n: readableIntDifference(blitz!.endContext.score, blitzAverages[rank]!), verdict: blitzBetterThanRankAverage??false ? t.verdictBetter : t.verdictWorse, rank: rank!.toUpperCase())}\n", style: TextStyle(
|
||||
if (rank != null && rank != "z" && rank != "x+") TextSpan(text: "${t.verdictGeneral(n: readableIntDifference(blitz!.stats.score, blitzAverages[rank]!), verdict: blitzBetterThanRankAverage??false ? t.verdictBetter : t.verdictWorse, rank: rank!.toUpperCase())}\n", style: TextStyle(
|
||||
color: blitzBetterThanRankAverage??false ? Colors.greenAccent : Colors.redAccent
|
||||
))
|
||||
else TextSpan(text: "${t.verdictGeneral(n: readableIntDifference(blitz!.endContext.score, closestAverageBlitz.value), verdict: blitzBetterThanClosestAverage ? t.verdictBetter : t.verdictWorse, rank: closestAverageBlitz.key.toUpperCase())}\n", style: TextStyle(
|
||||
else TextSpan(text: "${t.verdictGeneral(n: readableIntDifference(blitz!.stats.score, closestAverageBlitz.value), verdict: blitzBetterThanClosestAverage ? t.verdictBetter : t.verdictWorse, rank: closestAverageBlitz.key.toUpperCase())}\n", style: TextStyle(
|
||||
color: blitzBetterThanClosestAverage ? Colors.greenAccent : Colors.redAccent
|
||||
)),
|
||||
TextSpan(text: timestamp(blitz!.timestamp)),
|
||||
if (blitz!.rank != null) const TextSpan(text: " • "),
|
||||
if (blitz!.rank != null) TextSpan(text: "№${blitz!.rank}", style: TextStyle(color: getColorOfRank(blitz!.rank!))),
|
||||
const TextSpan(text: " • "),
|
||||
TextSpan(text: "№${blitz!.rank}", style: TextStyle(color: getColorOfRank(blitz!.rank))),
|
||||
]
|
||||
),
|
||||
),
|
||||
|
@ -1162,14 +1267,14 @@ class _TwoRecordsThingy extends StatelessWidget {
|
|||
crossAxisAlignment: WrapCrossAlignment.start,
|
||||
spacing: 20,
|
||||
children: [
|
||||
StatCellNum(playerStat: blitz!.endContext.level, playerStatLabel: t.statCellNum.level, isScreenBig: true, higherIsBetter: true, smallDecimal: false),
|
||||
StatCellNum(playerStat: blitz!.endContext.pps, playerStatLabel: t.statCellNum.pps, fractionDigits: 2, isScreenBig: true, higherIsBetter: true, smallDecimal: false),
|
||||
StatCellNum(playerStat: blitz!.endContext.spp, playerStatLabel: t.statCellNum.spp, fractionDigits: 2, isScreenBig: true, higherIsBetter: true)
|
||||
StatCellNum(playerStat: blitz!.stats.level, playerStatLabel: t.statCellNum.level, isScreenBig: true, higherIsBetter: true, smallDecimal: false),
|
||||
StatCellNum(playerStat: blitz!.stats.pps, playerStatLabel: t.statCellNum.pps, fractionDigits: 2, isScreenBig: true, higherIsBetter: true, smallDecimal: false),
|
||||
StatCellNum(playerStat: blitz!.stats.spp, playerStatLabel: t.statCellNum.spp, fractionDigits: 2, isScreenBig: true, higherIsBetter: true)
|
||||
],
|
||||
),
|
||||
if (blitz != null) FinesseThingy(blitz?.endContext.finesse, blitz?.endContext.finessePercentage),
|
||||
if (blitz != null) LineclearsThingy(blitz!.endContext.clears, blitz!.endContext.lines, blitz!.endContext.holds, blitz!.endContext.tSpins),
|
||||
if (blitz != null) Text("${blitz!.endContext.piecesPlaced} P • ${blitz!.endContext.inputs} KP • ${f2.format(blitz!.endContext.kpp)} KPP • ${f2.format(blitz!.endContext.kps)} KPS"),
|
||||
if (blitz != null) FinesseThingy(blitz?.stats.finesse, blitz?.stats.finessePercentage),
|
||||
if (blitz != null) LineclearsThingy(blitz!.stats.clears, blitz!.stats.lines, blitz!.stats.holds, blitz!.stats.tSpins),
|
||||
if (blitz != null) Text("${blitz!.stats.piecesPlaced} P • ${blitz!.stats.inputs} KP • ${f2.format(blitz!.stats.kpp)} KPP • ${f2.format(blitz!.stats.kps)} KPS"),
|
||||
if (blitz != null) Wrap(
|
||||
alignment: WrapAlignment.spaceBetween,
|
||||
crossAxisAlignment: WrapCrossAlignment.start,
|
||||
|
@ -1187,10 +1292,10 @@ class _TwoRecordsThingy extends StatelessWidget {
|
|||
for (int i = 1; i < blitzStream.records.length; i++) ListTile(
|
||||
onTap: () => Navigator.push(context, MaterialPageRoute(builder: (context) => SingleplayerRecordView(record: blitzStream.records[i]))),
|
||||
leading: Text("#${i+1}", style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28, shadows: textShadow, height: 0.9) ),
|
||||
title: Text("${NumberFormat.decimalPattern().format(blitzStream.records[i].endContext.score)} points",
|
||||
title: Text("${NumberFormat.decimalPattern().format(blitzStream.records[i].stats.score)} points",
|
||||
style: const TextStyle(fontSize: 18)),
|
||||
subtitle: Text(timestamp(blitzStream.records[i].timestamp), style: const TextStyle(color: Colors.grey, height: 0.85)),
|
||||
trailing: SpTrailingStats(blitzStream.records[i].endContext)
|
||||
trailing: SpTrailingStats(blitzStream.records[i], blitzStream.records[i].gamemode)
|
||||
)
|
||||
],
|
||||
),
|
||||
|
@ -1217,7 +1322,6 @@ class _RecentSingleplayersThingy extends StatelessWidget {
|
|||
child: RecentSingleplayerGames(recent: recent, hideTitle: true)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class _OtherThingy extends StatelessWidget {
|
||||
|
@ -1277,7 +1381,9 @@ class _OtherThingy extends StatelessWidget {
|
|||
Map<String, String> gametypes = {
|
||||
"40l": t.sprint,
|
||||
"blitz": t.blitz,
|
||||
"5mblast": "5,000,000 Blast"
|
||||
"5mblast": "5,000,000 Blast",
|
||||
"zenith": "Quick Play",
|
||||
"zenithex": "Quick Play Expert",
|
||||
};
|
||||
|
||||
// Individuly handle each entry type
|
||||
|
@ -1306,7 +1412,16 @@ class _OtherThingy extends StatelessWidget {
|
|||
children: [
|
||||
TextSpan(text: "${gametypes[news.data["gametype"]]} ", style: const TextStyle(fontWeight: FontWeight.bold)),
|
||||
TextSpan(text: t.newsParts.personalbestMiddle),
|
||||
TextSpan(text: news.data["gametype"] == "blitz" ? NumberFormat.decimalPattern().format(news.data["result"]) : get40lTime((news.data["result"]*1000).floor()), style: const TextStyle(fontWeight: FontWeight.bold)),
|
||||
TextSpan(text: switch (news.data["gametype"]){
|
||||
"blitz" => NumberFormat.decimalPattern().format(news.data["result"]),
|
||||
"40l" => get40lTime((news.data["result"]*1000).floor()),
|
||||
"5mblast" => get40lTime((news.data["result"]*1000).floor()),
|
||||
"zenith" => "${f2.format(news.data["result"])} m.",
|
||||
"zenithex" => "${f2.format(news.data["result"])} m.",
|
||||
_ => "unknown"
|
||||
},
|
||||
style: const TextStyle(fontWeight: FontWeight.bold)
|
||||
),
|
||||
]
|
||||
)
|
||||
),
|
||||
|
@ -1452,6 +1567,14 @@ class _OtherThingy extends StatelessWidget {
|
|||
Text(t.zen, style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)),
|
||||
Text("${t.statCellNum.level} ${NumberFormat.decimalPattern().format(zen!.level)}", style: const TextStyle(fontSize: 28, fontWeight: FontWeight.bold)),
|
||||
Text("${t.statCellNum.score} ${NumberFormat.decimalPattern().format(zen!.score)}", style: const TextStyle(fontSize: 18)),
|
||||
Container(
|
||||
constraints: const BoxConstraints(maxWidth: 300.0),
|
||||
child: Row(children: [
|
||||
const Text("Score requirement to level up:"),
|
||||
const Spacer(),
|
||||
Text(intf.format(zen!.scoreRequirement))
|
||||
],),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
@ -4,7 +4,6 @@ import 'package:flutter/material.dart';
|
|||
import 'package:intl/intl.dart';
|
||||
import 'package:tetra_stats/main.dart' show teto;
|
||||
import 'package:tetra_stats/gen/strings.g.dart';
|
||||
import 'package:tetra_stats/views/tl_match_view.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
|
||||
late String oldWindowTitle;
|
||||
|
@ -73,12 +72,6 @@ class MatchesState extends State<MatchesView> {
|
|||
}));
|
||||
},
|
||||
),
|
||||
onTap: (){Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => TlMatchResultView(record: value, initPlayerId: widget.userID),
|
||||
),
|
||||
);},
|
||||
)]
|
||||
: [Center(child: Text(t.noRecords, style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28)))],
|
||||
);
|
||||
|
|
|
@ -360,7 +360,7 @@ class RankState extends State<RankView> with SingleTickerProviderStateMixin {
|
|||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text("${_f2.format(they[index].rating)} TR", style: bigScreen ? const TextStyle(fontSize: 28) : null),
|
||||
Text("${_f2.format(they[index].tr)} TR", style: bigScreen ? const TextStyle(fontSize: 28) : null),
|
||||
Image.asset("res/tetrio_tl_alpha_ranks/${they[index].rank}.png", height: bigScreen ? 48 : 16),
|
||||
],
|
||||
),
|
||||
|
@ -379,6 +379,8 @@ class RankState extends State<RankView> with SingleTickerProviderStateMixin {
|
|||
child: ListView(
|
||||
children: [
|
||||
_ListEntry(value: widget.rank[1]["lowestTR"], label: t.statCellNum.tr.replaceAll(RegExp(r'\n'), " "), id: widget.rank[1]["lowestTRid"], username: widget.rank[1]["lowestTRnick"], approximate: false, fractionDigits: 2),
|
||||
_ListEntry(value: widget.rank[1]["lowestGlixare"], label: "Glixare", id: widget.rank[1]["lowestGlixareID"], username: widget.rank[1]["lowestGlixareNick"], approximate: false, fractionDigits: 3),
|
||||
_ListEntry(value: widget.rank[1]["lowestS1tr"], label: "S1 ${t.statCellNum.tr.replaceAll(RegExp(r'\n'), " ")}", id: widget.rank[1]["lowestS1trID"], username: widget.rank[1]["lowestS1trNick"], approximate: false, fractionDigits: 2),
|
||||
_ListEntry(value: widget.rank[1]["lowestGlicko"], label: "Glicko", id: widget.rank[1]["lowestGlickoID"], username: widget.rank[1]["lowestGlickoNick"], approximate: false, fractionDigits: 2),
|
||||
_ListEntry(value: widget.rank[1]["lowestRD"], label: t.statCellNum.rd.replaceAll(RegExp(r'\n'), " "), id: widget.rank[1]["lowestRdID"], username: widget.rank[1]["lowestRdNick"], approximate: false, fractionDigits: 3),
|
||||
_ListEntry(value: widget.rank[1]["lowestGamesPlayed"], label: t.statCellNum.gamesPlayed.replaceAll(RegExp(r'\n'), " "), id: widget.rank[1]["lowestGamesPlayedID"], username: widget.rank[1]["lowestGamesPlayedNick"], approximate: false),
|
||||
|
@ -412,7 +414,9 @@ class RankState extends State<RankView> with SingleTickerProviderStateMixin {
|
|||
Text(t.averageValues, style: TextStyle( fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)),
|
||||
Expanded(
|
||||
child: ListView(children: [
|
||||
_ListEntry(value: widget.rank[0].rating, label: t.statCellNum.tr.replaceAll(RegExp(r'\n'), " "), id: "", username: "", approximate: true, fractionDigits: 2),
|
||||
_ListEntry(value: widget.rank[0].tr, label: t.statCellNum.tr.replaceAll(RegExp(r'\n'), " "), id: "", username: "", approximate: true, fractionDigits: 2),
|
||||
_ListEntry(value: widget.rank[0].gxe, label: "Glixare", id: "", username: "", approximate: false, fractionDigits: 3),
|
||||
_ListEntry(value: widget.rank[0].s1tr, label: "S1 ${t.statCellNum.tr.replaceAll(RegExp(r'\n'), " ")}", id: "", username: "", approximate: false, fractionDigits: 2),
|
||||
_ListEntry(value: widget.rank[0].glicko, label: "Glicko", id: "", username: "", approximate: true, fractionDigits: 2),
|
||||
_ListEntry(value: widget.rank[0].rd, label: t.statCellNum.rd.replaceAll(RegExp(r'\n'), " "), id: "", username: "", approximate: true, fractionDigits: 3),
|
||||
_ListEntry(value: widget.rank[0].gamesPlayed, label: t.statCellNum.gamesPlayed.replaceAll(RegExp(r'\n'), " "), id: "", username: "", approximate: true, fractionDigits: 0),
|
||||
|
@ -446,6 +450,8 @@ class RankState extends State<RankView> with SingleTickerProviderStateMixin {
|
|||
child: ListView(
|
||||
children: [
|
||||
_ListEntry(value: widget.rank[1]["highestTR"], label: t.statCellNum.tr.replaceAll(RegExp(r'\n'), " "), id: widget.rank[1]["highestTRid"], username: widget.rank[1]["highestTRnick"], approximate: false, fractionDigits: 2),
|
||||
_ListEntry(value: widget.rank[1]["highestGlixare"], label: "Glixare", id: widget.rank[1]["highestGlixareID"], username: widget.rank[1]["highestGlixareNick"], approximate: false, fractionDigits: 3),
|
||||
_ListEntry(value: widget.rank[1]["highestS1tr"], label: "S1 ${t.statCellNum.tr.replaceAll(RegExp(r'\n'), " ")}", id: widget.rank[1]["highestS1trID"], username: widget.rank[1]["highestS1trNick"], approximate: false, fractionDigits: 2),
|
||||
_ListEntry(value: widget.rank[1]["highestGlicko"], label: "Glicko", id: widget.rank[1]["highestGlickoID"], username: widget.rank[1]["highestGlickoNick"], approximate: false, fractionDigits: 2),
|
||||
_ListEntry(value: widget.rank[1]["highestRD"], label: t.statCellNum.rd.replaceAll(RegExp(r'\n'), " "), id: widget.rank[1]["highestRdID"], username: widget.rank[1]["highestRdNick"], approximate: false, fractionDigits: 3),
|
||||
_ListEntry(value: widget.rank[1]["highestGamesPlayed"], label: t.statCellNum.gamesPlayed.replaceAll(RegExp(r'\n'), " "), id: widget.rank[1]["highestGamesPlayedID"], username: widget.rank[1]["highestGamesPlayedNick"], approximate: false),
|
||||
|
@ -517,7 +523,7 @@ class _ListEntry extends StatelessWidget {
|
|||
children: [
|
||||
Text(f.format(value),
|
||||
style: const TextStyle(fontSize: 22, height: 0.9)),
|
||||
if (id.isNotEmpty) Text(t.forPlayer(username: username))
|
||||
if (id.isNotEmpty) Text(t.forPlayer(username: username), style: const TextStyle(color: Colors.grey, fontWeight: FontWeight.w100),)
|
||||
],
|
||||
),
|
||||
onTap: id.isNotEmpty
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import 'dart:io';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tetra_stats/data_objects/tetrio.dart';
|
||||
import 'package:tetra_stats/gen/strings.g.dart';
|
||||
import 'package:tetra_stats/utils/numers_formats.dart';
|
||||
import 'package:tetra_stats/views/rank_averages_view.dart';
|
||||
import 'package:tetra_stats/utils/text_shadow.dart';
|
||||
import 'package:tetra_stats/widgets/text_timestamp.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
import 'package:tetra_stats/main.dart' show teto;
|
||||
|
||||
|
@ -17,14 +19,9 @@ class RankAveragesView extends StatefulWidget {
|
|||
late String oldWindowTitle;
|
||||
|
||||
class RanksAverages extends State<RankAveragesView> {
|
||||
Map<String, List<dynamic>> averages = {};
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
teto.fetchTLLeaderboard().then((value){
|
||||
averages = value.averages;
|
||||
setState(() {});
|
||||
});
|
||||
if (!kIsWeb && !Platform.isAndroid && !Platform.isIOS){
|
||||
windowManager.getTitle().then((value) => oldWindowTitle = value);
|
||||
windowManager.setTitle("Tetra Stats: ${t.rankAveragesViewTitle}");
|
||||
|
@ -46,29 +43,133 @@ class RanksAverages extends State<RankAveragesView> {
|
|||
),
|
||||
backgroundColor: Colors.black,
|
||||
body: SafeArea(
|
||||
child: averages.isEmpty ? const Center(child: Text('Fetching...')) : ListView.builder(
|
||||
itemCount: averages.length,
|
||||
itemBuilder: (context, index){
|
||||
List<String> keys = averages.keys.toList();
|
||||
return ListTile(
|
||||
leading: Image.asset("res/tetrio_tl_alpha_ranks/${keys[index]}.png", height: 48),
|
||||
title: Text(t.players(n: averages[keys[index]]?[1]["players"]), style: const TextStyle(fontFamily: "Eurostile Round Extended")),
|
||||
subtitle: Text("${f2.format(averages[keys[index]]?[0].apm)} APM, ${f2.format(averages[keys[index]]?[0].pps)} PPS, ${f2.format(averages[keys[index]]?[0].vs)} VS, ${f2.format(averages[keys[index]]?[0].nerdStats.app)} APP, ${f2.format(averages[keys[index]]?[0].nerdStats.vsapm)} VS/APM",
|
||||
style: const TextStyle(fontFamily: "Eurostile Round Condensed", color: Colors.grey, fontSize: 13)),
|
||||
trailing: Text("${f2.format(averages[keys[index]]?[1]["toEnterTR"])} TR", style: const TextStyle(fontSize: 28, fontFamily: "Eurostile Round")),
|
||||
onTap: (){
|
||||
if (averages[keys[index]]?[1]["players"] > 0) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => RankView(rank: averages[keys[index]]!),
|
||||
child: FutureBuilder<CutoffsTetrio?>(future: teto.fetchCutoffsTetrio(), builder: (context, snapshot){
|
||||
switch (snapshot.connectionState) {
|
||||
case ConnectionState.none:
|
||||
case ConnectionState.waiting:
|
||||
case ConnectionState.active:
|
||||
return const Center(child: CircularProgressIndicator(color: Colors.white));
|
||||
case ConnectionState.done:
|
||||
if (snapshot.hasData){
|
||||
return Container(
|
||||
alignment: Alignment.center,
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
width: 900,
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Table(
|
||||
defaultVerticalAlignment: TableCellVerticalAlignment.middle,
|
||||
border: TableBorder.all(color: Colors.grey.shade900),
|
||||
columnWidths: const {
|
||||
0: FixedColumnWidth(48),
|
||||
1: FixedColumnWidth(155),
|
||||
2: FixedColumnWidth(150),
|
||||
3: FixedColumnWidth(90),
|
||||
4: FixedColumnWidth(130),
|
||||
},
|
||||
children: [
|
||||
TableRow(
|
||||
children: [
|
||||
Text(t.rank, textAlign: TextAlign.center, style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, fontWeight: FontWeight.w500, color: Colors.white)),
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(right: 8.0),
|
||||
child: Text("TR", textAlign: TextAlign.right, style: TextStyle(fontFamily: "Eurostile Round", fontSize: 28, fontWeight: FontWeight.w500, color: Colors.white)),
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(right: 8.0),
|
||||
child: Text("APM", textAlign: TextAlign.right, style: TextStyle(fontFamily: "Eurostile Round", fontSize: 28, fontWeight: FontWeight.w500, color: Colors.white)),
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(right: 8.0),
|
||||
child: Text("PPS", textAlign: TextAlign.right, style: TextStyle(fontFamily: "Eurostile Round", fontSize: 28, fontWeight: FontWeight.w500, color: Colors.white)),
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(right: 8.0),
|
||||
child: Text("VS", textAlign: TextAlign.right, style: TextStyle(fontFamily: "Eurostile Round", fontSize: 28, fontWeight: FontWeight.w500, color: Colors.white)),
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(right: 8.0),
|
||||
child: Text("Advanced", textAlign: TextAlign.right, style: TextStyle(fontFamily: "Eurostile Round", fontSize: 14, fontWeight: FontWeight.w500, color: Colors.white)),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 8.0),
|
||||
child: Text("Players (${intf.format(snapshot.data!.total)})", textAlign: TextAlign.right, style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, fontWeight: FontWeight.w500, color: Colors.white)),
|
||||
),
|
||||
]
|
||||
),
|
||||
for (String rank in snapshot.data!.data.keys) TableRow(
|
||||
decoration: BoxDecoration(gradient: LinearGradient(colors: [rankColors[rank]!.withAlpha(200), rankColors[rank]!.withAlpha(100)])),
|
||||
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/$rank.png", height: 48)),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 8.0),
|
||||
child: Text(f2.format(snapshot.data!.data[rank]!.tr), textAlign: TextAlign.right, style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28, fontWeight: FontWeight.w500, color: Colors.white, shadows: textShadow)),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 8.0),
|
||||
child: Text(f2.format(snapshot.data!.data[rank]!.apm), textAlign: TextAlign.right, style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28, fontWeight: FontWeight.w100, color: Colors.white, shadows: textShadow)),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 8.0),
|
||||
child: Text(f2.format(snapshot.data!.data[rank]!.pps), textAlign: TextAlign.right, style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28, fontWeight: FontWeight.w100, color: Colors.white, shadows: textShadow)),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 8.0),
|
||||
child: Text(f2.format(snapshot.data!.data[rank]!.vs), textAlign: TextAlign.right, style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28, fontWeight: FontWeight.w100, color: Colors.white, shadows: textShadow)),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 8.0),
|
||||
child: Text("${f3.format(snapshot.data!.data[rank]!.apm / (snapshot.data!.data[rank]!.pps * 60))} APP\n${f3.format(snapshot.data!.data[rank]!.vs / snapshot.data!.data[rank]!.apm)} VS/APM", textAlign: TextAlign.right, style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, fontWeight: FontWeight.w100, color: Colors.white, shadows: textShadow)),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 8.0),
|
||||
child: RichText(
|
||||
textAlign: TextAlign.right,
|
||||
text: TextSpan(
|
||||
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, fontWeight: FontWeight.w100, color: Colors.white, shadows: textShadow),
|
||||
children: [
|
||||
TextSpan(text: intf.format(snapshot.data!.data[rank]!.count)),
|
||||
TextSpan(text: " (${f2.format(snapshot.data!.data[rank]!.countPercentile * 100)}%)", style: const TextStyle(color: Colors.white60, shadows: null)),
|
||||
TextSpan(text: "\n(from № ${intf.format(snapshot.data!.data[rank]!.pos)})", style: const TextStyle(color: Colors.white60, shadows: null))
|
||||
]
|
||||
))
|
||||
),
|
||||
]
|
||||
)
|
||||
],
|
||||
),
|
||||
Text(t.sprintAndBlitsRelevance(date: timestamp(snapshot.data!.timestamp)))
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
})
|
||||
),
|
||||
}
|
||||
if (snapshot.hasError){
|
||||
return Center(child:
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(snapshot.error.toString(), style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 42, fontWeight: FontWeight.bold), textAlign: TextAlign.center),
|
||||
if (snapshot.stackTrace != null) Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: Text(snapshot.stackTrace.toString(), style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 18), textAlign: TextAlign.center),
|
||||
),
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
return const Text("end of FutureBuilder");
|
||||
}
|
||||
})
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ class SingleplayerRecordView extends StatelessWidget {
|
|||
backgroundColor: Colors.black,
|
||||
appBar: AppBar(
|
||||
title: Text("${
|
||||
switch (record.endContext.gameType){
|
||||
switch (record.gamemode){
|
||||
"40l" => t.sprint,
|
||||
"blitz" => t.blitz,
|
||||
String() => "5000000 Blast",
|
||||
|
|
|
@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:intl/intl.dart';
|
||||
import 'package:tetra_stats/data_objects/tetrio.dart';
|
||||
import 'package:tetra_stats/gen/strings.g.dart';
|
||||
import 'package:tetra_stats/widgets/text_timestamp.dart';
|
||||
import 'package:tetra_stats/widgets/tl_thingy.dart';
|
||||
import 'package:tetra_stats/widgets/user_thingy.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
|
@ -11,7 +12,7 @@ import 'package:window_manager/window_manager.dart';
|
|||
final DateFormat dateFormat = DateFormat.yMMMd(LocaleSettings.currentLocale.languageCode).add_Hms();
|
||||
|
||||
class StateView extends StatefulWidget {
|
||||
final TetrioPlayer state;
|
||||
final TetraLeague state;
|
||||
const StateView({super.key, required this.state});
|
||||
|
||||
@override
|
||||
|
@ -28,7 +29,7 @@ class StateState extends State<StateView> {
|
|||
_scrollController = ScrollController();
|
||||
if (!kIsWeb && !Platform.isAndroid && !Platform.isIOS){
|
||||
windowManager.getTitle().then((value) => oldWindowTitle = value);
|
||||
windowManager.setTitle("Tetra Stats: ${t.stateViewTitle(nickname: widget.state.username.toUpperCase(), date: dateFormat.format(widget.state.state))}");
|
||||
windowManager.setTitle("State from ${timestamp(widget.state.timestamp)}");
|
||||
}
|
||||
super.initState();
|
||||
}
|
||||
|
@ -48,16 +49,13 @@ class StateState extends State<StateView> {
|
|||
Widget build(BuildContext context) {
|
||||
final t = Translations.of(context);
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(t.stateViewTitle(nickname: widget.state.username.toUpperCase(), date: dateFormat.format(widget.state.state))),
|
||||
),
|
||||
backgroundColor: Colors.black,
|
||||
body: SafeArea(
|
||||
child: NestedScrollView(
|
||||
controller: _scrollController,
|
||||
headerSliverBuilder: (context, value) {
|
||||
return [SliverToBoxAdapter(child: UserThingy(player: widget.state, showStateTimestamp: true, setState: _justUpdate))];
|
||||
},
|
||||
body: TLThingy(tl: widget.state.tlSeason1, userID: widget.state.userId, states: const [],))));
|
||||
appBar: AppBar(
|
||||
title: Text("State from ${timestamp(widget.state.timestamp)}"),
|
||||
),
|
||||
backgroundColor: Colors.black,
|
||||
body: SafeArea(
|
||||
child: TLThingy(tl: widget.state, userID: widget.state.id, states: [])
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
import 'dart:io';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:tetra_stats/data_objects/tetrio.dart';
|
||||
import 'package:tetra_stats/gen/strings.g.dart';
|
||||
import 'package:tetra_stats/main.dart' show teto;
|
||||
import 'package:tetra_stats/utils/numers_formats.dart';
|
||||
import 'package:tetra_stats/views/mathes_view.dart';
|
||||
import 'package:tetra_stats/views/state_view.dart';
|
||||
import 'package:tetra_stats/widgets/text_timestamp.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
|
||||
class StatesView extends StatefulWidget {
|
||||
final List<TetrioPlayer> states;
|
||||
const StatesView({super.key, required this.states});
|
||||
final String nickname;
|
||||
final String id;
|
||||
const StatesView({required this.nickname, required this.id, super.key});
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => StatesState();
|
||||
|
@ -25,7 +26,7 @@ class StatesState extends State<StatesView> {
|
|||
void initState() {
|
||||
if (!kIsWeb && !Platform.isAndroid && !Platform.isIOS){
|
||||
windowManager.getTitle().then((value) => oldWindowTitle = value);
|
||||
windowManager.setTitle("Tetra Stats: ${t.statesViewTitle(number: widget.states.length, nickname: widget.states.last.username.toUpperCase())}");
|
||||
//windowManager.setTitle("Tetra Stats: ${t.statesViewTitle(number: widget.states.length, nickname: widget.states.last.id.toUpperCase())}");
|
||||
}
|
||||
super.initState();
|
||||
}
|
||||
|
@ -41,45 +42,78 @@ class StatesState extends State<StatesView> {
|
|||
final t = Translations.of(context);
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(t.statesViewTitle(number: widget.states.length, nickname: widget.states.last.username.toUpperCase())),
|
||||
title: Text(t.statesViewTitle(number: "", nickname: widget.nickname)),
|
||||
actions: [
|
||||
IconButton(
|
||||
onPressed: (){
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => MatchesView(userID: widget.states.first.userId, username: widget.states.first.username),
|
||||
),
|
||||
);
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => MatchesView(userID: widget.id, username: widget.nickname),
|
||||
),
|
||||
);
|
||||
}, icon: const Icon(Icons.list), tooltip: t.viewAllMatches)
|
||||
],
|
||||
),
|
||||
backgroundColor: Colors.black,
|
||||
body: SafeArea(
|
||||
child: ListView.builder(
|
||||
itemCount: widget.states.length,
|
||||
itemBuilder: (context, index) {
|
||||
return ListTile(
|
||||
title: Text(timestamp(widget.states[index].state)),
|
||||
subtitle: Text(t.statesViewEntry(level: widget.states[index].level.toStringAsFixed(2), gameTime: widget.states[index].gameTime, friends: widget.states[index].friendCount, rd: NumberFormat.compact().format(widget.states[index].tlSeason1.rd))),
|
||||
trailing: IconButton(
|
||||
icon: const Icon(Icons.delete_forever),
|
||||
onPressed: () {
|
||||
DateTime nn = widget.states[index].state;
|
||||
teto.deleteState(widget.states[index]).then((value) => setState(() {
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.stateRemoved(date: timestamp(nn)))));
|
||||
}));
|
||||
},
|
||||
child: FutureBuilder<List<TetraLeague>>(future: teto.getStates(widget.id), builder: (context, snapshot) {
|
||||
switch (snapshot.connectionState) {
|
||||
case ConnectionState.none:
|
||||
case ConnectionState.waiting:
|
||||
case ConnectionState.active:
|
||||
return const Center(child: CircularProgressIndicator(color: Colors.white));
|
||||
case ConnectionState.done:
|
||||
if (snapshot.hasData) {
|
||||
return ListView.builder(
|
||||
itemCount: snapshot.data!.length,
|
||||
prototypeItem: ListTile(
|
||||
title: Text(""),
|
||||
subtitle: Text("", style: TextStyle(color: Colors.grey)),
|
||||
trailing: IconButton(icon: const Icon(Icons.delete_forever), onPressed: (){}),
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => StateView(state: widget.states[index]),
|
||||
itemBuilder: (context, index) {
|
||||
return ListTile(
|
||||
title: Text(timestamp(snapshot.data![index].timestamp)),
|
||||
subtitle: Text(
|
||||
t.statesViewEntry(level: f2.format(snapshot.data![index].tr), games: intf.format(snapshot.data![index].gamesPlayed), glicko: snapshot.data![index].glicko != null ? f2.format(snapshot.data![index].glicko) : "---", rd: snapshot.data![index].rd != null ? f2.format(snapshot.data![index].rd) : "--"),
|
||||
style: TextStyle(color: Colors.grey),
|
||||
),
|
||||
trailing: IconButton(
|
||||
icon: const Icon(Icons.delete_forever),
|
||||
onPressed: () {
|
||||
teto.deleteState(snapshot.data![index].id+snapshot.data![index].timestamp.millisecondsSinceEpoch.toRadixString(16)).then((value) => setState(() {
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.stateRemoved(date: timestamp(snapshot.data![index].timestamp)))));
|
||||
}));
|
||||
},
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => StateView(state: snapshot.data![index]),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
});
|
||||
} else if (snapshot.hasError) {
|
||||
return Center(child:
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(snapshot.error.toString(), style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 42, fontWeight: FontWeight.bold), textAlign: TextAlign.center),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: Text(snapshot.stackTrace.toString(), style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 18), textAlign: TextAlign.center),
|
||||
),
|
||||
],
|
||||
)
|
||||
);
|
||||
})));
|
||||
}
|
||||
break;
|
||||
}
|
||||
return const Center(child: Text('default case of FutureBuilder', style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 42), textAlign: TextAlign.center));
|
||||
}
|
||||
)));}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,13 +4,13 @@ import 'package:flutter/material.dart';
|
|||
import 'package:intl/intl.dart';
|
||||
import 'package:tetra_stats/data_objects/tetrio.dart';
|
||||
import 'package:tetra_stats/gen/strings.g.dart';
|
||||
import 'package:tetra_stats/services/tetrio_crud.dart';
|
||||
import 'package:tetra_stats/main.dart';
|
||||
import 'package:tetra_stats/views/main_view.dart';
|
||||
import 'package:tetra_stats/views/rank_averages_view.dart';
|
||||
import 'package:tetra_stats/views/ranks_averages_view.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
import 'package:tetra_stats/widgets/text_timestamp.dart';
|
||||
|
||||
final TetrioService _teto = TetrioService();
|
||||
List<DropdownMenuItem> _itemStats = [for (MapEntry e in chartsShortTitles.entries) DropdownMenuItem(value: e.key, child: Text(e.value))];
|
||||
Stats _sortBy = Stats.tr;
|
||||
bool reversed = false;
|
||||
|
@ -64,148 +64,155 @@ class TLLeaderboardState extends State<TLLeaderboardView> {
|
|||
),
|
||||
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: CircularProgressIndicator());
|
||||
case ConnectionState.done:
|
||||
final allPlayers = snapshot.data?.getStatRanking(snapshot.data!.leaderboard, _sortBy, reversed: reversed, country: _country);
|
||||
if (!kIsWeb && !Platform.isAndroid && !Platform.isIOS) windowManager.setTitle("Tetra Stats: ${t.tlLeaderboard} - ${t.players(n: allPlayers!.length)}");
|
||||
bool bigScreen = MediaQuery.of(context).size.width > 768;
|
||||
return NestedScrollView(
|
||||
headerSliverBuilder: (context, value) {
|
||||
String howManyPlayers(int numberOfPlayers) => Intl.plural(
|
||||
numberOfPlayers,
|
||||
zero: t.lbViewZeroEntrys,
|
||||
one: t.lbViewOneEntry,
|
||||
other: t.lbViewManyEntrys(numberOfPlayers: t.players(n: numberOfPlayers)),
|
||||
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: Wrap(
|
||||
direction: Axis.horizontal,
|
||||
alignment: WrapAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
howManyPlayers(allPlayers.length),
|
||||
style: const TextStyle(color: Colors.white, fontSize: 25),
|
||||
),
|
||||
TextButton(onPressed: (){
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => RankView(rank: snapshot.data!.getAverageOfRank("")),
|
||||
),
|
||||
);
|
||||
}, child: Text(t.everyoneAverages,
|
||||
style: const TextStyle(fontSize: 25)))
|
||||
],)
|
||||
)),
|
||||
SliverToBoxAdapter(child: Padding(
|
||||
padding: const EdgeInsets.only(left: 16),
|
||||
child: Wrap(
|
||||
direction: Axis.horizontal,
|
||||
alignment: WrapAlignment.start,
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
spacing: 16,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.baseline,
|
||||
textBaseline: TextBaseline.alphabetic,
|
||||
children: [
|
||||
Text("${t.sortBy}: ",
|
||||
style: const TextStyle(color: Colors.white, fontSize: 25)),
|
||||
DropdownButton(items: _itemStats, value: _sortBy, onChanged: ((value) {
|
||||
_sortBy = value;
|
||||
setState(() {});
|
||||
}),),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.baseline,
|
||||
textBaseline: TextBaseline.alphabetic,
|
||||
children: [
|
||||
Text("${t.reversed}: ",
|
||||
style: const TextStyle(color: Colors.white, fontSize: 25)),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(0, 5.5, 0, 7.5),
|
||||
child: Checkbox(value: reversed,
|
||||
checkColor: Colors.black,
|
||||
onChanged: ((value) {
|
||||
reversed = value!;
|
||||
setState(() {});
|
||||
}),),
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.baseline,
|
||||
textBaseline: TextBaseline.alphabetic,
|
||||
children: [
|
||||
Text("${t.country}: ",
|
||||
style: const TextStyle(color: Colors.white, fontSize: 25)),
|
||||
DropdownButton(items: _itemCountries, value: _country, onChanged: ((value) {
|
||||
_country = value;
|
||||
setState(() {});
|
||||
}),),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),),
|
||||
const SliverToBoxAdapter(child: Divider())
|
||||
];
|
||||
},
|
||||
body: ListView.builder(
|
||||
itemCount: allPlayers!.length,
|
||||
prototypeItem: ListTile(
|
||||
leading: Text("0", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 28 : 24, height: 0.9)),
|
||||
title: Text("ehhh...", style: TextStyle(fontFamily: bigScreen ? "Eurostile Round Extended" : "Eurostile Round", height: 0.9)),
|
||||
trailing: SizedBox(height: bigScreen ? 48 : 36, width: 1,),
|
||||
subtitle: const Text("eh..."),
|
||||
child: FutureBuilder(
|
||||
future: teto.fetchTLLeaderboard(),
|
||||
builder: (context, snapshot) {
|
||||
switch (snapshot.connectionState) {
|
||||
case ConnectionState.none:
|
||||
case ConnectionState.waiting:
|
||||
case ConnectionState.active:
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
case ConnectionState.done:
|
||||
if (snapshot.hasData){
|
||||
final allPlayers = snapshot.data?.getStatRanking(snapshot.data!.leaderboard, _sortBy, reversed: reversed, country: _country);
|
||||
if (!kIsWeb && !Platform.isAndroid && !Platform.isIOS) windowManager.setTitle("Tetra Stats: ${t.tlLeaderboard} - ${t.players(n: allPlayers != null ? allPlayers.length : 0)}");
|
||||
bool bigScreen = MediaQuery.of(context).size.width > 768;
|
||||
return NestedScrollView(
|
||||
headerSliverBuilder: (context, value) {
|
||||
return [
|
||||
SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 16),
|
||||
child: Wrap(
|
||||
direction: Axis.horizontal,
|
||||
alignment: WrapAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"${t.players(n: allPlayers.length)} • ${t.sprintAndBlitsRelevance(date: timestamp(snapshot.data!.timestamp))}",
|
||||
style: const TextStyle(color: Colors.white, fontSize: 25),
|
||||
),
|
||||
TextButton(onPressed: (){
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => RankView(rank: snapshot.data!.getAverageOfRank("")),
|
||||
),
|
||||
itemBuilder: (context, index) {
|
||||
return ListTile(
|
||||
leading: Text(
|
||||
(index+1).toString(),
|
||||
style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 28 : 24, height: 0.9)
|
||||
);
|
||||
}, child: Text(t.everyoneAverages,
|
||||
style: const TextStyle(fontSize: 25)))
|
||||
],)
|
||||
)),
|
||||
SliverToBoxAdapter(child: Padding(
|
||||
padding: const EdgeInsets.only(left: 16),
|
||||
child: Wrap(
|
||||
direction: Axis.horizontal,
|
||||
alignment: WrapAlignment.start,
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
spacing: 16,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.baseline,
|
||||
textBaseline: TextBaseline.alphabetic,
|
||||
children: [
|
||||
Text("${t.sortBy}: ",
|
||||
style: const TextStyle(color: Colors.white, fontSize: 25)),
|
||||
DropdownButton(items: _itemStats, value: _sortBy, onChanged: ((value) {
|
||||
_sortBy = value;
|
||||
setState(() {});
|
||||
}),),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.baseline,
|
||||
textBaseline: TextBaseline.alphabetic,
|
||||
children: [
|
||||
Text("${t.reversed}: ",
|
||||
style: const TextStyle(color: Colors.white, fontSize: 25)),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(0, 5.5, 0, 7.5),
|
||||
child: Checkbox(value: reversed,
|
||||
checkColor: Colors.black,
|
||||
onChanged: ((value) {
|
||||
reversed = value!;
|
||||
setState(() {});
|
||||
}),),
|
||||
),
|
||||
title: Text(allPlayers[index].username, style: TextStyle(fontFamily: bigScreen ? "Eurostile Round Extended" : "Eurostile Round", height: 0.9)),
|
||||
subtitle: (bigScreen || _sortBy != Stats.tr) ? Text(_sortBy == Stats.tr ? "${f2.format(allPlayers[index].apm)} APM, ${f2.format(allPlayers[index].pps)} PPS, ${f2.format(allPlayers[index].vs)} VS, ${f2.format(allPlayers[index].nerdStats.app)} APP, ${f2.format(allPlayers[index].nerdStats.vsapm)} VS/APM" : "${_f4.format(allPlayers[index].getStatByEnum(_sortBy))} ${chartsShortTitles[_sortBy]}",
|
||||
style: TextStyle(fontFamily: "Eurostile Round Condensed", fontSize: bigScreen ? null : 13, color: _sortBy == Stats.tr ? Colors.grey : null)) : null,
|
||||
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: bigScreen ? 48 : 36),
|
||||
],
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => MainView(player: allPlayers[index].userId),
|
||||
maintainState: false,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}));
|
||||
}
|
||||
})),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.baseline,
|
||||
textBaseline: TextBaseline.alphabetic,
|
||||
children: [
|
||||
Text("${t.country}: ",
|
||||
style: const TextStyle(color: Colors.white, fontSize: 25)),
|
||||
DropdownButton(items: _itemCountries, value: _country, onChanged: ((value) {
|
||||
_country = value;
|
||||
setState(() {});
|
||||
}),),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),),
|
||||
const SliverToBoxAdapter(child: Divider())
|
||||
];
|
||||
},
|
||||
body: ListView.builder(
|
||||
itemCount: allPlayers!.length,
|
||||
prototypeItem: ListTile(
|
||||
leading: Text("0", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 28 : 24, height: 0.9)),
|
||||
title: Text("ehhh...", style: TextStyle(fontFamily: bigScreen ? "Eurostile Round Extended" : "Eurostile Round", height: 0.9)),
|
||||
trailing: SizedBox(height: bigScreen ? 48 : 36, width: 1,),
|
||||
subtitle: const Text("eh..."),
|
||||
),
|
||||
itemBuilder: (context, index) {
|
||||
return ListTile(
|
||||
leading: Text(
|
||||
(index+1).toString(),
|
||||
style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 28 : 24, height: 0.9)
|
||||
),
|
||||
title: Text(allPlayers[index].username, style: TextStyle(fontFamily: bigScreen ? "Eurostile Round Extended" : "Eurostile Round", height: 0.9)),
|
||||
subtitle: (bigScreen || _sortBy != Stats.tr) ? Text(_sortBy == Stats.tr ? "${f2.format(allPlayers[index].apm)} APM, ${f2.format(allPlayers[index].pps)} PPS, ${f2.format(allPlayers[index].vs)} VS, ${f2.format(allPlayers[index].nerdStats.app)} APP, ${f2.format(allPlayers[index].nerdStats.vsapm)} VS/APM" : "${_f4.format(allPlayers[index].getStatByEnum(_sortBy))} ${chartsShortTitles[_sortBy]}",
|
||||
style: TextStyle(fontFamily: "Eurostile Round Condensed", fontSize: bigScreen ? null : 13, color: _sortBy == Stats.tr ? Colors.grey : null)) : null,
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text("${f2.format(allPlayers[index].tr)} TR", style: const TextStyle(fontSize: 28)),
|
||||
Image.asset("res/tetrio_tl_alpha_ranks/${allPlayers[index].rank}.png", height: bigScreen ? 48 : 36),
|
||||
],
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => MainView(player: allPlayers[index].userId),
|
||||
maintainState: false,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}));
|
||||
}
|
||||
if (snapshot.hasError){
|
||||
return Center(child:
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(snapshot.error.toString(), style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 42, fontWeight: FontWeight.bold), textAlign: TextAlign.center),
|
||||
if (snapshot.stackTrace != null) Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: Text(snapshot.stackTrace.toString(), style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 18), textAlign: TextAlign.center),
|
||||
),
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
return const Text("end of FutureBuilder");
|
||||
}
|
||||
})),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,13 +2,11 @@
|
|||
|
||||
import 'dart:io';
|
||||
import 'package:tetra_stats/data_objects/tetrio_multiplayer_replay.dart';
|
||||
import 'package:tetra_stats/services/crud_exceptions.dart';
|
||||
import 'package:tetra_stats/utils/relative_timestamps.dart';
|
||||
import 'package:tetra_stats/views/compare_view.dart' show CompareThingy, CompareBoolThingy;
|
||||
import 'package:tetra_stats/views/compare_view.dart' show CompareThingy;
|
||||
import 'package:tetra_stats/widgets/list_tile_trailing_stats.dart';
|
||||
import 'package:tetra_stats/widgets/text_timestamp.dart';
|
||||
import 'package:tetra_stats/widgets/vs_graphs.dart';
|
||||
import 'package:tetra_stats/main.dart' show teto;
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
@ -30,7 +28,7 @@ Duration framesToTime(int frames){
|
|||
}
|
||||
|
||||
class TlMatchResultView extends StatefulWidget {
|
||||
final TetraLeagueAlphaRecord record;
|
||||
final BetaRecord record;
|
||||
final String initPlayerId;
|
||||
const TlMatchResultView({super.key, required this.record, required this.initPlayerId});
|
||||
|
||||
|
@ -40,15 +38,65 @@ class TlMatchResultView extends StatefulWidget {
|
|||
|
||||
class TlMatchResultState extends State<TlMatchResultView> {
|
||||
late Future<ReplayData?> replayData;
|
||||
late Duration time;
|
||||
late String readableTime;
|
||||
late String reason;
|
||||
Duration totalTime = const Duration();
|
||||
List<Duration> roundLengths = [];
|
||||
List<BetaLeagueStats> timeWeightedStats = [];
|
||||
late bool initPlayerWon;
|
||||
|
||||
@override
|
||||
void initState(){
|
||||
rounds = [DropdownMenuItem(value: -1, child: Text(t.match))];
|
||||
rounds.addAll([for (int i = 0; i < widget.record.endContext.first.secondaryTracking.length; i++) DropdownMenuItem(value: i, child: Text(t.roundNumber(n: i+1)))]);
|
||||
replayData = teto.analyzeReplay(widget.record.replayId, widget.record.replayAvalable);
|
||||
rounds.addAll([for (int i = 0; i < widget.record.results.rounds.length; i++) DropdownMenuItem(value: i, child: Text(t.roundNumber(n: i+1)))]);
|
||||
if (rounds.indexWhere((element) => element.value == -2) == -1) rounds.insert(1, DropdownMenuItem(value: -2, child: Text(t.timeWeightedmatch)));
|
||||
greenSidePlayer = widget.record.results.leaderboard.indexWhere((element) => element.id == widget.initPlayerId);
|
||||
redSidePlayer = widget.record.results.leaderboard.indexWhere((element) => element.id != widget.initPlayerId);
|
||||
List<double> apmMultipliedByWeights = [0, 0];
|
||||
List<double> ppsMultipliedByWeights= [0, 0];
|
||||
List<double> vsMultipliedByWeights = [0, 0];
|
||||
for (var round in widget.record.results.rounds){
|
||||
var longerLifetime = round[0].lifetime.compareTo(round[1].lifetime) == 1 ? round[0].lifetime : round[1].lifetime;
|
||||
roundLengths.add(longerLifetime);
|
||||
totalTime += longerLifetime;
|
||||
|
||||
BetaLeagueRound greenSide = round.firstWhere((element) => element.id == widget.initPlayerId);
|
||||
BetaLeagueRound redSide = round.firstWhere((element) => element.id != widget.initPlayerId);
|
||||
|
||||
apmMultipliedByWeights[0] += greenSide.stats.apm*longerLifetime.inMilliseconds;
|
||||
apmMultipliedByWeights[1] += redSide.stats.apm*longerLifetime.inMilliseconds;
|
||||
ppsMultipliedByWeights[0] += greenSide.stats.pps*longerLifetime.inMilliseconds;
|
||||
ppsMultipliedByWeights[1] += redSide.stats.pps*longerLifetime.inMilliseconds;
|
||||
vsMultipliedByWeights[0] += greenSide.stats.vs*longerLifetime.inMilliseconds;
|
||||
vsMultipliedByWeights[1] += redSide.stats.vs*longerLifetime.inMilliseconds;
|
||||
}
|
||||
timeWeightedStats = [
|
||||
BetaLeagueStats(
|
||||
apm: apmMultipliedByWeights[0]/totalTime.inMilliseconds,
|
||||
pps: ppsMultipliedByWeights[0]/totalTime.inMilliseconds,
|
||||
vs: vsMultipliedByWeights[0]/totalTime.inMilliseconds,
|
||||
garbageSent: widget.record.results.leaderboard[greenSidePlayer].stats.garbageSent,
|
||||
garbageReceived: widget.record.results.leaderboard[greenSidePlayer].stats.garbageReceived,
|
||||
kills: widget.record.results.leaderboard[greenSidePlayer].stats.kills,
|
||||
altitude: widget.record.results.leaderboard[greenSidePlayer].stats.altitude,
|
||||
rank: widget.record.results.leaderboard[greenSidePlayer].stats.rank
|
||||
),
|
||||
BetaLeagueStats(
|
||||
apm: apmMultipliedByWeights[1]/totalTime.inMilliseconds,
|
||||
pps: ppsMultipliedByWeights[1]/totalTime.inMilliseconds,
|
||||
vs: vsMultipliedByWeights[1]/totalTime.inMilliseconds,
|
||||
garbageSent: widget.record.results.leaderboard[redSidePlayer].stats.garbageSent,
|
||||
garbageReceived: widget.record.results.leaderboard[redSidePlayer].stats.garbageReceived,
|
||||
kills: widget.record.results.leaderboard[redSidePlayer].stats.kills,
|
||||
altitude: widget.record.results.leaderboard[redSidePlayer].stats.altitude,
|
||||
rank: widget.record.results.leaderboard[redSidePlayer].stats.rank
|
||||
),
|
||||
];
|
||||
initPlayerWon = widget.record.results.leaderboard[greenSidePlayer].wins > widget.record.results.leaderboard[redSidePlayer].wins;
|
||||
if (!kIsWeb && !Platform.isAndroid && !Platform.isIOS){
|
||||
windowManager.getTitle().then((value) => oldWindowTitle = value);
|
||||
windowManager.setTitle("Tetra Stats: ${widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).username.toUpperCase()} ${t.vs} ${widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).username.toUpperCase()} ${t.inTLmatch} ${timestamp(widget.record.timestamp)}");
|
||||
windowManager.setTitle("Tetra Stats: ${widget.record.results.leaderboard[greenSidePlayer].username.toUpperCase()} ${t.vs} ${widget.record.results.leaderboard[redSidePlayer].username.toUpperCase()} ${t.inTLmatch} ${widget.record.gamemode} ${timestamp(widget.record.ts)}");
|
||||
}
|
||||
super.initState();
|
||||
}
|
||||
|
@ -62,45 +110,17 @@ class TlMatchResultState extends State<TlMatchResultView> {
|
|||
|
||||
Widget buildComparison(double width, bool showMobileSelector){
|
||||
bool bigScreen = width >= 768;
|
||||
if (roundSelector.isNegative){
|
||||
time = totalTime;
|
||||
readableTime = !time.isNegative ? "${t.matchLength}: ${time.inMinutes}:${secs.format(time.inMicroseconds /1000000 % 60)}" : "${t.matchLength}: ---";
|
||||
}else{
|
||||
time = roundLengths[roundSelector];
|
||||
int alive = widget.record.results.rounds[roundSelector].indexWhere((element) => element.alive);
|
||||
readableTime = "${t.roundLength}: ${!time.isNegative ? "${time.inMinutes}:${secs.format(time.inMicroseconds /1000000 % 60)}" : "---"}\n${t.winner}: ${alive == -1 ? "idk" : widget.record.results.rounds[roundSelector][alive].username}";
|
||||
}
|
||||
return SizedBox(
|
||||
width: width,
|
||||
child: FutureBuilder(future: replayData, builder: (context, snapshot){
|
||||
late Duration time;
|
||||
late String readableTime;
|
||||
late String reason;
|
||||
timeWeightedStatsAvaliable = true;
|
||||
if (snapshot.connectionState != ConnectionState.done) return const LinearProgressIndicator();
|
||||
if (!snapshot.hasError){
|
||||
if (rounds.indexWhere((element) => element.value == -2) == -1) rounds.insert(1, DropdownMenuItem(value: -2, child: Text(t.timeWeightedmatch)));
|
||||
greenSidePlayer = snapshot.data!.endcontext.indexWhere((element) => element.userId == widget.initPlayerId);
|
||||
redSidePlayer = snapshot.data!.endcontext.indexWhere((element) => element.userId != widget.initPlayerId);
|
||||
if (roundSelector.isNegative){
|
||||
time = framesToTime(snapshot.data!.totalLength);
|
||||
readableTime = "${t.matchLength}: ${time.inMinutes}:${secs.format(time.inMicroseconds /1000000 % 60)}";
|
||||
}else{
|
||||
time = framesToTime(snapshot.data!.roundLengths[roundSelector]);
|
||||
readableTime = "${t.roundLength}: ${time.inMinutes}:${secs.format(time.inMicroseconds /1000000 % 60)}\n${t.winner}: ${snapshot.data!.roundWinners[roundSelector][1]}";
|
||||
}
|
||||
}else{
|
||||
switch (snapshot.error.runtimeType){
|
||||
case ReplayNotAvalable:
|
||||
reason = t.matchIsTooOld;
|
||||
break;
|
||||
case SzyNotFound:
|
||||
reason = t.matchIsTooOld;
|
||||
break;
|
||||
case SzyForbidden:
|
||||
reason = t.errors.replayRejected;
|
||||
break;
|
||||
case SzyTooManyRequests:
|
||||
reason = t.errors.tooManyRequests;
|
||||
break;
|
||||
default:
|
||||
reason = snapshot.error.toString();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return NestedScrollView(
|
||||
child: NestedScrollView(
|
||||
headerSliverBuilder: (context, value) {
|
||||
return [
|
||||
SliverToBoxAdapter(
|
||||
|
@ -117,15 +137,15 @@ class TlMatchResultState extends State<TlMatchResultView> {
|
|||
colors: const [Colors.green, Colors.transparent],
|
||||
begin: Alignment.bottomCenter,
|
||||
end: Alignment.topCenter,
|
||||
stops: [0.0, widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).success ? 0.4 : 0.0],
|
||||
stops: [0.0, initPlayerWon ? 0.4 : 0.0],
|
||||
)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(8, 0, 8, 0),
|
||||
child: Column(children: [
|
||||
Text(widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).username, style: bigScreen ? const TextStyle(
|
||||
Text(widget.record.results.leaderboard[greenSidePlayer].username, style: bigScreen ? const TextStyle(
|
||||
fontFamily: "Eurostile Round Extended",
|
||||
fontSize: 28) : const TextStyle()),
|
||||
Text(widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).points.toString(), style: const TextStyle(
|
||||
Text(widget.record.results.leaderboard[greenSidePlayer].wins.toString(), style: const TextStyle(
|
||||
fontFamily: "Eurostile Round Extended",
|
||||
fontSize: 42))
|
||||
]),
|
||||
|
@ -143,15 +163,15 @@ class TlMatchResultState extends State<TlMatchResultView> {
|
|||
colors: const [Colors.red, Colors.transparent],
|
||||
begin: Alignment.bottomCenter,
|
||||
end: Alignment.topCenter,
|
||||
stops: [0.0, widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).success ? 0.4 : 0.0],
|
||||
stops: [0.0, !initPlayerWon ? 0.4 : 0.0],
|
||||
)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(8, 0, 8, 0),
|
||||
child: Column(children: [
|
||||
Text(widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).username, style: bigScreen ? const TextStyle(
|
||||
Text(widget.record.results.leaderboard[redSidePlayer].username, style: bigScreen ? const TextStyle(
|
||||
fontFamily: "Eurostile Round Extended",
|
||||
fontSize: 28) : const TextStyle()),
|
||||
Text(widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).points.toString(), style: const TextStyle(
|
||||
Text(widget.record.results.leaderboard[redSidePlayer].wins.toString(), style: const TextStyle(
|
||||
fontFamily: "Eurostile Round Extended",
|
||||
fontSize: 42))
|
||||
]),
|
||||
|
@ -179,10 +199,10 @@ class TlMatchResultState extends State<TlMatchResultView> {
|
|||
),
|
||||
),
|
||||
),
|
||||
if (widget.record.ownId == widget.record.replayId && showMobileSelector) SliverToBoxAdapter(
|
||||
if (widget.record.id == widget.record.replayID && showMobileSelector) SliverToBoxAdapter(
|
||||
child: Center(child: Text(t.p1nkl0bst3rAlert, textAlign: TextAlign.center)),
|
||||
),
|
||||
if (showMobileSelector) SliverToBoxAdapter(child: Center(child: Text(snapshot.hasError ? reason : readableTime, textAlign: TextAlign.center))),
|
||||
if (showMobileSelector) SliverToBoxAdapter(child: Center(child: Text(readableTime, textAlign: TextAlign.center))),
|
||||
const SliverToBoxAdapter(
|
||||
child: Divider(),
|
||||
)
|
||||
|
@ -194,106 +214,37 @@ class TlMatchResultState extends State<TlMatchResultView> {
|
|||
children: [
|
||||
CompareThingy(
|
||||
label: "APM",
|
||||
greenSide: (roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[greenSidePlayer].apm :
|
||||
roundSelector.isNegative ? widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).secondary : widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).secondaryTracking[roundSelector],
|
||||
redSide: (roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[redSidePlayer].apm :
|
||||
roundSelector == -1 ? widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).secondary : widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).secondaryTracking[roundSelector],
|
||||
greenSide: roundSelector == -2 ? timeWeightedStats[0].apm :
|
||||
roundSelector.isNegative ? widget.record.results.leaderboard[greenSidePlayer].stats.apm : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id == widget.initPlayerId).stats.apm,
|
||||
redSide: roundSelector == -2 ? timeWeightedStats[1].apm :
|
||||
roundSelector == -1 ? widget.record.results.leaderboard[redSidePlayer].stats.apm : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id != widget.initPlayerId).stats.apm,
|
||||
fractionDigits: 2,
|
||||
higherIsBetter: true,
|
||||
),
|
||||
CompareThingy(
|
||||
label: "PPS",
|
||||
greenSide: (roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[greenSidePlayer].pps:
|
||||
roundSelector.isNegative ? widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).tertiary : widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).tertiaryTracking[roundSelector],
|
||||
redSide: (roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[redSidePlayer].pps :
|
||||
roundSelector.isNegative ? widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).tertiary: widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).tertiaryTracking[roundSelector],
|
||||
greenSide: roundSelector == -2 ? timeWeightedStats[0].pps :
|
||||
roundSelector.isNegative ? widget.record.results.leaderboard[greenSidePlayer].stats.pps : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id == widget.initPlayerId).stats.pps,
|
||||
redSide: roundSelector == -2 ? timeWeightedStats[1].pps :
|
||||
roundSelector == -1 ? widget.record.results.leaderboard[redSidePlayer].stats.pps : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id != widget.initPlayerId).stats.pps,
|
||||
fractionDigits: 2,
|
||||
higherIsBetter: true,
|
||||
),
|
||||
CompareThingy(
|
||||
label: "VS",
|
||||
greenSide: (roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[greenSidePlayer].vs :
|
||||
roundSelector.isNegative ? widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).extra : widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).extraTracking[roundSelector],
|
||||
redSide: (roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[redSidePlayer].vs :
|
||||
roundSelector.isNegative ? widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).extra : widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).extraTracking[roundSelector],
|
||||
greenSide: roundSelector == -2 ? timeWeightedStats[0].vs :
|
||||
roundSelector.isNegative ? widget.record.results.leaderboard[greenSidePlayer].stats.vs : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id == widget.initPlayerId).stats.vs,
|
||||
redSide: roundSelector == -2 ? timeWeightedStats[1].vs :
|
||||
roundSelector == -1 ? widget.record.results.leaderboard[redSidePlayer].stats.vs : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id != widget.initPlayerId).stats.vs,
|
||||
fractionDigits: 2,
|
||||
higherIsBetter: true,
|
||||
),
|
||||
if (snapshot.hasData) Column(children: [
|
||||
CompareThingy(greenSide: roundSelector.isNegative ? snapshot.data!.totalStats[greenSidePlayer].inputs : snapshot.data!.stats[roundSelector][greenSidePlayer].inputs,
|
||||
redSide: roundSelector.isNegative ? snapshot.data!.totalStats[redSidePlayer].inputs : snapshot.data!.stats[roundSelector][redSidePlayer].inputs,
|
||||
label: "Inputs", higherIsBetter: true),
|
||||
CompareThingy(greenSide: roundSelector.isNegative ? snapshot.data!.totalStats[greenSidePlayer].piecesPlaced : snapshot.data!.stats[roundSelector][greenSidePlayer].piecesPlaced,
|
||||
redSide: roundSelector.isNegative ? snapshot.data!.totalStats[redSidePlayer].piecesPlaced : snapshot.data!.stats[roundSelector][redSidePlayer].piecesPlaced,
|
||||
label: "Pieces Placed", higherIsBetter: true),
|
||||
CompareThingy(greenSide: (roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[greenSidePlayer].kpp :
|
||||
roundSelector.isNegative ? snapshot.data!.totalStats[greenSidePlayer].kpp : snapshot.data!.stats[roundSelector][greenSidePlayer].kpp,
|
||||
redSide: (roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[redSidePlayer].kpp :
|
||||
roundSelector.isNegative ? snapshot.data!.totalStats[redSidePlayer].kpp : snapshot.data!.stats[roundSelector][redSidePlayer].kpp,
|
||||
label: "KPP", higherIsBetter: false, fractionDigits: 2,),
|
||||
CompareThingy(greenSide: (roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[greenSidePlayer].kps :
|
||||
roundSelector.isNegative ? snapshot.data!.totalStats[greenSidePlayer].kps : snapshot.data!.stats[roundSelector][greenSidePlayer].kps,
|
||||
redSide: (roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[redSidePlayer].kps :
|
||||
roundSelector.isNegative ? snapshot.data!.totalStats[redSidePlayer].kps : snapshot.data!.stats[roundSelector][redSidePlayer].kps,
|
||||
label: "KPS", higherIsBetter: true, fractionDigits: 2,),
|
||||
CompareThingy(greenSide: roundSelector.isNegative ? snapshot.data!.totalStats[greenSidePlayer].linesCleared : snapshot.data!.stats[roundSelector][greenSidePlayer].linesCleared,
|
||||
redSide: roundSelector.isNegative ? snapshot.data!.totalStats[redSidePlayer].linesCleared : snapshot.data!.stats[roundSelector][redSidePlayer].linesCleared,
|
||||
label: "Lines Cleared", higherIsBetter: true),
|
||||
CompareThingy(greenSide: roundSelector.isNegative ? snapshot.data!.totalStats[greenSidePlayer].score : snapshot.data!.stats[roundSelector][greenSidePlayer].score,
|
||||
redSide: roundSelector.isNegative ? snapshot.data!.totalStats[redSidePlayer].score : snapshot.data!.stats[roundSelector][redSidePlayer].score,
|
||||
label: "Score", higherIsBetter: true),
|
||||
CompareThingy(greenSide: (roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[greenSidePlayer].spp :
|
||||
roundSelector.isNegative ? snapshot.data!.totalStats[greenSidePlayer].spp : snapshot.data!.stats[roundSelector][greenSidePlayer].spp,
|
||||
redSide: (roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[redSidePlayer].spp :
|
||||
roundSelector.isNegative ? snapshot.data!.totalStats[redSidePlayer].spp : snapshot.data!.stats[roundSelector][redSidePlayer].spp,
|
||||
label: "SPP", higherIsBetter: true, fractionDigits: 2,),
|
||||
CompareThingy(greenSide: roundSelector.isNegative ? snapshot.data!.totalStats[greenSidePlayer].finessePercentage * 100 : snapshot.data!.stats[roundSelector][greenSidePlayer].finessePercentage * 100,
|
||||
redSide: roundSelector.isNegative ? snapshot.data!.totalStats[redSidePlayer].finessePercentage * 100 : snapshot.data!.stats[roundSelector][redSidePlayer].finessePercentage * 100,
|
||||
label: "Finnese", postfix: "%", fractionDigits: 2, higherIsBetter: true),
|
||||
CompareThingy(greenSide: roundSelector.isNegative ? snapshot.data!.totalStats[greenSidePlayer].topSpike : snapshot.data!.stats[roundSelector][greenSidePlayer].topSpike,
|
||||
redSide: roundSelector.isNegative ? snapshot.data!.totalStats[redSidePlayer].topSpike : snapshot.data!.stats[roundSelector][redSidePlayer].topSpike,
|
||||
label: "Best Spike", higherIsBetter: true),
|
||||
CompareThingy(greenSide: roundSelector.isNegative ? snapshot.data!.totalStats[greenSidePlayer].topCombo : snapshot.data!.stats[roundSelector][greenSidePlayer].topCombo,
|
||||
redSide: roundSelector.isNegative ? snapshot.data!.totalStats[redSidePlayer].topCombo : snapshot.data!.stats[roundSelector][redSidePlayer].topCombo,
|
||||
label: "Best Combo", higherIsBetter: true),
|
||||
CompareThingy(greenSide: roundSelector.isNegative ? snapshot.data!.totalStats[greenSidePlayer].topBtB : snapshot.data!.stats[roundSelector][greenSidePlayer].topBtB,
|
||||
redSide: roundSelector.isNegative ? snapshot.data!.totalStats[redSidePlayer].topBtB : snapshot.data!.stats[roundSelector][redSidePlayer].topBtB,
|
||||
label: "Best BtB", higherIsBetter: true),
|
||||
const Divider(),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 16),
|
||||
child: Text("Garbage", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)),
|
||||
),
|
||||
CompareThingy(greenSide: roundSelector.isNegative ? snapshot.data!.totalStats[greenSidePlayer].garbage.sent : snapshot.data!.stats[roundSelector][greenSidePlayer].garbage.sent,
|
||||
redSide: roundSelector.isNegative ? snapshot.data!.totalStats[redSidePlayer].garbage.sent : snapshot.data!.stats[roundSelector][redSidePlayer].garbage.sent,
|
||||
label: "Sent", higherIsBetter: true),
|
||||
CompareThingy(greenSide: roundSelector.isNegative ? snapshot.data!.totalStats[greenSidePlayer].garbage.recived : snapshot.data!.stats[roundSelector][greenSidePlayer].garbage.recived,
|
||||
redSide: roundSelector.isNegative ? snapshot.data!.totalStats[redSidePlayer].garbage.recived : snapshot.data!.stats[roundSelector][redSidePlayer].garbage.recived,
|
||||
label: "Received", higherIsBetter: true),
|
||||
CompareThingy(greenSide: roundSelector.isNegative ? snapshot.data!.totalStats[greenSidePlayer].garbage.attack : snapshot.data!.stats[roundSelector][greenSidePlayer].garbage.attack,
|
||||
redSide: roundSelector.isNegative ? snapshot.data!.totalStats[redSidePlayer].garbage.attack : snapshot.data!.stats[roundSelector][redSidePlayer].garbage.attack,
|
||||
label: "Attack", higherIsBetter: true),
|
||||
CompareThingy(greenSide: roundSelector.isNegative ? snapshot.data!.totalStats[greenSidePlayer].garbage.cleared : snapshot.data!.stats[roundSelector][greenSidePlayer].garbage.cleared,
|
||||
redSide: roundSelector.isNegative ? snapshot.data!.totalStats[redSidePlayer].garbage.cleared : snapshot.data!.stats[roundSelector][redSidePlayer].garbage.cleared,
|
||||
label: "Cleared", higherIsBetter: true),
|
||||
const Divider(),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 16),
|
||||
child: Text("Line Clears", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)),
|
||||
),
|
||||
CompareThingy(greenSide: roundSelector.isNegative ? snapshot.data!.totalStats[greenSidePlayer].clears.allClears : snapshot.data!.stats[roundSelector][greenSidePlayer].clears.allClears,
|
||||
redSide: roundSelector.isNegative ? snapshot.data!.totalStats[redSidePlayer].clears.allClears : snapshot.data!.stats[roundSelector][redSidePlayer].clears.allClears,
|
||||
label: "PC", higherIsBetter: true),
|
||||
CompareThingy(greenSide: roundSelector.isNegative ? snapshot.data!.totalStats[greenSidePlayer].tspins : snapshot.data!.stats[roundSelector][greenSidePlayer].tspins,
|
||||
redSide: roundSelector.isNegative ? snapshot.data!.totalStats[redSidePlayer].tspins : snapshot.data!.stats[roundSelector][redSidePlayer].tspins,
|
||||
label: "T-spins", higherIsBetter: true),
|
||||
CompareThingy(greenSide: roundSelector.isNegative ? snapshot.data!.totalStats[greenSidePlayer].clears.quads : snapshot.data!.stats[roundSelector][greenSidePlayer].clears.quads,
|
||||
redSide: roundSelector.isNegative ? snapshot.data!.totalStats[redSidePlayer].clears.quads : snapshot.data!.stats[roundSelector][redSidePlayer].clears.quads,
|
||||
label: "Quads", higherIsBetter: true),
|
||||
],),
|
||||
],
|
||||
),
|
||||
const Divider(),
|
||||
if (widget.record.gamemode == "league") CompareThingy(greenSide: roundSelector.isNegative ? widget.record.results.leaderboard[greenSidePlayer].stats.garbageSent : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id == widget.initPlayerId).stats.garbageSent,
|
||||
redSide: roundSelector.isNegative ? widget.record.results.leaderboard[redSidePlayer].stats.garbageSent : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id != widget.initPlayerId).stats.garbageSent,
|
||||
label: "Sent", higherIsBetter: true),
|
||||
if (widget.record.gamemode == "league") CompareThingy(greenSide: roundSelector.isNegative ? widget.record.results.leaderboard[greenSidePlayer].stats.garbageReceived : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id == widget.initPlayerId).stats.garbageReceived,
|
||||
redSide: roundSelector.isNegative ? widget.record.results.leaderboard[redSidePlayer].stats.garbageReceived : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id != widget.initPlayerId).stats.garbageReceived,
|
||||
label: "Received", higherIsBetter: true), const Divider(),
|
||||
Column(
|
||||
children: [
|
||||
Padding(
|
||||
|
@ -305,180 +256,179 @@ class TlMatchResultState extends State<TlMatchResultView> {
|
|||
),
|
||||
CompareThingy(
|
||||
label: "APP",
|
||||
greenSide: (roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[greenSidePlayer].nerdStats.app :
|
||||
roundSelector.isNegative ? widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).nerdStats.app : widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).nerdStatsTracking[roundSelector].app,
|
||||
redSide: (roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[redSidePlayer].nerdStats.app :
|
||||
roundSelector.isNegative ? widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).nerdStats.app : widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).nerdStatsTracking[roundSelector].app,
|
||||
greenSide: roundSelector == -2 ? timeWeightedStats[0].nerdStats.app :
|
||||
roundSelector.isNegative ? widget.record.results.leaderboard[greenSidePlayer].stats.nerdStats.app : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id == widget.initPlayerId).stats.nerdStats.app,
|
||||
redSide: roundSelector == -2 ? timeWeightedStats[1].nerdStats.app :
|
||||
roundSelector == -1 ? widget.record.results.leaderboard[redSidePlayer].stats.nerdStats.app : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id != widget.initPlayerId).stats.nerdStats.app,
|
||||
fractionDigits: 3,
|
||||
higherIsBetter: true,
|
||||
),
|
||||
CompareThingy(
|
||||
label: "VS/APM",
|
||||
greenSide: (roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[greenSidePlayer].nerdStats.vsapm :
|
||||
roundSelector.isNegative ? widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).nerdStats.vsapm : widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).nerdStatsTracking[roundSelector].vsapm,
|
||||
redSide: (roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[redSidePlayer].nerdStats.vsapm :
|
||||
roundSelector.isNegative ? widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).nerdStats.vsapm : widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).nerdStatsTracking[roundSelector].vsapm,
|
||||
greenSide: roundSelector == -2 ? timeWeightedStats[0].nerdStats.vsapm :
|
||||
roundSelector.isNegative ? widget.record.results.leaderboard[greenSidePlayer].stats.nerdStats.vsapm : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id == widget.initPlayerId).stats.nerdStats.vsapm,
|
||||
redSide: roundSelector == -2 ? timeWeightedStats[1].nerdStats.vsapm :
|
||||
roundSelector == -1 ? widget.record.results.leaderboard[redSidePlayer].stats.nerdStats.vsapm : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id != widget.initPlayerId).stats.nerdStats.vsapm,
|
||||
fractionDigits: 3,
|
||||
higherIsBetter: true,
|
||||
),
|
||||
CompareThingy(
|
||||
label: "DS/S",
|
||||
greenSide: (roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[greenSidePlayer].nerdStats.dss :
|
||||
roundSelector.isNegative ? widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).nerdStats.dss : widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).nerdStatsTracking[roundSelector].dss,
|
||||
redSide: (roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[redSidePlayer].nerdStats.dss :
|
||||
roundSelector.isNegative ? widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).nerdStats.dss : widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).nerdStatsTracking[roundSelector].dss,
|
||||
greenSide: roundSelector == -2 ? timeWeightedStats[0].nerdStats.dss :
|
||||
roundSelector.isNegative ? widget.record.results.leaderboard[greenSidePlayer].stats.nerdStats.dss : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id == widget.initPlayerId).stats.nerdStats.dss,
|
||||
redSide: roundSelector == -2 ? timeWeightedStats[1].nerdStats.dss :
|
||||
roundSelector == -1 ? widget.record.results.leaderboard[redSidePlayer].stats.nerdStats.dss : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id != widget.initPlayerId).stats.nerdStats.dss,
|
||||
fractionDigits: 3,
|
||||
higherIsBetter: true,
|
||||
),
|
||||
CompareThingy(
|
||||
label: "DS/P",
|
||||
greenSide: (roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[greenSidePlayer].nerdStats.dsp :
|
||||
roundSelector.isNegative ? widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).nerdStats.dsp : widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).nerdStatsTracking[roundSelector].dsp,
|
||||
redSide: (roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[redSidePlayer].nerdStats.dsp :
|
||||
roundSelector.isNegative ? widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).nerdStats.dsp : widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).nerdStatsTracking[roundSelector].dsp,
|
||||
greenSide: roundSelector == -2 ? timeWeightedStats[0].nerdStats.dsp :
|
||||
roundSelector.isNegative ? widget.record.results.leaderboard[greenSidePlayer].stats.nerdStats.dsp : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id == widget.initPlayerId).stats.nerdStats.dsp,
|
||||
redSide: roundSelector == -2 ? timeWeightedStats[1].nerdStats.dsp :
|
||||
roundSelector == -1 ? widget.record.results.leaderboard[redSidePlayer].stats.nerdStats.dsp : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id != widget.initPlayerId).stats.nerdStats.dsp,
|
||||
fractionDigits: 3,
|
||||
higherIsBetter: true,
|
||||
),
|
||||
CompareThingy(
|
||||
label: "APP + DS/P",
|
||||
greenSide: (roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[greenSidePlayer].nerdStats.appdsp :
|
||||
roundSelector.isNegative ? widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).nerdStats.appdsp : widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).nerdStatsTracking[roundSelector].appdsp,
|
||||
redSide: (roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[redSidePlayer].nerdStats.appdsp :
|
||||
roundSelector.isNegative ? widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).nerdStats.appdsp : widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).nerdStatsTracking[roundSelector].appdsp,
|
||||
greenSide: roundSelector == -2 ? timeWeightedStats[0].nerdStats.appdsp :
|
||||
roundSelector.isNegative ? widget.record.results.leaderboard[greenSidePlayer].stats.nerdStats.appdsp : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id == widget.initPlayerId).stats.nerdStats.appdsp,
|
||||
redSide: roundSelector == -2 ? timeWeightedStats[1].nerdStats.appdsp :
|
||||
roundSelector == -1 ? widget.record.results.leaderboard[redSidePlayer].stats.nerdStats.appdsp : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id != widget.initPlayerId).stats.nerdStats.appdsp,
|
||||
fractionDigits: 3,
|
||||
higherIsBetter: true,
|
||||
),
|
||||
CompareThingy(
|
||||
label: t.statCellNum.cheese.replaceAll(RegExp(r'\n'), " "),
|
||||
greenSide: (roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[greenSidePlayer].nerdStats.cheese :
|
||||
roundSelector.isNegative ? widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).nerdStats.cheese : widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).nerdStatsTracking[roundSelector].cheese,
|
||||
redSide: (roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[redSidePlayer].nerdStats.cheese :
|
||||
roundSelector.isNegative ? widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).nerdStats.cheese : widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).nerdStatsTracking[roundSelector].cheese,
|
||||
greenSide: roundSelector == -2 ? timeWeightedStats[0].nerdStats.cheese :
|
||||
roundSelector.isNegative ? widget.record.results.leaderboard[greenSidePlayer].stats.nerdStats.cheese : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id == widget.initPlayerId).stats.nerdStats.cheese,
|
||||
redSide: roundSelector == -2 ? timeWeightedStats[1].nerdStats.cheese :
|
||||
roundSelector == -1 ? widget.record.results.leaderboard[redSidePlayer].stats.nerdStats.cheese : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id != widget.initPlayerId).stats.nerdStats.cheese,
|
||||
fractionDigits: 2,
|
||||
higherIsBetter: false,
|
||||
),
|
||||
CompareThingy(
|
||||
label: "Gb Eff.",
|
||||
greenSide: (roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[greenSidePlayer].nerdStats.gbe :
|
||||
roundSelector.isNegative ? widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).nerdStats.gbe : widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).nerdStatsTracking[roundSelector].gbe,
|
||||
redSide: (roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[redSidePlayer].nerdStats.gbe :
|
||||
roundSelector.isNegative ? widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).nerdStats.gbe : widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).nerdStatsTracking[roundSelector].gbe,
|
||||
greenSide: roundSelector == -2 ? timeWeightedStats[0].nerdStats.gbe :
|
||||
roundSelector.isNegative ? widget.record.results.leaderboard[greenSidePlayer].stats.nerdStats.gbe : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id == widget.initPlayerId).stats.nerdStats.gbe,
|
||||
redSide: roundSelector == -2 ? timeWeightedStats[1].nerdStats.gbe :
|
||||
roundSelector == -1 ? widget.record.results.leaderboard[redSidePlayer].stats.nerdStats.gbe : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id != widget.initPlayerId).stats.nerdStats.gbe,
|
||||
fractionDigits: 3,
|
||||
higherIsBetter: true,
|
||||
),
|
||||
CompareThingy(
|
||||
label: "wAPP",
|
||||
greenSide: (roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[greenSidePlayer].nerdStats.nyaapp :
|
||||
roundSelector.isNegative ? widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).nerdStats.nyaapp : widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).nerdStatsTracking[roundSelector].nyaapp,
|
||||
redSide: (roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[redSidePlayer].nerdStats.nyaapp :
|
||||
roundSelector.isNegative ? widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).nerdStats.nyaapp : widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).nerdStatsTracking[roundSelector].nyaapp,
|
||||
greenSide: roundSelector == -2 ? timeWeightedStats[0].nerdStats.nyaapp :
|
||||
roundSelector.isNegative ? widget.record.results.leaderboard[greenSidePlayer].stats.nerdStats.nyaapp : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id == widget.initPlayerId).stats.nerdStats.nyaapp,
|
||||
redSide: roundSelector == -2 ? timeWeightedStats[1].nerdStats.nyaapp :
|
||||
roundSelector == -1 ? widget.record.results.leaderboard[redSidePlayer].stats.nerdStats.nyaapp : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id != widget.initPlayerId).stats.nerdStats.nyaapp,
|
||||
fractionDigits: 3,
|
||||
higherIsBetter: true,
|
||||
),
|
||||
CompareThingy(
|
||||
label: "Area",
|
||||
greenSide: (roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[greenSidePlayer].nerdStats.area :
|
||||
roundSelector.isNegative ? widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).nerdStats.area : widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).nerdStatsTracking[roundSelector].area,
|
||||
redSide: (roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[redSidePlayer].nerdStats.area :
|
||||
roundSelector.isNegative ? widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).nerdStats.area : widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).nerdStatsTracking[roundSelector].area,
|
||||
greenSide: roundSelector == -2 ? timeWeightedStats[0].nerdStats.area :
|
||||
roundSelector.isNegative ? widget.record.results.leaderboard[greenSidePlayer].stats.nerdStats.area : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id == widget.initPlayerId).stats.nerdStats.area,
|
||||
redSide: roundSelector == -2 ? timeWeightedStats[1].nerdStats.area :
|
||||
roundSelector == -1 ? widget.record.results.leaderboard[redSidePlayer].stats.nerdStats.area : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id != widget.initPlayerId).stats.nerdStats.area,
|
||||
fractionDigits: 2,
|
||||
higherIsBetter: true,
|
||||
),
|
||||
CompareThingy(
|
||||
label: t.statCellNum.estOfTRShort,
|
||||
greenSide: (roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[greenSidePlayer].estTr.esttr :
|
||||
roundSelector.isNegative ? widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).estTr.esttr : widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).estTrTracking[roundSelector].esttr,
|
||||
redSide: (roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[redSidePlayer].estTr.esttr :
|
||||
roundSelector.isNegative ? widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).estTr.esttr : widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).estTrTracking[roundSelector].esttr,
|
||||
greenSide: roundSelector == -2 ? timeWeightedStats[0].estTr.esttr :
|
||||
roundSelector.isNegative ? widget.record.results.leaderboard[greenSidePlayer].stats.estTr.esttr : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id == widget.initPlayerId).stats.estTr.esttr,
|
||||
redSide: roundSelector == -2 ? timeWeightedStats[1].estTr.esttr :
|
||||
roundSelector == -1 ? widget.record.results.leaderboard[redSidePlayer].stats.estTr.esttr : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id != widget.initPlayerId).stats.estTr.esttr,
|
||||
fractionDigits: 2,
|
||||
higherIsBetter: true,
|
||||
),
|
||||
CompareThingy(
|
||||
label: "Opener",
|
||||
greenSide: (roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[greenSidePlayer].playstyle.opener :
|
||||
roundSelector.isNegative ? widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).playstyle.opener : widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).playstyleTracking[roundSelector].opener,
|
||||
redSide: (roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[redSidePlayer].playstyle.opener :
|
||||
roundSelector.isNegative ? widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).playstyle.opener : widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).playstyleTracking[roundSelector].opener,
|
||||
greenSide: roundSelector == -2 ? timeWeightedStats[0].playstyle.opener :
|
||||
roundSelector.isNegative ? widget.record.results.leaderboard[greenSidePlayer].stats.playstyle.opener : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id == widget.initPlayerId).stats.playstyle.opener,
|
||||
redSide: roundSelector == -2 ? timeWeightedStats[1].playstyle.opener :
|
||||
roundSelector == -1 ? widget.record.results.leaderboard[redSidePlayer].stats.playstyle.opener : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id != widget.initPlayerId).stats.playstyle.opener,
|
||||
fractionDigits: 3,
|
||||
higherIsBetter: true,
|
||||
),
|
||||
CompareThingy(
|
||||
label: "Plonk",
|
||||
greenSide: (roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[greenSidePlayer].playstyle.plonk :
|
||||
roundSelector.isNegative ? widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).playstyle.plonk : widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).playstyleTracking[roundSelector].plonk,
|
||||
redSide: (roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[redSidePlayer].playstyle.plonk :
|
||||
roundSelector.isNegative ? widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).playstyle.plonk : widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).playstyleTracking[roundSelector].plonk,
|
||||
greenSide: roundSelector == -2 ? timeWeightedStats[0].playstyle.plonk :
|
||||
roundSelector.isNegative ? widget.record.results.leaderboard[greenSidePlayer].stats.playstyle.opener : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id == widget.initPlayerId).stats.playstyle.plonk,
|
||||
redSide: roundSelector == -2 ? timeWeightedStats[1].playstyle.plonk :
|
||||
roundSelector == -1 ? widget.record.results.leaderboard[redSidePlayer].stats.playstyle.opener : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id != widget.initPlayerId).stats.playstyle.plonk,
|
||||
fractionDigits: 3,
|
||||
higherIsBetter: true,
|
||||
),
|
||||
CompareThingy(
|
||||
label: "Stride",
|
||||
greenSide: (roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[greenSidePlayer].playstyle.stride :
|
||||
roundSelector.isNegative ? widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).playstyle.stride : widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).playstyleTracking[roundSelector].stride,
|
||||
redSide: (roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[redSidePlayer].playstyle.stride :
|
||||
roundSelector.isNegative ? widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).playstyle.stride : widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).playstyleTracking[roundSelector].stride,
|
||||
greenSide: roundSelector == -2 ? timeWeightedStats[0].playstyle.stride :
|
||||
roundSelector.isNegative ? widget.record.results.leaderboard[greenSidePlayer].stats.playstyle.stride : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id == widget.initPlayerId).stats.playstyle.stride,
|
||||
redSide: roundSelector == -2 ? timeWeightedStats[1].playstyle.stride :
|
||||
roundSelector == -1 ? widget.record.results.leaderboard[redSidePlayer].stats.playstyle.stride : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id != widget.initPlayerId).stats.playstyle.stride,
|
||||
fractionDigits: 3,
|
||||
higherIsBetter: true,
|
||||
),
|
||||
CompareThingy(
|
||||
label: "Inf. DS",
|
||||
greenSide: (roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[greenSidePlayer].playstyle.infds :
|
||||
roundSelector.isNegative ? widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).playstyle.infds : widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).playstyleTracking[roundSelector].infds,
|
||||
redSide: (roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[redSidePlayer].playstyle.infds :
|
||||
roundSelector.isNegative ? widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).playstyle.infds : widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).playstyleTracking[roundSelector].infds,
|
||||
greenSide: roundSelector == -2 ? timeWeightedStats[0].playstyle.infds :
|
||||
roundSelector.isNegative ? widget.record.results.leaderboard[greenSidePlayer].stats.playstyle.infds : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id == widget.initPlayerId).stats.playstyle.infds,
|
||||
redSide: roundSelector == -2 ? timeWeightedStats[1].playstyle.infds :
|
||||
roundSelector == -1 ? widget.record.results.leaderboard[redSidePlayer].stats.playstyle.infds : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id != widget.initPlayerId).stats.playstyle.infds,
|
||||
fractionDigits: 3,
|
||||
higherIsBetter: true,
|
||||
),
|
||||
VsGraphs(
|
||||
(roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[greenSidePlayer].apm : roundSelector.isNegative ? widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).secondary : widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).secondaryTracking[roundSelector],
|
||||
(roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[greenSidePlayer].pps : roundSelector.isNegative ? widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).tertiary : widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).tertiaryTracking[roundSelector],
|
||||
(roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[greenSidePlayer].vs : roundSelector.isNegative ? widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).extra : widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).extraTracking[roundSelector],
|
||||
(roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[greenSidePlayer].nerdStats : roundSelector.isNegative ? widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).nerdStats : widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).nerdStatsTracking[roundSelector],
|
||||
(roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[greenSidePlayer].playstyle : roundSelector.isNegative ? widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).playstyle : widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).playstyleTracking[roundSelector],
|
||||
(roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[redSidePlayer].apm : roundSelector.isNegative ? widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).secondary : widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).secondaryTracking[roundSelector],
|
||||
(roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[redSidePlayer].pps : roundSelector.isNegative ? widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).tertiary : widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).tertiaryTracking[roundSelector],
|
||||
(roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[redSidePlayer].vs : roundSelector.isNegative ? widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).extra : widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).extraTracking[roundSelector],
|
||||
(roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[redSidePlayer].nerdStats : roundSelector.isNegative ? widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).nerdStats : widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).nerdStatsTracking[roundSelector],
|
||||
(roundSelector == -2 && snapshot.hasData) ? snapshot.data!.timeWeightedStats[redSidePlayer].playstyle : roundSelector.isNegative ? widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).playstyle : widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).playstyleTracking[roundSelector]
|
||||
roundSelector == -2 ? timeWeightedStats[0].apm : roundSelector.isNegative ? widget.record.results.leaderboard[greenSidePlayer].stats.apm : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id == widget.initPlayerId).stats.apm,
|
||||
roundSelector == -2 ? timeWeightedStats[0].pps : roundSelector.isNegative ? widget.record.results.leaderboard[greenSidePlayer].stats.pps : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id == widget.initPlayerId).stats.pps,
|
||||
roundSelector == -2 ? timeWeightedStats[0].vs : roundSelector.isNegative ? widget.record.results.leaderboard[greenSidePlayer].stats.vs : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id == widget.initPlayerId).stats.vs,
|
||||
roundSelector == -2 ? timeWeightedStats[0].nerdStats : roundSelector.isNegative ? widget.record.results.leaderboard[greenSidePlayer].stats.nerdStats : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id == widget.initPlayerId).stats.nerdStats,
|
||||
roundSelector == -2 ? timeWeightedStats[0].playstyle : roundSelector.isNegative ? widget.record.results.leaderboard[greenSidePlayer].stats.playstyle : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id == widget.initPlayerId).stats.playstyle,
|
||||
roundSelector == -2 ? timeWeightedStats[1].apm : roundSelector.isNegative ? widget.record.results.leaderboard[redSidePlayer].stats.apm : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id != widget.initPlayerId).stats.apm,
|
||||
roundSelector == -2 ? timeWeightedStats[1].pps : roundSelector.isNegative ? widget.record.results.leaderboard[redSidePlayer].stats.pps : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id != widget.initPlayerId).stats.pps,
|
||||
roundSelector == -2 ? timeWeightedStats[1].vs : roundSelector.isNegative ? widget.record.results.leaderboard[redSidePlayer].stats.vs : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id != widget.initPlayerId).stats.vs,
|
||||
roundSelector == -2 ? timeWeightedStats[1].nerdStats : roundSelector.isNegative ? widget.record.results.leaderboard[redSidePlayer].stats.nerdStats : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id != widget.initPlayerId).stats.nerdStats,
|
||||
roundSelector == -2 ? timeWeightedStats[1].playstyle : roundSelector.isNegative ? widget.record.results.leaderboard[redSidePlayer].stats.playstyle : widget.record.results.rounds[roundSelector].firstWhere((element) => element.id != widget.initPlayerId).stats.playstyle,
|
||||
)
|
||||
],
|
||||
),
|
||||
if (widget.record.ownId != widget.record.replayId) const Divider(),
|
||||
if (widget.record.ownId != widget.record.replayId) Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 16),
|
||||
child: Text("Handling",
|
||||
style: TextStyle(
|
||||
fontFamily: "Eurostile Round Extended",
|
||||
fontSize: bigScreen ? 42 : 28)),
|
||||
),
|
||||
CompareThingy(
|
||||
greenSide: widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).handling.das,
|
||||
redSide: widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).handling.das,
|
||||
label: "DAS", fractionDigits: 1, postfix: "F",
|
||||
higherIsBetter: false),
|
||||
CompareThingy(
|
||||
greenSide: widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).handling.arr,
|
||||
redSide: widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).handling.arr,
|
||||
label: "ARR", fractionDigits: 1, postfix: "F",
|
||||
higherIsBetter: false),
|
||||
CompareThingy(
|
||||
greenSide: widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).handling.sdf,
|
||||
redSide: widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).handling.sdf,
|
||||
label: "SDF", prefix: "x",
|
||||
higherIsBetter: true),
|
||||
CompareBoolThingy(
|
||||
greenSide: widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).handling.safeLock,
|
||||
redSide: widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).handling.safeLock,
|
||||
label: "Safe HD",
|
||||
trueIsBetter: true)
|
||||
],
|
||||
)
|
||||
// if (widget.record.ownId != widget.record.replayId) const Divider(),
|
||||
// if (widget.record.ownId != widget.record.replayId) Column(
|
||||
// children: [
|
||||
// Padding(
|
||||
// padding: const EdgeInsets.only(bottom: 16),
|
||||
// child: Text("Handling",
|
||||
// style: TextStyle(
|
||||
// fontFamily: "Eurostile Round Extended",
|
||||
// fontSize: bigScreen ? 42 : 28)),
|
||||
// ),
|
||||
// CompareThingy(
|
||||
// greenSide: widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).handling.das,
|
||||
// redSide: widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).handling.das,
|
||||
// label: "DAS", fractionDigits: 1, postfix: "F",
|
||||
// higherIsBetter: false),
|
||||
// CompareThingy(
|
||||
// greenSide: widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).handling.arr,
|
||||
// redSide: widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).handling.arr,
|
||||
// label: "ARR", fractionDigits: 1, postfix: "F",
|
||||
// higherIsBetter: false),
|
||||
// CompareThingy(
|
||||
// greenSide: widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).handling.sdf,
|
||||
// redSide: widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).handling.sdf,
|
||||
// label: "SDF", prefix: "x",
|
||||
// higherIsBetter: true),
|
||||
// CompareBoolThingy(
|
||||
// greenSide: widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).handling.safeLock,
|
||||
// redSide: widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).handling.safeLock,
|
||||
// label: "Safe HD",
|
||||
// trueIsBetter: true)
|
||||
// ],
|
||||
// )
|
||||
],
|
||||
)
|
||||
);
|
||||
}),
|
||||
])),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -494,76 +444,34 @@ class TlMatchResultState extends State<TlMatchResultView> {
|
|||
Wrap(
|
||||
alignment: WrapAlignment.spaceBetween,
|
||||
children: [
|
||||
FutureBuilder(future: replayData, builder: (context, snapshot) {
|
||||
switch(snapshot.connectionState){
|
||||
case ConnectionState.none:
|
||||
case ConnectionState.waiting:
|
||||
case ConnectionState.active:
|
||||
return const CircularProgressIndicator();
|
||||
case ConnectionState.done:
|
||||
if (!snapshot.hasError){
|
||||
var time = framesToTime(snapshot.data!.totalLength);
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(t.matchLength),
|
||||
RichText(
|
||||
text: TextSpan(
|
||||
text: "${time.inMinutes}:${NumberFormat("00", LocaleSettings.currentLocale.languageCode).format(time.inSeconds%60)}",
|
||||
style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28, fontWeight: FontWeight.w500, color: Colors.white),
|
||||
children: [TextSpan(text: ".${NumberFormat("000", LocaleSettings.currentLocale.languageCode).format(time.inMilliseconds%1000)}", style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, fontWeight: FontWeight.w100))]
|
||||
),
|
||||
)
|
||||
],);
|
||||
}else{
|
||||
String reason;
|
||||
switch (snapshot.error.runtimeType){
|
||||
case ReplayNotAvalable:
|
||||
reason = t.matchIsTooOld;
|
||||
break;
|
||||
case SzyNotFound:
|
||||
reason = t.matchIsTooOld;
|
||||
break;
|
||||
case SzyForbidden:
|
||||
reason = t.errors.replayRejected;
|
||||
break;
|
||||
case SzyTooManyRequests:
|
||||
reason = t.errors.tooManyRequests;
|
||||
break;
|
||||
default:
|
||||
reason = snapshot.error.toString();
|
||||
break;
|
||||
}
|
||||
timeWeightedStatsAvaliable = false;
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (widget.record.ownId != widget.record.replayId) Text("${t.replayIssue}: $reason"),
|
||||
if (widget.record.ownId == widget.record.replayId) Center(child: Text(t.p1nkl0bst3rAlert, textAlign: TextAlign.center)),
|
||||
if (widget.record.ownId != widget.record.replayId) RichText(
|
||||
text: const TextSpan(
|
||||
text: "-:--",
|
||||
style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28, fontWeight: FontWeight.w500, color: Colors.grey),
|
||||
children: [TextSpan(text: ".---", style: TextStyle(fontFamily: "Eurostile Round", fontSize: 14, fontWeight: FontWeight.w100))]
|
||||
),
|
||||
)
|
||||
],);
|
||||
}
|
||||
|
||||
}
|
||||
},),
|
||||
if (widget.record.ownId != widget.record.replayId) Column(
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(t.matchLength),
|
||||
RichText(
|
||||
text: !totalTime.isNegative ? TextSpan(
|
||||
text: "${totalTime.inMinutes}:${NumberFormat("00", LocaleSettings.currentLocale.languageCode).format(totalTime.inSeconds%60)}",
|
||||
style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28, fontWeight: FontWeight.w500, color: Colors.white),
|
||||
children: [TextSpan(text: ".${NumberFormat("000", LocaleSettings.currentLocale.languageCode).format(totalTime.inMilliseconds%1000)}", style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, fontWeight: FontWeight.w100))]
|
||||
) : const TextSpan(
|
||||
text: "-:--",
|
||||
style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28, fontWeight: FontWeight.w500, color: Colors.grey),
|
||||
children: [TextSpan(text: ".---", style: TextStyle(fontFamily: "Eurostile Round", fontSize: 14, fontWeight: FontWeight.w100))]
|
||||
),
|
||||
)
|
||||
],),
|
||||
if (widget.record.id != widget.record.replayID) Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Text(t.numberOfRounds),
|
||||
RichText(
|
||||
text: TextSpan(
|
||||
text: widget.record.endContext.first.secondaryTracking.isNotEmpty ? widget.record.endContext.first.secondaryTracking.length.toString() : "---",
|
||||
style: TextStyle(
|
||||
text: widget.record.results.rounds.length.toString(),
|
||||
style: const TextStyle(
|
||||
fontFamily: "Eurostile Round Extended",
|
||||
fontSize: 28,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: widget.record.endContext.first.secondaryTracking.isEmpty ? Colors.grey : Colors.white
|
||||
color: Colors.white
|
||||
),
|
||||
),
|
||||
)
|
||||
|
@ -572,109 +480,63 @@ class TlMatchResultState extends State<TlMatchResultView> {
|
|||
OverflowBar(
|
||||
alignment: MainAxisAlignment.spaceEvenly,
|
||||
children: <Widget>[
|
||||
TextButton( style: roundSelector == -1 ? ButtonStyle(backgroundColor: MaterialStatePropertyAll(Colors.grey.shade900)) : null,
|
||||
TextButton( style: roundSelector == -1 ? ButtonStyle(backgroundColor: WidgetStatePropertyAll(Colors.grey.shade900)) : null,
|
||||
onPressed: () {
|
||||
roundSelector = -1;
|
||||
setState(() {});
|
||||
}, child: Text(t.matchStats)),
|
||||
TextButton( style: roundSelector == -2 ? ButtonStyle(backgroundColor: MaterialStatePropertyAll(Colors.grey.shade900)) : null,
|
||||
TextButton( style: roundSelector == -2 ? ButtonStyle(backgroundColor: WidgetStatePropertyAll(Colors.grey.shade900)) : null,
|
||||
onPressed: timeWeightedStatsAvaliable ? () {
|
||||
roundSelector = -2;
|
||||
setState(() {});
|
||||
} : null, child: Text(t.timeWeightedmatchStats)) ,
|
||||
//TextButton( child: const Text('Button 3'), onPressed: () {}),
|
||||
],
|
||||
)
|
||||
]),
|
||||
// Column(
|
||||
// children: [
|
||||
// ListTile(
|
||||
// leading: Text("Round time"),
|
||||
// title: Text("Winner", textAlign: TextAlign.center,),
|
||||
// trailing: Text("Round stats"),
|
||||
// )
|
||||
// ],
|
||||
// )
|
||||
],
|
||||
)
|
||||
)
|
||||
];
|
||||
},
|
||||
body: ListView.builder(itemCount: widget.record.endContext.first.secondaryTracking.length,
|
||||
body: ListView.builder(itemCount: widget.record.results.rounds.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return FutureBuilder(future: replayData, builder: (context, snapshot) {
|
||||
switch(snapshot.connectionState){
|
||||
case ConnectionState.none:
|
||||
case ConnectionState.waiting:
|
||||
case ConnectionState.active:
|
||||
return const LinearProgressIndicator();
|
||||
case ConnectionState.done:
|
||||
if (!snapshot.hasError){
|
||||
var time = framesToTime(snapshot.data!.roundLengths[index]);
|
||||
var accentColor = snapshot.data!.roundWinners[index][0] == widget.initPlayerId ? Colors.green : Colors.red;
|
||||
var bgColor = roundSelector == index ? Colors.grey.shade900 : Colors.transparent;
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
stops: const [0, 0.05],
|
||||
colors: [accentColor, bgColor]
|
||||
)
|
||||
),
|
||||
child: ListTile(
|
||||
leading:RichText(
|
||||
text: TextSpan(
|
||||
text: "${time.inMinutes}:${NumberFormat("00", LocaleSettings.currentLocale.languageCode).format(time.inSeconds%60)}",
|
||||
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 22, fontWeight: FontWeight.w500, color: Colors.white),
|
||||
children: [TextSpan(text: ".${NumberFormat("000", LocaleSettings.currentLocale.languageCode).format(time.inMilliseconds%1000)}", style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, fontWeight: FontWeight.w100))]
|
||||
),
|
||||
),
|
||||
title: Text(snapshot.data!.roundWinners[index][1], textAlign: TextAlign.center),
|
||||
trailing: TrailingStats(
|
||||
widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).secondaryTracking[index],
|
||||
widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).tertiaryTracking[index],
|
||||
widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).extraTracking[index],
|
||||
widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).secondaryTracking[index],
|
||||
widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).tertiaryTracking[index],
|
||||
widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).extraTracking[index]
|
||||
),
|
||||
onTap:(){
|
||||
roundSelector = index;
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
);
|
||||
}else{
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: roundSelector == index ? Colors.grey.shade900 : Colors.transparent
|
||||
),
|
||||
child: ListTile(
|
||||
leading: RichText(
|
||||
text: const TextSpan(
|
||||
text: "-:--",
|
||||
style: TextStyle(fontFamily: "Eurostile Round", fontSize: 22, fontWeight: FontWeight.w500, color: Colors.grey),
|
||||
children: [TextSpan(text: ".---", style: TextStyle(fontFamily: "Eurostile Round", fontSize: 14, fontWeight: FontWeight.w100))]
|
||||
),
|
||||
),
|
||||
title: const Text("---", style: TextStyle(color: Colors.grey), textAlign: TextAlign.center),
|
||||
trailing: TrailingStats(
|
||||
widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).secondaryTracking[index],
|
||||
widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).tertiaryTracking[index],
|
||||
widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).extraTracking[index],
|
||||
widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).secondaryTracking[index],
|
||||
widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).tertiaryTracking[index],
|
||||
widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).extraTracking[index]
|
||||
),
|
||||
onTap:(){
|
||||
roundSelector = index;
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
var accentColor = widget.record.results.rounds[index][0].id == widget.initPlayerId ? Colors.green : Colors.red;
|
||||
var bgColor = roundSelector == index ? Colors.grey.shade900 : Colors.transparent;
|
||||
var time = roundLengths[index];
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
stops: const [0, 0.05],
|
||||
colors: [accentColor, bgColor]
|
||||
)
|
||||
),
|
||||
child: ListTile(
|
||||
leading:RichText(
|
||||
text: !time.isNegative ? TextSpan(
|
||||
text: "${time.inMinutes}:${NumberFormat("00", LocaleSettings.currentLocale.languageCode).format(time.inSeconds%60)}",
|
||||
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 22, fontWeight: FontWeight.w500, color: Colors.white),
|
||||
children: [TextSpan(text: ".${NumberFormat("000", LocaleSettings.currentLocale.languageCode).format(time.inMilliseconds%1000)}", style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, fontWeight: FontWeight.w100))]
|
||||
) : const TextSpan(
|
||||
text: "-:--",
|
||||
style: TextStyle(fontFamily: "Eurostile Round", fontSize: 22, fontWeight: FontWeight.w500, color: Colors.grey),
|
||||
children: [TextSpan(text: ".---", style: TextStyle(fontFamily: "Eurostile Round", fontSize: 14, fontWeight: FontWeight.w100))]
|
||||
),
|
||||
),
|
||||
title: Text(widget.record.results.rounds[index][0].username, textAlign: TextAlign.center),
|
||||
trailing: TrailingStats(
|
||||
widget.record.results.rounds[index].firstWhere((element) => element.id == widget.initPlayerId).stats.apm,
|
||||
widget.record.results.rounds[index].firstWhere((element) => element.id == widget.initPlayerId).stats.pps,
|
||||
widget.record.results.rounds[index].firstWhere((element) => element.id == widget.initPlayerId).stats.vs,
|
||||
widget.record.results.rounds[index].firstWhere((element) => element.id != widget.initPlayerId).stats.apm,
|
||||
widget.record.results.rounds[index].firstWhere((element) => element.id != widget.initPlayerId).stats.pps,
|
||||
widget.record.results.rounds[index].firstWhere((element) => element.id != widget.initPlayerId).stats.vs
|
||||
),
|
||||
onTap:(){
|
||||
roundSelector = index;
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
);
|
||||
})
|
||||
),
|
||||
),
|
||||
|
@ -707,10 +569,10 @@ class TlMatchResultState extends State<TlMatchResultView> {
|
|||
final t = Translations.of(context);
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text("${widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).username.toUpperCase()} ${t.vs} ${widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).username.toUpperCase()} ${t.inTLmatch} ${timestamp(widget.record.timestamp)}"),
|
||||
title: Text("${widget.record.results.leaderboard[greenSidePlayer].username.toUpperCase()} ${t.vs} ${widget.record.results.leaderboard[redSidePlayer].username.toUpperCase()} ${t.inTLmatch} ${widget.record.gamemode} ${timestamp(widget.record.ts)}"),
|
||||
actions: [
|
||||
PopupMenuButton(
|
||||
enabled: widget.record.replayAvalable,
|
||||
enabled: widget.record.gamemode == "league",
|
||||
itemBuilder: (BuildContext context) => <PopupMenuEntry>[
|
||||
PopupMenuItem(
|
||||
value: 1,
|
||||
|
@ -724,10 +586,10 @@ class TlMatchResultState extends State<TlMatchResultView> {
|
|||
onSelected: (value) async {
|
||||
switch (value) {
|
||||
case 1:
|
||||
await launchInBrowser(Uri.parse("https://inoue.szy.lol/api/replay/${widget.record.replayId}"));
|
||||
await launchInBrowser(Uri.parse("https://inoue.szy.lol/api/replay/${widget.record.replayID}"));
|
||||
break;
|
||||
case 2:
|
||||
await launchInBrowser(Uri.parse("https://tetr.io/#r:${widget.record.replayId}"));
|
||||
await launchInBrowser(Uri.parse("https://tetr.io/#r:${widget.record.replayID}"));
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
|
|
@ -78,7 +78,7 @@ class TrackedPlayersState extends State<TrackedPlayersView> {
|
|||
case ConnectionState.active:
|
||||
return const Center(child: CircularProgressIndicator(color: Colors.white));
|
||||
case ConnectionState.done:
|
||||
final allPlayers = (snapshot.data != null) ? snapshot.data as Map<String, List<TetrioPlayer>> : <String, List<TetrioPlayer>>{};
|
||||
final allPlayers = (snapshot.data != null) ? snapshot.data as Map<String, String> : <String, String>{};
|
||||
List<String> keys = allPlayers.keys.toList();
|
||||
return NestedScrollView(
|
||||
headerSliverBuilder: (context, value) {
|
||||
|
@ -105,29 +105,29 @@ class TrackedPlayersState extends State<TrackedPlayersView> {
|
|||
];
|
||||
},
|
||||
body: ListView.builder(
|
||||
itemCount: allPlayers.length,
|
||||
itemBuilder: (context, index) {
|
||||
return ListTile(
|
||||
title: Text(t.trackedPlayersEntry(nickname: allPlayers[keys[index]]!.last.username, numberOfStates: allPlayers[keys[index]]!.length)),
|
||||
subtitle: Text(t.trackedPlayersDescription(firstStateDate: timestamp(allPlayers[keys[index]]!.first.state), lastStateDate: timestamp(allPlayers[keys[index]]!.last.state))),
|
||||
trailing: IconButton(
|
||||
icon: const Icon(Icons.delete_forever),
|
||||
onPressed: () {
|
||||
String nn = allPlayers[keys[index]]!.last.username;
|
||||
setState(() {teto.deletePlayer(keys[index]);});
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.trackedPlayersStatesDeleted(nickname: nn))));
|
||||
},
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => StatesView(states: allPlayers[keys[index]]!),
|
||||
),
|
||||
);
|
||||
itemCount: allPlayers.length,
|
||||
itemBuilder: (context, index) {
|
||||
print(index);
|
||||
return ListTile(
|
||||
title: Text(allPlayers[keys[index]]??"No nickname (huh?)"),
|
||||
subtitle: Text(keys[index], style: TextStyle(fontFamily: "Eurostile Round Condensed", color: Colors.grey)),
|
||||
trailing: IconButton(
|
||||
icon: const Icon(Icons.delete_forever),
|
||||
onPressed: () {
|
||||
setState(() {teto.deletePlayer(keys[index]);});
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.trackedPlayersStatesDeleted(nickname: allPlayers[keys[index]]??"No nickname (huh?)"))));
|
||||
},
|
||||
);
|
||||
}));
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => StatesView(nickname: allPlayers[keys[index]]!, id: keys[index]),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}));
|
||||
}
|
||||
})),
|
||||
);
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:tetra_stats/data_objects/tetrio.dart';
|
||||
import 'package:tetra_stats/gen/strings.g.dart';
|
||||
import 'package:tetra_stats/widgets/text_timestamp.dart';
|
||||
import 'package:tetra_stats/widgets/zenith_thingy.dart';
|
||||
|
||||
class ZenithRecordView extends StatelessWidget {
|
||||
final RecordSingle record;
|
||||
|
||||
const ZenithRecordView({super.key, required this.record});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final t = Translations.of(context);
|
||||
//bool bigScreen = MediaQuery.of(context).size.width >= 368;
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.black,
|
||||
appBar: AppBar(
|
||||
title: Text("${
|
||||
switch (record.gamemode){
|
||||
"zenith" => t.quickPlay,
|
||||
"zenithex" => "${t.quickPlay} ${t.expert}",
|
||||
String() => "5000000 Blast",
|
||||
}
|
||||
} ${timestamp(record.timestamp)}"),
|
||||
),
|
||||
body: SafeArea(
|
||||
child: SizedBox(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
child: SingleChildScrollView(
|
||||
child: ZenithThingy(record: record, switchable: false),
|
||||
),
|
||||
)
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
// ignore_for_file: unused_field, unused_local_variable, invalid_use_of_visible_for_testing_member, implementation_imports, overridden_fields
|
||||
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:fl_chart/fl_chart.dart';
|
||||
|
@ -196,7 +198,7 @@ class MyRadarChartPainter extends RadarChartPainter{
|
|||
}
|
||||
|
||||
class MyRadarChartLeaf extends RadarChartLeaf{
|
||||
MyRadarChartLeaf({required super.data, required super.targetData});
|
||||
const MyRadarChartLeaf({super.key, required super.data, required super.targetData});
|
||||
|
||||
@override
|
||||
RenderRadarChart createRenderObject(BuildContext context) => MyRenderRadarChart(
|
||||
|
|
|
@ -7,8 +7,9 @@ class LineclearsThingy extends StatelessWidget{
|
|||
final int lines;
|
||||
final int holds;
|
||||
final int tSpins;
|
||||
final bool showMoreClears;
|
||||
|
||||
const LineclearsThingy(this.clears, this.lines, this.holds, this.tSpins, {super.key});
|
||||
const LineclearsThingy(this.clears, this.lines, this.holds, this.tSpins, {super.key, this.showMoreClears = false});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -21,6 +22,7 @@ class LineclearsThingy extends StatelessWidget{
|
|||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(t.numOfGameActions.lineClears(n: lines), style: const TextStyle(color: Colors.white, fontFamily: "Eurostile Round Extended"), textAlign: TextAlign.center),
|
||||
if (showMoreClears) Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("Pentas"), Text(clears.pentas.toString())]),
|
||||
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("Quads"), Text(clears.quads.toString())]),
|
||||
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("Triples"), Text(clears.triples.toString())]),
|
||||
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("Doubles"), Text(clears.doubles.toString())]),
|
||||
|
@ -36,10 +38,14 @@ class LineclearsThingy extends StatelessWidget{
|
|||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(t.numOfGameActions.tspinsTotal(n: tSpins), style: const TextStyle(color: Colors.white, fontFamily: "Eurostile Round Extended"), textAlign: TextAlign.center),
|
||||
if (showMoreClears) Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("T-spin pentas"), Text(clears.tSpinPentas.toString())]),
|
||||
if (showMoreClears) Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("T-spin quads"), Text(clears.tSpinQuads.toString())]),
|
||||
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("T-spins triples"), Text(clears.tSpinTriples.toString())]),
|
||||
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("T-spins doubles"), Text(clears.tSpinDoubles.toString())]),
|
||||
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("T-spins singles"), Text(clears.tSpinSingles.toString())]),
|
||||
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("T-spins zeros"), Text(clears.tSpinZeros.toString())]),
|
||||
if (showMoreClears) Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("Mini T-spins quads"), Text(clears.tSpinMiniQuads.toString())]),
|
||||
if (showMoreClears) Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("Mini T-spins triples"), Text(clears.tSpinMiniTriples.toString())]),
|
||||
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("Mini T-spins doubles"), Text(clears.tSpinMiniDoubles.toString())]),
|
||||
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("Mini T-spins singles"), Text(clears.tSpinMiniSingles.toString())]),
|
||||
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [const Text("Mini T-spins zeros"), Text(clears.tSpinMiniZeros.toString())]),
|
||||
|
|
|
@ -25,7 +25,7 @@ class RecentSingleplayerGames extends StatelessWidget{
|
|||
for(RecordSingle record in recent.records) ListTile(
|
||||
onTap: () => Navigator.push(context, MaterialPageRoute(builder: (context) => SingleplayerRecordView(record: record))),
|
||||
leading: Text(
|
||||
switch (record.endContext.gameType){
|
||||
switch (record.gamemode){
|
||||
"40l" => "40L",
|
||||
"blitz" => "BLZ",
|
||||
"5mblast" => "5MB",
|
||||
|
@ -34,15 +34,15 @@ class RecentSingleplayerGames extends StatelessWidget{
|
|||
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28, shadows: textShadow, height: 0.9)
|
||||
),
|
||||
title: Text(
|
||||
switch (record.endContext.gameType){
|
||||
"40l" => get40lTime(record.endContext.finalTime.inMicroseconds),
|
||||
"blitz" => t.blitzScore(p: NumberFormat.decimalPattern().format(record.endContext.score)),
|
||||
"5mblast" => get40lTime(record.endContext.finalTime.inMicroseconds),
|
||||
switch (record.gamemode){
|
||||
"40l" => get40lTime(record.stats.finalTime.inMicroseconds),
|
||||
"blitz" => t.blitzScore(p: NumberFormat.decimalPattern().format(record.stats.score)),
|
||||
"5mblast" => get40lTime(record.stats.finalTime.inMicroseconds),
|
||||
String() => "huh",
|
||||
},
|
||||
style: const TextStyle(fontSize: 18)),
|
||||
subtitle: Text(timestamp(record.timestamp), style: const TextStyle(color: Colors.grey, height: 0.85)),
|
||||
trailing: SpTrailingStats(record.endContext)
|
||||
trailing: SpTrailingStats(record, record.gamemode)
|
||||
)
|
||||
],
|
||||
);
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:intl/intl.dart';
|
||||
import 'package:tetra_stats/data_objects/tetrio.dart';
|
||||
import 'package:tetra_stats/gen/strings.g.dart';
|
||||
import 'package:tetra_stats/utils/colors_functions.dart';
|
||||
import 'package:tetra_stats/utils/numers_formats.dart';
|
||||
import 'package:tetra_stats/utils/open_in_browser.dart';
|
||||
import 'package:tetra_stats/utils/relative_timestamps.dart';
|
||||
|
@ -22,30 +23,21 @@ class SingleplayerRecord extends StatelessWidget {
|
|||
/// Widget that displays data from [record]
|
||||
const SingleplayerRecord({super.key, required this.record, this.stream, this.rank, this.hideTitle = false});
|
||||
|
||||
Color getColorOfRank(int rank){
|
||||
if (rank == 1) return Colors.yellowAccent;
|
||||
if (rank == 2) return Colors.blueGrey;
|
||||
if (rank == 3) return Colors.brown[400]!;
|
||||
if (rank <= 9) return Colors.blueAccent;
|
||||
if (rank <= 99) return Colors.greenAccent;
|
||||
return Colors.grey;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (record == null) return Center(child: Text(t.noRecord, textAlign: TextAlign.center, style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28)));
|
||||
late MapEntry closestAverageBlitz;
|
||||
late bool blitzBetterThanClosestAverage;
|
||||
bool? blitzBetterThanRankAverage = (rank != null && rank != "z") ? record!.endContext.score > blitzAverages[rank]! : null;
|
||||
bool? blitzBetterThanRankAverage = (rank != null && rank != "z" && rank != "x+") ? record!.stats.score > blitzAverages[rank]! : null;
|
||||
late MapEntry closestAverageSprint;
|
||||
late bool sprintBetterThanClosestAverage;
|
||||
bool? sprintBetterThanRankAverage = (rank != null && rank != "z") ? record!.endContext.finalTime < sprintAverages[rank]! : null;
|
||||
if (record!.endContext.gameType == "40l") {
|
||||
closestAverageSprint = sprintAverages.entries.singleWhere((element) => element.value == sprintAverages.values.reduce((a, b) => (a-record!.endContext.finalTime).abs() < (b -record!.endContext.finalTime).abs() ? a : b));
|
||||
sprintBetterThanClosestAverage = record!.endContext.finalTime < closestAverageSprint.value;
|
||||
}else if (record!.endContext.gameType == "blitz"){
|
||||
closestAverageBlitz = blitzAverages.entries.singleWhere((element) => element.value == blitzAverages.values.reduce((a, b) => (a-record!.endContext.score).abs() < (b -record!.endContext.score).abs() ? a : b));
|
||||
blitzBetterThanClosestAverage = record!.endContext.score > closestAverageBlitz.value;
|
||||
bool? sprintBetterThanRankAverage = (rank != null && rank != "z" && rank != "x+") ? record!.stats.finalTime < sprintAverages[rank]! : null;
|
||||
if (record!.gamemode == "40l") {
|
||||
closestAverageSprint = sprintAverages.entries.singleWhere((element) => element.value == sprintAverages.values.reduce((a, b) => (a-record!.stats.finalTime).abs() < (b -record!.stats.finalTime).abs() ? a : b));
|
||||
sprintBetterThanClosestAverage = record!.stats.finalTime < closestAverageSprint.value;
|
||||
}else if (record!.gamemode == "blitz"){
|
||||
closestAverageBlitz = blitzAverages.entries.singleWhere((element) => element.value == blitzAverages.values.reduce((a, b) => (a-record!.stats.score).abs() < (b -record!.stats.score).abs() ? a : b));
|
||||
blitzBetterThanClosestAverage = record!.stats.score > closestAverageBlitz.value;
|
||||
}
|
||||
|
||||
return LayoutBuilder(
|
||||
|
@ -61,20 +53,20 @@ class SingleplayerRecord extends StatelessWidget {
|
|||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (record!.endContext.gameType == "40l") Padding(padding: const EdgeInsets.only(right: 8.0),
|
||||
if (record!.gamemode == "40l") Padding(padding: const EdgeInsets.only(right: 8.0),
|
||||
child: Image.asset("res/tetrio_tl_alpha_ranks/${closestAverageSprint.key}.png", height: 96)
|
||||
),
|
||||
if (record!.endContext.gameType == "blitz") Padding(padding: const EdgeInsets.only(right: 8.0),
|
||||
if (record!.gamemode == "blitz") Padding(padding: const EdgeInsets.only(right: 8.0),
|
||||
child: Image.asset("res/tetrio_tl_alpha_ranks/${closestAverageBlitz.key}.png", height: 96)
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (record!.endContext.gameType == "40l" && !hideTitle) Text(t.sprint, style: const TextStyle(height: 0.1, fontFamily: "Eurostile Round Extended", fontSize: 18)),
|
||||
if (record!.endContext.gameType == "blitz" && !hideTitle) Text(t.blitz, style: const TextStyle(height: 0.1, fontFamily: "Eurostile Round Extended", fontSize: 18)),
|
||||
if (record!.gamemode == "40l" && !hideTitle) Text(t.sprint, style: const TextStyle(height: 0.1, fontFamily: "Eurostile Round Extended", fontSize: 18)),
|
||||
if (record!.gamemode == "blitz" && !hideTitle) Text(t.blitz, style: const TextStyle(height: 0.1, fontFamily: "Eurostile Round Extended", fontSize: 18)),
|
||||
RichText(text: TextSpan(
|
||||
text: record!.endContext.gameType == "40l" ? get40lTime(record!.endContext.finalTime.inMicroseconds) : NumberFormat.decimalPattern().format(record!.endContext.score),
|
||||
text: record!.gamemode == "40l" ? get40lTime(record!.stats.finalTime.inMicroseconds) : NumberFormat.decimalPattern().format(record!.stats.score),
|
||||
style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 36 : 32, fontWeight: FontWeight.w500, color: Colors.white),
|
||||
),
|
||||
),
|
||||
|
@ -82,20 +74,20 @@ class SingleplayerRecord extends StatelessWidget {
|
|||
text: "",
|
||||
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, color: Colors.grey),
|
||||
children: [
|
||||
if (record!.endContext.gameType == "40l" && (rank != null && rank != "z")) TextSpan(text: "${t.verdictGeneral(n: readableTimeDifference(record!.endContext.finalTime, sprintAverages[rank]!), verdict: sprintBetterThanRankAverage??false ? t.verdictBetter : t.verdictWorse, rank: rank!.toUpperCase())}\n", style: TextStyle(
|
||||
if (record!.gamemode == "40l" && (rank != null && rank != "z" && rank != "x+")) TextSpan(text: "${t.verdictGeneral(n: readableTimeDifference(record!.stats.finalTime, sprintAverages[rank]!), verdict: sprintBetterThanRankAverage??false ? t.verdictBetter : t.verdictWorse, rank: rank!.toUpperCase())}\n", style: TextStyle(
|
||||
color: sprintBetterThanRankAverage??false ? Colors.greenAccent : Colors.redAccent
|
||||
))
|
||||
else if (record!.endContext.gameType == "40l" && (rank == null || rank == "z")) TextSpan(text: "${t.verdictGeneral(n: readableTimeDifference(record!.endContext.finalTime, closestAverageSprint.value), verdict: sprintBetterThanClosestAverage ? t.verdictBetter : t.verdictWorse, rank: closestAverageSprint.key.toUpperCase())}\n", style: TextStyle(
|
||||
else if (record!.gamemode == "40l" && (rank == null || rank == "z" || rank != "x+")) TextSpan(text: "${t.verdictGeneral(n: readableTimeDifference(record!.stats.finalTime, closestAverageSprint.value), verdict: sprintBetterThanClosestAverage ? t.verdictBetter : t.verdictWorse, rank: closestAverageSprint.key.toUpperCase())}\n", style: TextStyle(
|
||||
color: sprintBetterThanClosestAverage ? Colors.greenAccent : Colors.redAccent
|
||||
))
|
||||
else if (record!.endContext.gameType == "blitz" && (rank != null && rank != "z")) TextSpan(text: "${t.verdictGeneral(n: readableIntDifference(record!.endContext.score, blitzAverages[rank]!), verdict: blitzBetterThanRankAverage??false ? t.verdictBetter : t.verdictWorse, rank: rank!.toUpperCase())}\n", style: TextStyle(
|
||||
else if (record!.gamemode == "blitz" && (rank != null && rank != "z" && rank != "x+")) TextSpan(text: "${t.verdictGeneral(n: readableIntDifference(record!.stats.score, blitzAverages[rank]!), verdict: blitzBetterThanRankAverage??false ? t.verdictBetter : t.verdictWorse, rank: rank!.toUpperCase())}\n", style: TextStyle(
|
||||
color: blitzBetterThanRankAverage??false ? Colors.greenAccent : Colors.redAccent
|
||||
))
|
||||
else if (record!.endContext.gameType == "blitz" && (rank == null || rank == "z")) TextSpan(text: "${t.verdictGeneral(n: readableIntDifference(record!.endContext.score, closestAverageBlitz.value), verdict: blitzBetterThanClosestAverage ? t.verdictBetter : t.verdictWorse, rank: closestAverageBlitz.key.toUpperCase())}\n", style: TextStyle(
|
||||
else if (record!.gamemode == "blitz" && (rank == null || rank == "z" || rank != "x+")) TextSpan(text: "${t.verdictGeneral(n: readableIntDifference(record!.stats.score, closestAverageBlitz.value), verdict: blitzBetterThanClosestAverage ? t.verdictBetter : t.verdictWorse, rank: closestAverageBlitz.key.toUpperCase())}\n", style: TextStyle(
|
||||
color: blitzBetterThanClosestAverage ? Colors.greenAccent : Colors.redAccent
|
||||
)),
|
||||
if (record!.rank != null) TextSpan(text: "№${record!.rank}", style: TextStyle(color: getColorOfRank(record!.rank!))),
|
||||
if (record!.rank != null) const TextSpan(text: " • "),
|
||||
if (record!.rank != -1) TextSpan(text: "№${record!.rank}", style: TextStyle(color: getColorOfRank(record!.rank))),
|
||||
if (record!.rank != -1) const TextSpan(text: " • "),
|
||||
TextSpan(text: timestamp(record!.timestamp)),
|
||||
]
|
||||
),
|
||||
|
@ -103,29 +95,29 @@ class SingleplayerRecord extends StatelessWidget {
|
|||
],),
|
||||
],
|
||||
),
|
||||
if (record!.endContext.gameType == "40l") Wrap(
|
||||
if (record!.gamemode == "40l") Wrap(
|
||||
alignment: WrapAlignment.spaceBetween,
|
||||
spacing: 20,
|
||||
children: [
|
||||
StatCellNum(playerStat: record!.endContext.piecesPlaced, playerStatLabel: t.statCellNum.pieces, isScreenBig: bigScreen, higherIsBetter: true, smallDecimal: false),
|
||||
StatCellNum(playerStat: record!.endContext.pps, playerStatLabel: t.statCellNum.pps, fractionDigits: 2, isScreenBig: bigScreen, higherIsBetter: true, smallDecimal: false),
|
||||
StatCellNum(playerStat: record!.endContext.kpp, playerStatLabel: t.statCellNum.kpp, fractionDigits: 2, isScreenBig: bigScreen, higherIsBetter: true, smallDecimal: false),
|
||||
StatCellNum(playerStat: record!.stats.piecesPlaced, playerStatLabel: t.statCellNum.pieces, isScreenBig: bigScreen, higherIsBetter: true, smallDecimal: false),
|
||||
StatCellNum(playerStat: record!.stats.pps, playerStatLabel: t.statCellNum.pps, fractionDigits: 2, isScreenBig: bigScreen, higherIsBetter: true, smallDecimal: false),
|
||||
StatCellNum(playerStat: record!.stats.kpp, playerStatLabel: t.statCellNum.kpp, fractionDigits: 2, isScreenBig: bigScreen, higherIsBetter: true, smallDecimal: false),
|
||||
],
|
||||
),
|
||||
if (record!.endContext.gameType == "blitz") Wrap(
|
||||
if (record!.gamemode == "blitz") Wrap(
|
||||
alignment: WrapAlignment.spaceBetween,
|
||||
crossAxisAlignment: WrapCrossAlignment.start,
|
||||
spacing: 20,
|
||||
children: [
|
||||
StatCellNum(playerStat: record!.endContext.level, playerStatLabel: t.statCellNum.level, isScreenBig: bigScreen, higherIsBetter: true, smallDecimal: false),
|
||||
StatCellNum(playerStat: record!.endContext.pps, playerStatLabel: t.statCellNum.pps, fractionDigits: 2, isScreenBig: bigScreen, higherIsBetter: true, smallDecimal: false),
|
||||
StatCellNum(playerStat: record!.endContext.spp, playerStatLabel: t.statCellNum.spp, fractionDigits: 2, isScreenBig: bigScreen, higherIsBetter: true)
|
||||
StatCellNum(playerStat: record!.stats.level, playerStatLabel: t.statCellNum.level, isScreenBig: bigScreen, higherIsBetter: true, smallDecimal: false),
|
||||
StatCellNum(playerStat: record!.stats.pps, playerStatLabel: t.statCellNum.pps, fractionDigits: 2, isScreenBig: bigScreen, higherIsBetter: true, smallDecimal: false),
|
||||
StatCellNum(playerStat: record!.stats.spp, playerStatLabel: t.statCellNum.spp, fractionDigits: 2, isScreenBig: bigScreen, higherIsBetter: true)
|
||||
],
|
||||
),
|
||||
FinesseThingy(record?.endContext.finesse, record?.endContext.finessePercentage),
|
||||
LineclearsThingy(record!.endContext.clears, record!.endContext.lines, record!.endContext.holds, record!.endContext.tSpins),
|
||||
if (record!.endContext.gameType == "40l") Text("${record!.endContext.inputs} KP • ${f2.format(record!.endContext.kps)} KPS"),
|
||||
if (record!.endContext.gameType == "blitz") Text("${record!.endContext.piecesPlaced} P • ${record!.endContext.inputs} KP • ${f2.format(record!.endContext.kpp)} KPP • ${f2.format(record!.endContext.kps)} KPS"),
|
||||
FinesseThingy(record?.stats.finesse, record?.stats.finessePercentage),
|
||||
LineclearsThingy(record!.stats.clears, record!.stats.lines, record!.stats.holds, record!.stats.tSpins),
|
||||
if (record!.gamemode == "40l") Text("${record!.stats.inputs} KP • ${f2.format(record!.stats.kps)} KPS"),
|
||||
if (record!.gamemode == "blitz") Text("${record!.stats.piecesPlaced} P • ${record!.stats.inputs} KP • ${f2.format(record!.stats.kpp)} KPP • ${f2.format(record!.stats.kps)} KPS"),
|
||||
if (record != null) Wrap(
|
||||
alignment: WrapAlignment.spaceBetween,
|
||||
crossAxisAlignment: WrapCrossAlignment.start,
|
||||
|
@ -141,15 +133,15 @@ class SingleplayerRecord extends StatelessWidget {
|
|||
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28, shadows: textShadow, height: 0.9)
|
||||
),
|
||||
title: Text(
|
||||
switch (stream!.records[i].endContext.gameType){
|
||||
"40l" => get40lTime(stream!.records[i].endContext.finalTime.inMicroseconds),
|
||||
"blitz" => t.blitzScore(p: NumberFormat.decimalPattern().format(stream!.records[i].endContext.score)),
|
||||
"5mblast" => get40lTime(stream!.records[i].endContext.finalTime.inMicroseconds),
|
||||
switch (stream!.records[i].gamemode){
|
||||
"40l" => get40lTime(stream!.records[i].stats.finalTime.inMicroseconds),
|
||||
"blitz" => t.blitzScore(p: NumberFormat.decimalPattern().format(stream!.records[i].stats.score)),
|
||||
"5mblast" => get40lTime(stream!.records[i].stats.finalTime.inMicroseconds),
|
||||
String() => "huh",
|
||||
},
|
||||
style: const TextStyle(fontSize: 18)),
|
||||
subtitle: Text(timestamp(stream!.records[i].timestamp), style: const TextStyle(color: Colors.grey, height: 0.85)),
|
||||
trailing: SpTrailingStats(stream!.records[i].endContext)
|
||||
trailing: SpTrailingStats(stream!.records[i], stream!.records[i].gamemode)
|
||||
)
|
||||
]
|
||||
),
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:tetra_stats/data_objects/tetrio.dart';
|
||||
import 'package:tetra_stats/utils/numers_formats.dart';
|
||||
import 'package:tetra_stats/utils/relative_timestamps.dart';
|
||||
|
||||
class SpTrailingStats extends StatelessWidget{
|
||||
final EndContextSingle endContext;
|
||||
final RecordSingle record;
|
||||
final String gamemode;
|
||||
|
||||
const SpTrailingStats(this.endContext, {super.key});
|
||||
const SpTrailingStats(this.record, this.gamemode, {super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -14,12 +16,28 @@ class SpTrailingStats extends StatelessWidget{
|
|||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Text("${endContext.piecesPlaced} P, ${f2.format(endContext.pps)} PPS", style: style, textAlign: TextAlign.right),
|
||||
Text("${intf.format(endContext.finessePercentage*100)}% F, ${endContext.finesse?.faults} FF", style: style, textAlign: TextAlign.right),
|
||||
Text(switch(endContext.gameType){
|
||||
"40l" => "${f2.format(endContext.kps)} KPS, ${f2.format(endContext.kpp)} KPP",
|
||||
"blitz" => "${intf.format(endContext.spp)} SPP, lvl ${endContext.level}",
|
||||
"5mblast" => "${intf.format(endContext.spp)} SPP, ${endContext.lines} L",
|
||||
Text(switch(gamemode){
|
||||
"40l" => "${record.stats.piecesPlaced} P, ${f2.format(record.stats.pps)} PPS",
|
||||
"blitz" => "${record.stats.piecesPlaced} P, ${f2.format(record.stats.pps)} PPS",
|
||||
"5mblast" => "${record.stats.piecesPlaced} P, ${f2.format(record.stats.pps)} PPS",
|
||||
"zenith" => "${f2.format(record.aggregateStats.apm)} APM, ${f2.format(record.aggregateStats.pps)} PPS",
|
||||
"zenithex" => "${f2.format(record.aggregateStats.apm)} APM, ${f2.format(record.aggregateStats.pps)} PPS",
|
||||
String() => "huh"
|
||||
}, style: style, textAlign: TextAlign.right),
|
||||
Text(switch(gamemode){
|
||||
"40l" => "${intf.format(record.stats.finessePercentage*100)}% F, ${record.stats.finesse?.faults} FF",
|
||||
"blitz" => "${intf.format(record.stats.finessePercentage*100)}% F, ${record.stats.finesse?.faults} FF",
|
||||
"5mblast" => "${intf.format(record.stats.finessePercentage*100)}% F, ${record.stats.finesse?.faults} FF",
|
||||
"zenith" => "${f2.format(record.stats.cps)} CSP (${f2.format(record.stats.zenith!.peakrank)} peak)",
|
||||
"zenithex" => "${f2.format(record.stats.cps)} CSP (${f2.format(record.stats.zenith!.peakrank)} peak)",
|
||||
String() => "huh"
|
||||
}, style: style, textAlign: TextAlign.right),
|
||||
Text(switch(gamemode){
|
||||
"40l" => "${f2.format(record.stats.kps)} KPS, ${f2.format(record.stats.kpp)} KPP",
|
||||
"blitz" => "${intf.format(record.stats.spp)} SPP, lvl ${record.stats.level}",
|
||||
"5mblast" => "${intf.format(record.stats.spp)} SPP, ${record.stats.lines} L",
|
||||
"zenith" => "${record.stats.kills} KO's, ${getMoreNormalTime(record.stats.finalTime)}",
|
||||
"zenithex" => "${record.stats.kills} KO's, ${getMoreNormalTime(record.stats.finalTime)}",
|
||||
String() => "huh"
|
||||
}, style: style, textAlign: TextAlign.right)
|
||||
],
|
||||
|
|
|
@ -11,7 +11,7 @@ class StatCellNum extends StatelessWidget {
|
|||
required this.playerStat,
|
||||
required this.playerStatLabel,
|
||||
required this.isScreenBig,
|
||||
this.smallDecimal = true,
|
||||
this.smallDecimal = false,
|
||||
this.alertWidgets,
|
||||
this.fractionDigits,
|
||||
this.oldPlayerStat,
|
||||
|
@ -52,7 +52,7 @@ class StatCellNum extends StatelessWidget {
|
|||
RichText(
|
||||
text: TextSpan(text: splited[0],
|
||||
children: [
|
||||
if ((fractionDigits??0) > 0) TextSpan(text: f.symbols.DECIMAL_SEP+splited[1], style: smallDecimal ? const TextStyle(fontFamily: "Eurostile Round", fontSize: 16) : null)
|
||||
if ((fractionDigits??0) > 0 && splited.elementAtOrNull(1) != null) TextSpan(text: f.symbols.DECIMAL_SEP+splited[1], style: smallDecimal ? const TextStyle(fontFamily: "Eurostile Round", fontSize: 16) : null)
|
||||
],
|
||||
style: TextStyle(
|
||||
fontFamily: "Eurostile Round Extended",
|
||||
|
@ -88,25 +88,25 @@ class StatCellNum extends StatelessWidget {
|
|||
: TextButton(
|
||||
onPressed: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) => AlertDialog(
|
||||
title: Text(alertTitle??playerStatLabel.replaceAll(RegExp(r'\n'), " "),
|
||||
style: const TextStyle(
|
||||
fontFamily: "Eurostile Round Extended")),
|
||||
content: SingleChildScrollView(
|
||||
child: ListBody(children: alertWidgets!),
|
||||
),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
child: Text(okText??"OK"),
|
||||
onPressed: () {Navigator.of(context).pop();}
|
||||
)
|
||||
],
|
||||
)
|
||||
context: context,
|
||||
builder: (BuildContext context) => AlertDialog(
|
||||
title: Text(alertTitle??playerStatLabel.replaceAll(RegExp(r'\n'), " "),
|
||||
style: const TextStyle(
|
||||
fontFamily: "Eurostile Round Extended")),
|
||||
content: SingleChildScrollView(
|
||||
child: ListBody(children: alertWidgets!),
|
||||
),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
child: Text(okText??"OK"),
|
||||
onPressed: () {Navigator.of(context).pop();}
|
||||
)
|
||||
],
|
||||
)
|
||||
);
|
||||
},
|
||||
style: ButtonStyle(
|
||||
padding: MaterialStateProperty.all(EdgeInsets.zero)),
|
||||
padding: WidgetStateProperty.all(EdgeInsets.zero)),
|
||||
child: Text(
|
||||
playerStatLabel,
|
||||
textAlign: TextAlign.center,
|
||||
|
|
|
@ -9,9 +9,7 @@ import 'package:tetra_stats/gen/strings.g.dart';
|
|||
import 'package:tetra_stats/utils/numers_formats.dart';
|
||||
|
||||
class TLProgress extends StatelessWidget{
|
||||
final TetraLeagueAlpha tlData;
|
||||
final String? nextRank;
|
||||
final String? previousRank;
|
||||
final TetraLeague tlData;
|
||||
final double? nextRankTRcutoff;
|
||||
final double? previousRankTRcutoff;
|
||||
final double? nextRankGlickoCutoff;
|
||||
|
@ -19,7 +17,7 @@ class TLProgress extends StatelessWidget{
|
|||
final double? nextRankTRcutoffTarget;
|
||||
final double? previousRankTRcutoffTarget;
|
||||
|
||||
const TLProgress({super.key, required this.tlData, this.nextRank, this.previousRank, this.nextRankTRcutoff, this.previousRankTRcutoff, this.nextRankGlickoCutoff, this.previousGlickoCutoff, this.nextRankTRcutoffTarget, this.previousRankTRcutoffTarget});
|
||||
const TLProgress({super.key, required this.tlData, this.nextRankTRcutoff, this.previousRankTRcutoff, this.nextRankGlickoCutoff, this.previousGlickoCutoff, this.nextRankTRcutoffTarget, this.previousRankTRcutoffTarget});
|
||||
|
||||
double getBarPosition(){
|
||||
return min(max(0, 1 - (tlData.standing - tlData.nextAt)/(tlData.prevAt - tlData.nextAt)), 1);
|
||||
|
@ -31,65 +29,57 @@ class TLProgress extends StatelessWidget{
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (nextRank == null && previousRank == null && nextRankTRcutoff == null && previousRankTRcutoff == null && nextRankGlickoCutoff == null && previousGlickoCutoff == null && nextRankTRcutoffTarget == null && previousRankTRcutoffTarget == null) return Container();
|
||||
if (tlData.prevAt < 0 && tlData.nextAt < 0 && nextRankTRcutoff == null && previousRankTRcutoff == null && nextRankGlickoCutoff == null && previousGlickoCutoff == null && nextRankTRcutoffTarget == null && previousRankTRcutoffTarget == null) return Container();
|
||||
final glickoForWin = rate(tlData.glicko!, tlData.rd!, 0.06, [[tlData.glicko!, tlData.rd!, 1]], {})[0]-tlData.glicko!;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.fromLTRB(8, 0, 8, 0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
height: 48,
|
||||
child: Stack(
|
||||
alignment: AlignmentDirectional.bottomCenter,
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
Positioned(left: 0,
|
||||
child: RichText(
|
||||
textAlign: TextAlign.left,
|
||||
text: TextSpan(
|
||||
style: const TextStyle(color: Colors.white, fontFamily: "Eurostile Round", fontSize: 12),
|
||||
children: [
|
||||
if (tlData.prevAt > 0) TextSpan(text: "№ ${f0.format(tlData.prevAt)}"),
|
||||
if (tlData.prevAt > 0 && previousRankTRcutoff != null) const TextSpan(text: "\n"),
|
||||
if (previousRankTRcutoff != null) TextSpan(text: "${f2.format(previousRankTRcutoff)} (${comparef2.format(previousRankTRcutoff!-tlData.rating)}) TR"),
|
||||
if ((tlData.prevAt > 0 || previousRankTRcutoff != null) && previousGlickoCutoff != null) const TextSpan(text: "\n"),
|
||||
if (previousGlickoCutoff != null) TextSpan(text: (tlData.standing > tlData.prevAt || ((tlData.glicko!-previousGlickoCutoff!)/glickoForWin < 0.5 && tlData.percentileRank != "d")) ? t.demotionOnNextLoss : t.numOfdefeats(losses: f2.format((tlData.glicko!-previousGlickoCutoff!)/glickoForWin)), style: TextStyle(color: (tlData.standing > tlData.prevAt || ((tlData.glicko!-previousGlickoCutoff!)/glickoForWin < 0.5 && tlData.percentileRank != "d")) ? Colors.redAccent : null))
|
||||
]
|
||||
)
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
RichText(
|
||||
textAlign: TextAlign.left,
|
||||
text: TextSpan(
|
||||
style: const TextStyle(color: Colors.white, fontFamily: "Eurostile Round", fontSize: 12),
|
||||
children: [
|
||||
if (tlData.prevAt > 0) TextSpan(text: "№ ${f0.format(tlData.prevAt)}"),
|
||||
if (tlData.prevAt > 0 && previousRankTRcutoff != null) const TextSpan(text: "\n"),
|
||||
if (previousRankTRcutoff != null) TextSpan(text: "${f2.format(previousRankTRcutoff)} (${comparef2.format(previousRankTRcutoff!-tlData.tr)}) TR"),
|
||||
if ((tlData.prevAt > 0 || previousRankTRcutoff != null) && previousGlickoCutoff != null) const TextSpan(text: "\n"),
|
||||
if (previousGlickoCutoff != null) TextSpan(text: (tlData.standing > tlData.prevAt || ((tlData.glicko!-previousGlickoCutoff!)/glickoForWin < 0.5 && tlData.percentileRank != "d")) ? t.demotionOnNextLoss : t.numOfdefeats(losses: f2.format((tlData.glicko!-previousGlickoCutoff!)/glickoForWin)), style: TextStyle(color: (tlData.standing > tlData.prevAt || ((tlData.glicko!-previousGlickoCutoff!)/glickoForWin < 0.5 && tlData.percentileRank != "d")) ? Colors.redAccent : null))
|
||||
]
|
||||
)
|
||||
),
|
||||
Positioned(right: 0,
|
||||
child: RichText(
|
||||
textAlign: TextAlign.right,
|
||||
text: TextSpan(
|
||||
style: const TextStyle(color: Colors.white, fontFamily: "Eurostile Round", fontSize: 12),
|
||||
children: [
|
||||
if (tlData.nextAt > 0) TextSpan(text: "№ ${f0.format(tlData.nextAt)}"),
|
||||
if (tlData.nextAt > 0 && nextRankTRcutoff != null) const TextSpan(text: "\n"),
|
||||
if (nextRankTRcutoff != null) TextSpan(text: "${f2.format(nextRankTRcutoff)} (${comparef2.format(nextRankTRcutoff!-tlData.rating)}) TR"),
|
||||
if ((tlData.nextAt > 0 || nextRankTRcutoff != null) && nextRankGlickoCutoff != null) const TextSpan(text: "\n"),
|
||||
if (nextRankGlickoCutoff != null) TextSpan(text: (tlData.standing < tlData.nextAt || ((nextRankGlickoCutoff!-tlData.glicko!)/glickoForWin < 0.5 && ((tlData.rank != "x" && tlData.rank != "z") || tlData.percentileRank != "x"))) ? t.promotionOnNextWin : t.numOfVictories(wins: f2.format((nextRankGlickoCutoff!-tlData.glicko!)/glickoForWin)), style: TextStyle(color: (tlData.standing < tlData.nextAt || ((nextRankGlickoCutoff!-tlData.glicko!)/glickoForWin < 0.5 && tlData.percentileRank != "x")) ? Colors.greenAccent : null))
|
||||
]
|
||||
)
|
||||
),
|
||||
const Spacer(),
|
||||
RichText(
|
||||
textAlign: TextAlign.right,
|
||||
text: TextSpan(
|
||||
style: const TextStyle(color: Colors.white, fontFamily: "Eurostile Round", fontSize: 12),
|
||||
children: [
|
||||
if (tlData.nextAt > 0) TextSpan(text: "№ ${f0.format(tlData.nextAt)}"),
|
||||
if (tlData.nextAt > 0 && nextRankTRcutoff != null) const TextSpan(text: "\n"),
|
||||
if (nextRankTRcutoff != null) TextSpan(text: "${f2.format(nextRankTRcutoff)} (${comparef2.format(nextRankTRcutoff!-tlData.tr)}) TR"),
|
||||
if ((tlData.nextAt > 0 || nextRankTRcutoff != null) && nextRankGlickoCutoff != null) const TextSpan(text: "\n"),
|
||||
if (nextRankGlickoCutoff != null) TextSpan(text: (tlData.standing < tlData.nextAt || ((nextRankGlickoCutoff!-tlData.glicko!)/glickoForWin < 0.5 && ((tlData.rank != "x+" && tlData.rank != "z") || tlData.percentileRank != "x+"))) ? t.promotionOnNextWin : t.numOfVictories(wins: f2.format((nextRankGlickoCutoff!-tlData.glicko!)/glickoForWin)), style: TextStyle(color: (tlData.standing < tlData.nextAt || ((nextRankGlickoCutoff!-tlData.glicko!)/glickoForWin < 0.5 && tlData.percentileRank != "x+")) ? Colors.greenAccent : null))
|
||||
]
|
||||
)
|
||||
)
|
||||
],),
|
||||
],
|
||||
),
|
||||
SfLinearGauge(
|
||||
minimum: 0,
|
||||
maximum: 1,
|
||||
interval: 1,
|
||||
ranges: [
|
||||
if (previousRankTRcutoff != null && nextRankTRcutoff != null) LinearGaugeRange(endValue: getBarTR(tlData.rating)!, color: Theme.of(context).colorScheme.primary, position: LinearElementPosition.cross)
|
||||
if (previousRankTRcutoff != null && nextRankTRcutoff != null) LinearGaugeRange(endValue: getBarTR(tlData.tr)!, color: Theme.of(context).colorScheme.primary, position: LinearElementPosition.cross)
|
||||
else if (tlData.standing != -1) LinearGaugeRange(endValue: getBarPosition(), color: Theme.of(context).colorScheme.primary, position: LinearElementPosition.cross),
|
||||
if (previousRankTRcutoff != null && previousRankTRcutoffTarget != null) LinearGaugeRange(endValue: getBarTR(previousRankTRcutoffTarget!)!, color: Colors.greenAccent, position: LinearElementPosition.inside),
|
||||
if (nextRankTRcutoff != null && nextRankTRcutoffTarget != null && previousRankTRcutoff != null) LinearGaugeRange(startValue: getBarTR(nextRankTRcutoffTarget!)!, endValue: 1, color: Colors.yellowAccent, position: LinearElementPosition.inside)
|
||||
],
|
||||
markerPointers: [
|
||||
LinearShapePointer(value: (previousRankTRcutoff != null && nextRankTRcutoff != null) ? getBarTR(tlData.rating)! : getBarPosition(), position: LinearElementPosition.cross, shapeType: LinearShapePointerType.diamond, color: Colors.white, height: 20),
|
||||
if (tlData.standing != -1) LinearWidgetPointer(offset: 4, position: LinearElementPosition.outside, value: (previousRankTRcutoff != null && nextRankTRcutoff != null) ? getBarTR(tlData.rating)! : getBarPosition(), child: Text("№ ${NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 0).format(tlData.standing)}", style: const TextStyle(fontSize: 14),))
|
||||
LinearShapePointer(value: (previousRankTRcutoff != null && nextRankTRcutoff != null) ? getBarTR(tlData.tr)! : getBarPosition(), position: LinearElementPosition.cross, shapeType: LinearShapePointerType.diamond, color: Colors.white, height: 20),
|
||||
if (tlData.standing != -1) LinearWidgetPointer(offset: 4, position: LinearElementPosition.outside, value: (previousRankTRcutoff != null && nextRankTRcutoff != null) ? getBarTR(tlData.tr)! : getBarPosition(), child: Text("№ ${NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 0).format(tlData.standing)}", style: const TextStyle(fontSize: 14),))
|
||||
],
|
||||
isMirrored: true,
|
||||
showTicks: true,
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
@ -11,8 +10,8 @@ var fDiff = NumberFormat("+#,###.####;-#,###.####");
|
|||
|
||||
class TLRatingThingy extends StatelessWidget{
|
||||
final String userID;
|
||||
final TetraLeagueAlpha tlData;
|
||||
final TetraLeagueAlpha? oldTl;
|
||||
final TetraLeague tlData;
|
||||
final TetraLeague? oldTl;
|
||||
final double? topTR;
|
||||
final DateTime? lastMatchPlayed;
|
||||
|
||||
|
@ -23,13 +22,13 @@ class TLRatingThingy extends StatelessWidget{
|
|||
bool oskKagariGimmick = prefs.getBool("oskKagariGimmick")??true;
|
||||
bool bigScreen = MediaQuery.of(context).size.width >= 768;
|
||||
String decimalSeparator = f4.symbols.DECIMAL_SEP;
|
||||
List<String> formatedTR = f4.format(tlData.rating).split(decimalSeparator);
|
||||
List<String> formatedGlicko = f4.format(tlData.glicko).split(decimalSeparator);
|
||||
List<String> formatedTR = f4.format(tlData.tr).split(decimalSeparator);
|
||||
List<String> formatedGlicko = tlData.glicko != null ? f4.format(tlData.glicko).split(decimalSeparator) : ["---","--"];
|
||||
List<String> formatedPercentile = f4.format(tlData.percentile * 100).split(decimalSeparator);
|
||||
DateTime now = DateTime.now();
|
||||
bool beforeS1end = now.isBefore(seasonEnd);
|
||||
int daysLeft = seasonEnd.difference(now).inDays;
|
||||
int safeRD = min(100, (100 + ((tlData.rd! >= 100 && tlData.decaying) ? 7 : max(0, 7 - (lastMatchPlayed != null ? now.difference(lastMatchPlayed!).inDays : 7))) - daysLeft).toInt());
|
||||
//DateTime now = DateTime.now();
|
||||
//bool beforeS1end = now.isBefore(seasonEnd);
|
||||
//int daysLeft = seasonEnd.difference(now).inDays;
|
||||
//int safeRD = min(100, (100 + ((tlData.rd! >= 100 && tlData.decaying) ? 7 : max(0, 7 - (lastMatchPlayed != null ? now.difference(lastMatchPlayed!).inDays : 7))) - daysLeft).toInt());
|
||||
return Wrap(
|
||||
direction: Axis.horizontal,
|
||||
alignment: WrapAlignment.spaceAround,
|
||||
|
@ -44,7 +43,7 @@ class TLRatingThingy extends StatelessWidget{
|
|||
RichText(
|
||||
text: TextSpan(
|
||||
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 20, color: Colors.white),
|
||||
children: switch(prefs.getInt("ratingMode")){
|
||||
children: (tlData.gamesPlayed > 9) ? switch(prefs.getInt("ratingMode")){
|
||||
1 => [
|
||||
TextSpan(text: formatedGlicko[0], style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)),
|
||||
if (formatedGlicko.elementAtOrNull(1) != null) TextSpan(text: decimalSeparator + formatedGlicko[1]),
|
||||
|
@ -60,23 +59,23 @@ class TLRatingThingy extends StatelessWidget{
|
|||
if (formatedTR.elementAtOrNull(1) != null) TextSpan(text: decimalSeparator + formatedTR[1]),
|
||||
TextSpan(text: " TR", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28))
|
||||
],
|
||||
}
|
||||
} : [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) Text(
|
||||
switch(prefs.getInt("ratingMode")){
|
||||
1 => "${fDiff.format(tlData.glicko! - oldTl!.glicko!)} Glicko",
|
||||
2 => "${fDiff.format(tlData.percentile * 100 - oldTl!.percentile * 100)} %",
|
||||
_ => "${fDiff.format(tlData.rating - oldTl!.rating)} TR"
|
||||
_ => "${fDiff.format(tlData.tr - oldTl!.tr)} TR"
|
||||
},
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: tlData.rating - oldTl!.rating < 0 ?
|
||||
color: tlData.tr - oldTl!.tr < 0 ?
|
||||
Colors.red :
|
||||
Colors.green
|
||||
),
|
||||
),
|
||||
Column(
|
||||
if (tlData.gamesPlayed > 9) Column(
|
||||
children: [
|
||||
RichText(
|
||||
textAlign: TextAlign.center,
|
||||
|
@ -84,14 +83,14 @@ class TLRatingThingy extends StatelessWidget{
|
|||
text: TextSpan(
|
||||
style: DefaultTextStyle.of(context).style,
|
||||
children: [
|
||||
TextSpan(text: prefs.getInt("ratingMode") == 2 ? "${f2.format(tlData.rating)} TR • % ${t.rank}: ${tlData.percentileRank.toUpperCase()}" : "${t.top} ${f2.format(tlData.percentile * 100)}% (${tlData.percentileRank.toUpperCase()})"),
|
||||
TextSpan(text: prefs.getInt("ratingMode") == 2 ? "${f2.format(tlData.tr)} TR • % ${t.rank}: ${tlData.percentileRank.toUpperCase()}" : "${t.top} ${f2.format(tlData.percentile * 100)}% (${tlData.percentileRank.toUpperCase()})"),
|
||||
if (tlData.bestRank != "z") const TextSpan(text: " • "),
|
||||
if (tlData.bestRank != "z") TextSpan(text: "${t.topRank}: ${tlData.bestRank.toUpperCase()}"),
|
||||
if (topTR != null) TextSpan(text: " (${f2.format(topTR)} TR)"),
|
||||
TextSpan(text: " • ${prefs.getInt("ratingMode") == 1 ? "${f2.format(tlData.rating)} TR • RD: " : "Glicko: ${f2.format(tlData.glicko!)}±"}"),
|
||||
TextSpan(text: " • ${prefs.getInt("ratingMode") == 1 ? "${f2.format(tlData.tr)} TR • RD: " : "Glicko: ${tlData.glicko != null ? f2.format(tlData.glicko) : "---"}±"}"),
|
||||
TextSpan(text: f2.format(tlData.rd!), style: tlData.decaying ? TextStyle(color: tlData.rd! > 98 ? Colors.red : Colors.yellow) : null),
|
||||
if (tlData.decaying) WidgetSpan(child: Icon(Icons.trending_up, color: tlData.rd! > 98 ? Colors.red : Colors.yellow,), alignment: PlaceholderAlignment.middle, baseline: TextBaseline.alphabetic),
|
||||
if (beforeS1end) tlData.rd! <= safeRD ? TextSpan(text: " (Safe)", style: TextStyle(color: Colors.greenAccent)) : TextSpan(text: " (> ${safeRD} RD !!!)", style: TextStyle(color: Colors.redAccent))
|
||||
//if (beforeS1end) tlData.rd! <= safeRD ? TextSpan(text: " (Safe)", style: TextStyle(color: Colors.greenAccent)) : TextSpan(text: " (> ${safeRD} RD !!!)", style: TextStyle(color: Colors.redAccent))
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'package:intl/intl.dart';
|
|||
import 'package:tetra_stats/data_objects/tetrio.dart';
|
||||
import 'package:syncfusion_flutter_gauges/gauges.dart';
|
||||
import 'package:tetra_stats/gen/strings.g.dart';
|
||||
import 'package:tetra_stats/main.dart';
|
||||
import 'package:tetra_stats/utils/colors_functions.dart';
|
||||
import 'package:tetra_stats/utils/numers_formats.dart';
|
||||
import 'package:tetra_stats/utils/relative_timestamps.dart';
|
||||
|
@ -19,15 +20,15 @@ import 'package:tetra_stats/widgets/tl_rating_thingy.dart';
|
|||
var intFDiff = NumberFormat("+#,###.000;-#,###.000");
|
||||
|
||||
class TLThingy extends StatefulWidget {
|
||||
final TetraLeagueAlpha tl;
|
||||
final TetraLeague tl;
|
||||
final String userID;
|
||||
final List<TetrioPlayer> states;
|
||||
final List<TetraLeague> states;
|
||||
final bool showTitle;
|
||||
final bool bot;
|
||||
final bool guest;
|
||||
final double? topTR;
|
||||
final PlayerLeaderboardPosition? lbPositions;
|
||||
final TetraLeagueAlpha? averages;
|
||||
final TetraLeague? averages;
|
||||
final double? thatRankCutoff;
|
||||
final double? thatRankCutoffGlicko;
|
||||
final double? thatRankTarget;
|
||||
|
@ -43,33 +44,22 @@ class TLThingy extends StatefulWidget {
|
|||
|
||||
class _TLThingyState extends State<TLThingy> with TickerProviderStateMixin {
|
||||
late bool oskKagariGimmick;
|
||||
late TetraLeagueAlpha? oldTl;
|
||||
late TetraLeagueAlpha currentTl;
|
||||
late TetraLeague? oldTl;
|
||||
late TetraLeague currentTl;
|
||||
late RangeValues _currentRangeValues;
|
||||
late List<TetrioPlayer> sortedStates;
|
||||
late Timer _countdownTimer;
|
||||
Duration seasonLeft = seasonEnd.difference(DateTime.now());
|
||||
|
||||
late List<TetraLeague> sortedStates;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_currentRangeValues = const RangeValues(0, 1);
|
||||
sortedStates = widget.states.reversed.toList();
|
||||
oldTl = sortedStates.elementAtOrNull(1)?.tlSeason1;
|
||||
oldTl = sortedStates.elementAtOrNull(1);
|
||||
currentTl = widget.tl;
|
||||
super.initState();
|
||||
_countdownTimer = Timer.periodic(
|
||||
Durations.extralong4,
|
||||
(Timer timer) {
|
||||
setState(() {
|
||||
seasonLeft = seasonEnd.difference(DateTime.now());
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_countdownTimer.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
@ -90,8 +80,8 @@ class _TLThingyState extends State<TLThingy> with TickerProviderStateMixin {
|
|||
return Column(
|
||||
children: [
|
||||
if (widget.showTitle) Text(t.tetraLeague, style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)),
|
||||
if (DateTime.now().isBefore(seasonEnd)) Text(t.seasonEnds(countdown: countdown(seasonLeft)))
|
||||
else Text(t.seasonEnded),
|
||||
//if (DateTime.now().isBefore(seasonEnd)) Text(t.seasonEnds(countdown: countdown(seasonLeft)))
|
||||
//else Text(t.seasonEnded),
|
||||
if (oldTl != null) Text(t.comparingWith(newDate: timestamp(currentTl.timestamp), oldDate: timestamp(oldTl!.timestamp)),
|
||||
textAlign: TextAlign.center,),
|
||||
if (oldTl != null) RangeSlider(values: _currentRangeValues, max: widget.states.length.toDouble(),
|
||||
|
@ -105,37 +95,26 @@ class _TLThingyState extends State<TLThingy> with TickerProviderStateMixin {
|
|||
if (values.start.round() == 0){
|
||||
currentTl = widget.tl;
|
||||
}else{
|
||||
currentTl = sortedStates[values.start.round()-1].tlSeason1;
|
||||
currentTl = sortedStates[values.start.round()-1]!;
|
||||
}
|
||||
if (values.end.round() == 0){
|
||||
oldTl = widget.tl;
|
||||
}else{
|
||||
oldTl = sortedStates[values.end.round()-1].tlSeason1;
|
||||
oldTl = sortedStates[values.end.round()-1];
|
||||
}
|
||||
});
|
||||
},
|
||||
),
|
||||
if (currentTl.gamesPlayed > 9) TLRatingThingy(userID: widget.userID, tlData: currentTl, oldTl: oldTl, topTR: widget.topTR, lastMatchPlayed: widget.lastMatchPlayed),
|
||||
TLRatingThingy(userID: widget.userID, tlData: currentTl, oldTl: oldTl, topTR: widget.topTR, lastMatchPlayed: widget.lastMatchPlayed),
|
||||
if (currentTl.gamesPlayed > 9) TLProgress(
|
||||
tlData: currentTl,
|
||||
previousRankTRcutoff: widget.thatRankCutoff,
|
||||
previousGlickoCutoff: widget.thatRankCutoffGlicko,
|
||||
previousRank: widget.tl.prevRank,
|
||||
previousRankTRcutoffTarget: widget.thatRankTarget,
|
||||
nextRankTRcutoff: widget.nextRankCutoff,
|
||||
nextRankGlickoCutoff: widget.nextRankCutoffGlicko,
|
||||
nextRankTRcutoffTarget: widget.nextRankTarget,
|
||||
nextRank: widget.tl.nextRank
|
||||
),
|
||||
if (currentTl.gamesPlayed < 10)
|
||||
Text(t.gamesUntilRanked(left: 10 - currentTl.gamesPlayed),
|
||||
softWrap: true,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontFamily: "Eurostile Round",
|
||||
fontSize: bigScreen ? 42 : 28,
|
||||
overflow: TextOverflow.visible,
|
||||
)),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(8, 16, 8, 48),
|
||||
child: Wrap(
|
||||
|
|
|
@ -182,7 +182,6 @@ class UserThingy extends StatelessWidget {
|
|||
],),
|
||||
onPressed: () {
|
||||
teto.addPlayerToTrack(player).then((value) => setState());
|
||||
teto.storeState(player);
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.becameTracked)));
|
||||
},
|
||||
),
|
||||
|
@ -213,7 +212,7 @@ class UserThingy extends StatelessWidget {
|
|||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => CompareView(greenSide: [player, null, player.tlSeason1], redSide: const [null, null, null], greenMode: Mode.player, redMode: Mode.player),
|
||||
builder: (context) => CompareView(greenSide: [player, null, null], redSide: const [null, null, null], greenMode: Mode.player, redMode: Mode.player),
|
||||
),
|
||||
);
|
||||
},
|
||||
|
@ -239,7 +238,7 @@ class UserThingy extends StatelessWidget {
|
|||
crossAxisAlignment: WrapCrossAlignment.start,
|
||||
clipBehavior: Clip.hardEdge, // hard WHAT???
|
||||
children: [
|
||||
StatCellNum(
|
||||
if (!player.level.isNegative && !player.level.isNaN) StatCellNum(
|
||||
playerStat: player.level,
|
||||
playerStatLabel: t.statCellNum.xpLevel,
|
||||
isScreenBig: bigScreen,
|
||||
|
|
|
@ -0,0 +1,283 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:syncfusion_flutter_gauges/gauges.dart';
|
||||
import 'package:tetra_stats/data_objects/tetrio.dart';
|
||||
import 'package:tetra_stats/gen/strings.g.dart';
|
||||
import 'package:tetra_stats/utils/colors_functions.dart';
|
||||
import 'package:tetra_stats/utils/numers_formats.dart';
|
||||
import 'package:tetra_stats/utils/relative_timestamps.dart';
|
||||
import 'package:tetra_stats/utils/text_shadow.dart';
|
||||
import 'package:tetra_stats/widgets/finesse_thingy.dart';
|
||||
import 'package:tetra_stats/widgets/gauget_num.dart';
|
||||
import 'package:tetra_stats/widgets/graphs.dart';
|
||||
import 'package:tetra_stats/widgets/lineclears_thingy.dart';
|
||||
import 'package:tetra_stats/widgets/stat_sell_num.dart';
|
||||
import 'package:tetra_stats/widgets/text_timestamp.dart';
|
||||
|
||||
class ZenithThingy extends StatefulWidget{
|
||||
final RecordSingle? record;
|
||||
final bool switchable;
|
||||
final bool initEXvalue;
|
||||
final RecordSingle? recordEX;
|
||||
final Function? parentZenithToggle;
|
||||
|
||||
const ZenithThingy({super.key, this.record, this.recordEX, this.switchable = true, this.parentZenithToggle, this.initEXvalue = false});
|
||||
|
||||
@override
|
||||
State<ZenithThingy> createState() => _ZenithThingyState();
|
||||
}
|
||||
|
||||
class _ZenithThingyState extends State<ZenithThingy> {
|
||||
late RecordSingle? record;
|
||||
bool ex = false;
|
||||
|
||||
@override
|
||||
void initState(){
|
||||
ex = widget.initEXvalue;
|
||||
|
||||
super.initState();
|
||||
if (widget.switchable){
|
||||
record = (ex ? widget.recordEX : widget.record);
|
||||
}else{
|
||||
record = widget.record;
|
||||
ex = widget.record!.gamemode == "zenithex";
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return LayoutBuilder(builder: (context, constraints){
|
||||
bool bigScreen = constraints.maxWidth > 768;
|
||||
if (record == null) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: Column(
|
||||
children: [
|
||||
Text("${t.quickPlay}${ex ? " ${t.expert}" : ""}", style: const TextStyle(height: 0.1, fontFamily: "Eurostile Round Extended", fontSize: 18)),
|
||||
RichText(text: TextSpan(
|
||||
text: "--- m",
|
||||
style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 36 : 32, fontWeight: FontWeight.w500, color: Colors.grey),
|
||||
),
|
||||
),
|
||||
TextButton(onPressed: (){
|
||||
if (ex){
|
||||
ex = false;
|
||||
}else{
|
||||
ex = true;
|
||||
}
|
||||
setState(() {
|
||||
if (widget.parentZenithToggle != null) widget.parentZenithToggle!();
|
||||
record = ex ? widget.recordEX : widget.record;
|
||||
});
|
||||
}, child: Text(ex ? "Switch to normal" : "Switch to Expert")),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
return Padding(padding: const EdgeInsets.only(top: 8.0),
|
||||
child: Column(
|
||||
children: [
|
||||
Text("${t.quickPlay}${ex ? " ${t.expert}" : ""}", style: const TextStyle(height: 0.1, fontFamily: "Eurostile Round Extended", fontSize: 18)),
|
||||
RichText(text: TextSpan(
|
||||
text: "${f2.format(record!.stats.zenith!.altitude)} m",
|
||||
style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 36 : 32, fontWeight: FontWeight.w500, color: Colors.white),
|
||||
),
|
||||
),
|
||||
if ((record!.extras as ZenithExtras).mods.isNotEmpty) RichText(
|
||||
text: TextSpan(
|
||||
text: "",
|
||||
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, color: Colors.white),
|
||||
children: [
|
||||
TextSpan(text: "${t.withMods}: "),
|
||||
for (String mod in (record!.extras as ZenithExtras).mods) TextSpan(text: "${mod.toUpperCase()} "),
|
||||
]
|
||||
),
|
||||
),
|
||||
RichText(
|
||||
text: TextSpan(
|
||||
text: "",
|
||||
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, color: Colors.grey),
|
||||
children: [
|
||||
if (record!.rank != -1) TextSpan(text: "№ ${intf.format(record!.rank)}", style: TextStyle(color: getColorOfRank(record!.rank))),
|
||||
if (record!.rank != -1) const TextSpan(text: " • "),
|
||||
if (record!.countryRank != -1) TextSpan(text: "№ ${intf.format(record!.countryRank)} local", style: TextStyle(color: getColorOfRank(record!.countryRank))),
|
||||
if (record!.countryRank != -1) const TextSpan(text: " • "),
|
||||
TextSpan(text: timestamp(widget.record!.timestamp)),
|
||||
]
|
||||
),
|
||||
),
|
||||
if (widget.switchable) TextButton(onPressed: (){
|
||||
if (ex){
|
||||
ex = false;
|
||||
}else{
|
||||
ex = true;
|
||||
}
|
||||
setState(() {
|
||||
if (widget.parentZenithToggle != null) widget.parentZenithToggle!();
|
||||
record = ex ? widget.recordEX : widget.record;
|
||||
});
|
||||
}, child: Text(ex ? "Switch to normal" : "Switch to Expert")),
|
||||
Wrap(
|
||||
alignment: WrapAlignment.center,
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
spacing: 20,
|
||||
children: [
|
||||
StatCellNum(playerStat: record!.aggregateStats.apm, playerStatLabel: t.statCellNum.apm, fractionDigits: 2, isScreenBig: bigScreen, higherIsBetter: true, smallDecimal: true),
|
||||
StatCellNum(playerStat: record!.aggregateStats.pps, playerStatLabel: t.statCellNum.pps, fractionDigits: 2, isScreenBig: bigScreen, higherIsBetter: true, smallDecimal: false),
|
||||
StatCellNum(playerStat: record!.aggregateStats.vs, playerStatLabel: t.statCellNum.vs, fractionDigits: 2, isScreenBig: bigScreen, higherIsBetter: true, smallDecimal: true),
|
||||
StatCellNum(playerStat: record!.stats.kills, playerStatLabel: "KO's", isScreenBig: bigScreen, higherIsBetter: true),
|
||||
StatCellNum(playerStat: record!.stats.cps, playerStatLabel: "Climb speed\n(Peak: ${f2.format(record!.stats.zenith!.peakrank)})", fractionDigits: 2, isScreenBig: bigScreen, higherIsBetter: true),
|
||||
StatCellNum(playerStat: record!.stats.topBtB, playerStatLabel: "Top B2B\nchain", isScreenBig: bigScreen, higherIsBetter: true)
|
||||
],
|
||||
),
|
||||
FinesseThingy(record?.stats.finesse, record?.stats.finessePercentage),
|
||||
LineclearsThingy(record!.stats.clears, record!.stats.lines, record!.stats.holds, record!.stats.tSpins),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: SizedBox(
|
||||
width: 300,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Stack(
|
||||
alignment: AlignmentDirectional.bottomStart,
|
||||
children: [
|
||||
const Text("T", style: TextStyle(
|
||||
fontStyle: FontStyle.italic,
|
||||
fontSize: 65,
|
||||
height: 1.2,
|
||||
)),
|
||||
const Positioned(left: 25, top: 20, child: Text("otal time", style: TextStyle(fontFamily: "Eurostile Round Extended"))),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 10.0),
|
||||
child: Text(getMoreNormalTime(record!.stats.finalTime), style: const TextStyle(
|
||||
shadows: textShadow,
|
||||
fontFamily: "Eurostile Round Extended",
|
||||
fontSize: 36,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.white
|
||||
)),
|
||||
)
|
||||
],
|
||||
),
|
||||
Table(
|
||||
columnWidths: const {
|
||||
0: FixedColumnWidth(36)
|
||||
},
|
||||
children: [
|
||||
const TableRow(
|
||||
children: [
|
||||
Text("Floor"),
|
||||
Text("Split", textAlign: TextAlign.right),
|
||||
Text("Total", textAlign: TextAlign.right),
|
||||
]
|
||||
),
|
||||
for (int i = 0; i < record!.stats.zenith!.splits.length; i++) TableRow(
|
||||
children: [
|
||||
Text((i+1).toString()),
|
||||
Text(record!.stats.zenith!.splits[i] != Duration.zero ? getMoreNormalTime(record!.stats.zenith!.splits[i]-(i-1 != -1 ? record!.stats.zenith!.splits[i-1] : Duration.zero)) : "--:--.---", textAlign: TextAlign.right),
|
||||
Text(record!.stats.zenith!.splits[i] != Duration.zero ? getMoreNormalTime(record!.stats.zenith!.splits[i]) : "--:--.---", textAlign: TextAlign.right),
|
||||
]
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
Text(t.nerdStats, style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(0, 40, 0, 0),
|
||||
child: Wrap(
|
||||
direction: Axis.horizontal,
|
||||
alignment: WrapAlignment.center,
|
||||
spacing: 35,
|
||||
crossAxisAlignment: WrapCrossAlignment.start,
|
||||
//clipBehavior: Clip.hardEdge,
|
||||
children: [
|
||||
GaugetNum(playerStat: record!.aggregateStats.nerdStats.app, playerStatLabel: t.statCellNum.app, higherIsBetter: true, minimum: 0, maximum: 1, ranges: [
|
||||
GaugeRange(startValue: 0, endValue: 0.2, color: Colors.red),
|
||||
GaugeRange(startValue: 0.2, endValue: 0.4, color: Colors.yellow),
|
||||
GaugeRange(startValue: 0.4, endValue: 0.6, color: Colors.green),
|
||||
GaugeRange(startValue: 0.6, endValue: 0.8, color: Colors.blue),
|
||||
GaugeRange(startValue: 0.8, endValue: 1, color: Colors.purple),
|
||||
], alertWidgets: [
|
||||
Text(t.statCellNum.appDescription),
|
||||
Text("${t.exactValue}: ${record!.aggregateStats.nerdStats.app}")
|
||||
]),
|
||||
GaugetNum(playerStat: record!.aggregateStats.nerdStats.vsapm, playerStatLabel: "VS / APM", higherIsBetter: true, minimum: 1.8, maximum: 2.4, ranges: [
|
||||
GaugeRange(startValue: 1.8, endValue: 2.0, color: Colors.green),
|
||||
GaugeRange(startValue: 2.0, endValue: 2.2, color: Colors.blue),
|
||||
GaugeRange(startValue: 2.2, endValue: 2.4, color: Colors.purple),
|
||||
], alertWidgets: [
|
||||
Text(t.statCellNum.vsapmDescription),
|
||||
Text("${t.exactValue}: ${record!.aggregateStats.nerdStats.vsapm}")
|
||||
])
|
||||
]),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(8, 0, 8, 0),
|
||||
child: Wrap(
|
||||
direction: Axis.horizontal,
|
||||
alignment: WrapAlignment.center,
|
||||
spacing: 25,
|
||||
crossAxisAlignment: WrapCrossAlignment.start,
|
||||
//clipBehavior: Clip.hardEdge,
|
||||
children: [
|
||||
StatCellNum(playerStat: record!.aggregateStats.nerdStats.dss, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: t.statCellNum.dss,
|
||||
alertWidgets: [Text(t.statCellNum.dssDescription),
|
||||
Text("${t.formula}: (VS / 100) - (APM / 60)"),
|
||||
Text("${t.exactValue}: ${record!.aggregateStats.nerdStats.dss}"),],
|
||||
okText: t.popupActions.ok,
|
||||
higherIsBetter: true,),
|
||||
StatCellNum(playerStat: record!.aggregateStats.nerdStats.dsp, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: t.statCellNum.dsp,
|
||||
alertWidgets: [Text(t.statCellNum.dspDescription),
|
||||
Text("${t.formula}: DS/S / PPS"),
|
||||
Text("${t.exactValue}: ${record!.aggregateStats.nerdStats.dsp}"),],
|
||||
okText: t.popupActions.ok,
|
||||
higherIsBetter: true),
|
||||
StatCellNum(playerStat: record!.aggregateStats.nerdStats.appdsp, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: t.statCellNum.appdsp,
|
||||
alertWidgets: [Text(t.statCellNum.appdspDescription),
|
||||
Text("${t.formula}: APP + DS/P"),
|
||||
Text("${t.exactValue}: ${record!.aggregateStats.nerdStats.appdsp}"),],
|
||||
okText: t.popupActions.ok,
|
||||
higherIsBetter: true),
|
||||
StatCellNum(playerStat: record!.aggregateStats.nerdStats.cheese, isScreenBig: bigScreen, fractionDigits: 2, playerStatLabel: t.statCellNum.cheese,
|
||||
alertWidgets: [Text(t.statCellNum.cheeseDescription),
|
||||
Text("${t.formula}: (DS/P * 150) + ((VS/APM - 2) * 50) + (0.6 - APP) * 125"),
|
||||
Text("${t.exactValue}: ${record!.aggregateStats.nerdStats.cheese}"),],
|
||||
okText: t.popupActions.ok,
|
||||
higherIsBetter: false),
|
||||
StatCellNum(playerStat: record!.aggregateStats.nerdStats.gbe, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: t.statCellNum.gbe,
|
||||
alertWidgets: [Text(t.statCellNum.gbeDescription),
|
||||
Text("${t.formula}: APP * DS/P * 2"),
|
||||
Text("${t.exactValue}: ${record!.aggregateStats.nerdStats.gbe}"),],
|
||||
okText: t.popupActions.ok,
|
||||
higherIsBetter: true),
|
||||
StatCellNum(playerStat: record!.aggregateStats.nerdStats.nyaapp, isScreenBig: bigScreen, fractionDigits: 3, playerStatLabel: t.statCellNum.nyaapp,
|
||||
alertWidgets: [Text(t.statCellNum.nyaappDescription),
|
||||
Text("${t.formula}: APP - 5 * tan(radians((Cheese Index / -30) + 1))"),
|
||||
Text("${t.exactValue}: ${record!.aggregateStats.nerdStats.nyaapp}")],
|
||||
okText: t.popupActions.ok,
|
||||
higherIsBetter: true),
|
||||
StatCellNum(playerStat: record!.aggregateStats.nerdStats.area, isScreenBig: bigScreen, fractionDigits: 1, playerStatLabel: t.statCellNum.area,
|
||||
alertWidgets: [Text(t.statCellNum.areaDescription),
|
||||
Text("${t.formula}: APM * 1 + PPS * 45 + VS * 0.444 + APP * 185 + DS/S * 175 + DS/P * 450 + Garbage Effi * 315"),
|
||||
Text("${t.exactValue}: ${record!.aggregateStats.nerdStats.area}"),],
|
||||
okText: t.popupActions.ok,
|
||||
higherIsBetter: true)
|
||||
]),
|
||||
)
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 16.0),
|
||||
child: Graphs(record!.aggregateStats.apm, record!.aggregateStats.pps, record!.aggregateStats.vs, record!.aggregateStats.nerdStats, record!.aggregateStats.playstyle),
|
||||
)
|
||||
],
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
216
pubspec.lock
|
@ -21,18 +21,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: archive
|
||||
sha256: "22600aa1e926be775fa5fe7e6894e7fb3df9efda8891c73f70fb3262399a432d"
|
||||
sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.4.10"
|
||||
version: "3.6.1"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: args
|
||||
sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596
|
||||
sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.2"
|
||||
version: "2.5.0"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -101,10 +101,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: coverage
|
||||
sha256: "8acabb8306b57a409bf4c83522065672ee13179297a6bb0cb9ead73948df7c76"
|
||||
sha256: "576aaab8b1abdd452e0f656c3e73da9ead9d7880e15bdc494189d9c1a1baf0db"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.7.2"
|
||||
version: "1.9.0"
|
||||
cross_file:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -133,18 +133,18 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: cupertino_icons
|
||||
sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d
|
||||
sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.6"
|
||||
version: "1.0.8"
|
||||
dev_build:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: dev_build
|
||||
sha256: e5d575f3de4b0e5f004e065e1e2d98fa012d634b61b5855216b5698ed7f1e443
|
||||
sha256: f526d1fbe68875f6119ffc333f114dfe6aa93ad04439276d53968f7977cc410e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.16.4+3"
|
||||
version: "1.0.0+11"
|
||||
equatable:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -197,18 +197,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: file_selector_android
|
||||
sha256: "1cd66575f063b689e041aec836905ba7be18d76c9f0634d0d75daec825f67095"
|
||||
sha256: d1e8655c1a4850a900a0cfaed55fdd273881d53a4bb78e4736dc170a0b17db78
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.0+7"
|
||||
version: "0.5.1+5"
|
||||
file_selector_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file_selector_ios
|
||||
sha256: b015154e6d9fddbc4d08916794df170b44531798c8dd709a026df162d07ad81d
|
||||
sha256: "38ebf91ecbcfa89a9639a0854ccaed8ab370c75678938eebca7d34184296f0bb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.1+8"
|
||||
version: "0.5.3"
|
||||
file_selector_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -221,10 +221,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: file_selector_macos
|
||||
sha256: b15c3da8bd4908b9918111fa486903f5808e388b8d1c559949f584725a6594d6
|
||||
sha256: f42eacb83b318e183b1ae24eead1373ab1334084404c8c16e0354f9a3e55d385
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.9.3+3"
|
||||
version: "0.9.4"
|
||||
file_selector_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -245,10 +245,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: file_selector_windows
|
||||
sha256: d3547240c20cabf205c7c7f01a50ecdbc413755814d6677f3cb366f04abcead0
|
||||
sha256: "2ad726953f6e8affbc4df8dc78b77c3b4a060967a291e528ef72ae846c60fb69"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.9.3+1"
|
||||
version: "0.9.3+2"
|
||||
fl_chart:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -266,10 +266,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_colorpicker
|
||||
sha256: "458a6ed8ea480eb16ff892aedb4b7092b2804affd7e046591fb03127e8d8ef8b"
|
||||
sha256: "969de5f6f9e2a570ac660fb7b501551451ea2a1ab9e2097e89475f60e07816ea"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.3"
|
||||
version: "1.1.0"
|
||||
flutter_launcher_icons:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
|
@ -282,10 +282,10 @@ packages:
|
|||
dependency: "direct dev"
|
||||
description:
|
||||
name: flutter_lints
|
||||
sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7
|
||||
sha256: "9e8c3858111da373efc5aa341de011d9bd23e2c5c5e0c62bccf32438e192d7b1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
version: "3.0.2"
|
||||
flutter_localizations:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
|
@ -295,18 +295,18 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_markdown
|
||||
sha256: "87e11b9df25a42e2db315b8b7a51fae8e66f57a4b2f50ec4b822d0fa155e6b52"
|
||||
sha256: "04c4722cc36ec5af38acc38ece70d22d3c2123c61305d555750a091517bbe504"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.22"
|
||||
version: "0.6.23"
|
||||
flutter_plugin_android_lifecycle:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_plugin_android_lifecycle
|
||||
sha256: b068ffc46f82a55844acfa4fdbb61fad72fa2aef0905548419d97f0f95c456da
|
||||
sha256: "9d98bd47ef9d34e803d438f17fd32b116d31009f534a6fa5ce3a1167f189a6de"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.17"
|
||||
version: "2.0.21"
|
||||
flutter_svg:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -329,10 +329,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: frontend_server_client
|
||||
sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612"
|
||||
sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.0"
|
||||
version: "4.0.0"
|
||||
glob:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -345,10 +345,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: go_router
|
||||
sha256: "7ecb2f391edbca5473db591b48555a8912dde60edd0fb3013bd6743033b2d3f8"
|
||||
sha256: b465e99ce64ba75e61c8c0ce3d87b66d8ac07f0b35d0a7e0263fcfc10f99e836
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "13.2.1"
|
||||
version: "13.2.5"
|
||||
http:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -377,18 +377,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: image
|
||||
sha256: "4c68bfd5ae83e700b5204c1e74451e7bf3cf750e6843c6e158289cf56bda018e"
|
||||
sha256: "2237616a36c0d69aef7549ab439b833fb7f9fb9fc861af2cc9ac3eedddd69ca8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.7"
|
||||
version: "4.2.0"
|
||||
intl:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: intl
|
||||
sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
|
||||
sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.18.1"
|
||||
version: "0.19.0"
|
||||
io:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -401,10 +401,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: js
|
||||
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
|
||||
sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.7"
|
||||
version: "0.7.1"
|
||||
json2yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -417,34 +417,34 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: json_annotation
|
||||
sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467
|
||||
sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.8.1"
|
||||
version: "4.9.0"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa"
|
||||
sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.0.0"
|
||||
version: "10.0.4"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0
|
||||
sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
version: "3.0.3"
|
||||
leak_tracker_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_testing
|
||||
sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47
|
||||
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
version: "3.0.1"
|
||||
lints:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -489,10 +489,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04
|
||||
sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.11.0"
|
||||
version: "1.12.0"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -553,26 +553,26 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: path_provider
|
||||
sha256: b27217933eeeba8ff24845c34003b003b2b22151de3c908d0e679e8fe1aa078b
|
||||
sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
version: "2.1.4"
|
||||
path_provider_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_android
|
||||
sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668"
|
||||
sha256: "490539678396d4c3c0b06efdaab75ae60675c3e0c66f72bc04c2e2c1e0e2abeb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.2"
|
||||
version: "2.2.9"
|
||||
path_provider_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_foundation
|
||||
sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f"
|
||||
sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.2"
|
||||
version: "2.4.0"
|
||||
path_provider_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -593,10 +593,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_windows
|
||||
sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170"
|
||||
sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.1"
|
||||
version: "2.3.0"
|
||||
petitparser:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -609,10 +609,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: platform
|
||||
sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec"
|
||||
sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.4"
|
||||
version: "3.1.5"
|
||||
plugin_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -621,14 +621,6 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.8"
|
||||
pointycastle:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pointycastle
|
||||
sha256: "43ac87de6e10afabc85c445745a7b799e04de84cebaa4fd7bf55a5e1e9604d29"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.7.4"
|
||||
pool:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -641,10 +633,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: process_run
|
||||
sha256: "8d9c6198b98fbbfb511edd42e7364e24d85c163e47398919871b952dc86a423e"
|
||||
sha256: c917dfb5f7afad4c7485bc00a4df038621248fce046105020cea276d1a87c820
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.14.2"
|
||||
version: "1.1.0"
|
||||
pub_semver:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -665,42 +657,42 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: shared_preferences
|
||||
sha256: "81429e4481e1ccfb51ede496e916348668fd0921627779233bd24cc3ff6abd02"
|
||||
sha256: d3bbe5553a986e83980916ded2f0b435ef2e1893dfaa29d5a7a790d0eca12180
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.2"
|
||||
version: "2.2.3"
|
||||
shared_preferences_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_android
|
||||
sha256: "8568a389334b6e83415b6aae55378e158fbc2314e074983362d20c562780fb06"
|
||||
sha256: "041be4d9d2dc6079cf342bc8b761b03787e3b71192d658220a56cac9c04a0294"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.1"
|
||||
version: "2.3.0"
|
||||
shared_preferences_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_foundation
|
||||
sha256: "7708d83064f38060c7b39db12aefe449cb8cdc031d6062280087bc4cdb988f5c"
|
||||
sha256: "671e7a931f55a08aa45be2a13fe7247f2a41237897df434b30d2012388191833"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.5"
|
||||
version: "2.5.0"
|
||||
shared_preferences_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_linux
|
||||
sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa"
|
||||
sha256: "2ba0510d3017f91655b7543e9ee46d48619de2a2af38e5c790423f7007c7ccc1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.2"
|
||||
version: "2.4.0"
|
||||
shared_preferences_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_platform_interface
|
||||
sha256: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b"
|
||||
sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.2"
|
||||
version: "2.4.1"
|
||||
shared_preferences_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -713,10 +705,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_windows
|
||||
sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59"
|
||||
sha256: "398084b47b7f92110683cac45c6dc4aae853db47e470e5ddcd52cab7f7196ab2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.2"
|
||||
version: "2.4.0"
|
||||
shelf:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -758,18 +750,18 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: slang
|
||||
sha256: "5e08ac915ac27a3508863f37734280d30c3713d56746cd2e4a5da77413da4b95"
|
||||
sha256: f68f6d6709890f85efabfb0318e9d694be2ebdd333e57fe5cb50eee449e4e3ab
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.30.1"
|
||||
version: "3.31.1"
|
||||
slang_flutter:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: slang_flutter
|
||||
sha256: "9ee040b0d364d3a4d692e4af536acff6ef513870689403494ebc6d59b0dccea6"
|
||||
sha256: f8400292be49c11697d94af58d7f7d054c91af759f41ffe71e4e5413871ffc62
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.30.0"
|
||||
version: "3.31.0"
|
||||
source_map_stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -798,10 +790,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: sqflite
|
||||
sha256: a9016f495c927cb90557c909ff26a6d92d9bd54fc42ba92e19d4e79d61e798c6
|
||||
sha256: a43e5a27235518c03ca238e7b4732cf35eabe863a369ceba6cbefa537a66f16d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.2"
|
||||
version: "2.3.3+1"
|
||||
sqflite_common:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -830,18 +822,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: sqlite3
|
||||
sha256: "072128763f1547e3e9b4735ce846bfd226d68019ccda54db4cd427b12dfdedc9"
|
||||
sha256: "1abbeb84bf2b1a10e5e1138c913123c8aa9d83cd64e5f9a0dd847b3c83063202"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.0"
|
||||
version: "2.4.2"
|
||||
sqlite3_flutter_libs:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: sqlite3_flutter_libs
|
||||
sha256: d6c31c8511c441d1f12f20b607343df1afe4eddf24a1cf85021677c8eea26060
|
||||
sha256: "62bbb4073edbcdf53f40c80775f33eea01d301b7b81417e5b3fb7395416258c1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.20"
|
||||
version: "0.5.24"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -910,26 +902,26 @@ packages:
|
|||
dependency: "direct dev"
|
||||
description:
|
||||
name: test
|
||||
sha256: a1f7595805820fcc05e5c52e3a231aedd0b72972cb333e8c738a8b1239448b6f
|
||||
sha256: "7ee446762c2c50b3bd4ea96fe13ffac69919352bd3b4b17bac3f3465edc58073"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.24.9"
|
||||
version: "1.25.2"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
|
||||
sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.1"
|
||||
version: "0.7.0"
|
||||
test_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_core
|
||||
sha256: a757b14fc47507060a162cc2530d9a4a2f92f5100a952c7443b5cad5ef5b106a
|
||||
sha256: "2bc4b4ecddd75309300d8096f781c0e3280ca1ef85beda558d33fcbedc2eead4"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.9"
|
||||
version: "0.6.0"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -942,26 +934,26 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: url_launcher
|
||||
sha256: "0ecc004c62fd3ed36a2ffcbe0dd9700aee63bd7532d0b642a488b1ec310f492e"
|
||||
sha256: "21b704ce5fa560ea9f3b525b43601c678728ba46725bab9b01187b4831377ed3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.2.5"
|
||||
version: "6.3.0"
|
||||
url_launcher_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_android
|
||||
sha256: d4ed0711849dd8e33eb2dd69c25db0d0d3fdc37e0a62e629fe32f57a22db2745
|
||||
sha256: "94d8ad05f44c6d4e2ffe5567ab4d741b82d62e3c8e288cc1fcea45965edf47c9"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.3.0"
|
||||
version: "6.3.8"
|
||||
url_launcher_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_ios
|
||||
sha256: "9149d493b075ed740901f3ee844a38a00b33116c7c5c10d7fb27df8987fb51d5"
|
||||
sha256: e43b677296fadce447e987a2f519dcf5f6d1e527dc35d01ffab4fff5b8a7063e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.2.5"
|
||||
version: "6.3.1"
|
||||
url_launcher_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -974,10 +966,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_macos
|
||||
sha256: b7244901ea3cf489c5335bdacda07264a6e960b1c1b1a9f91e4bc371d9e68234
|
||||
sha256: "9a1a42d5d2d95400c795b2914c36fdcb525870c752569438e4ebb09a2b5d90de"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.0"
|
||||
version: "3.2.0"
|
||||
url_launcher_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -998,10 +990,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_windows
|
||||
sha256: ecf9725510600aa2bb6d7ddabe16357691b6d2805f66216a97d1b881e21beff7
|
||||
sha256: "49c10f879746271804767cb45551ec5592cdab00ee105c06dddde1a98f73b185"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.1"
|
||||
version: "3.1.2"
|
||||
vector_graphics:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -1038,10 +1030,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957
|
||||
sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "13.0.0"
|
||||
version: "14.2.1"
|
||||
watcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -1078,18 +1070,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: win32
|
||||
sha256: "8cb58b45c47dcb42ab3651533626161d6b67a2921917d8d429791f76972b3480"
|
||||
sha256: "015002c060f1ae9f41a818f2d5640389cc05283e368be19dc8d77cecb43c40c9"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.3.0"
|
||||
version: "5.5.3"
|
||||
window_manager:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: window_manager
|
||||
sha256: b3c895bdf936c77b83c5254bec2e6b3f066710c1f89c38b20b8acc382b525494
|
||||
sha256: "8699323b30da4cdbe2aa2e7c9de567a6abd8a97d9a5c850a3c86dcd0b34bbfbf"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.8"
|
||||
version: "0.3.9"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -1115,5 +1107,5 @@ packages:
|
|||
source: hosted
|
||||
version: "3.1.2"
|
||||
sdks:
|
||||
dart: ">=3.3.0 <4.0.0"
|
||||
flutter: ">=3.19.0"
|
||||
dart: ">=3.4.0 <4.0.0"
|
||||
flutter: ">=3.22.0"
|
||||
|
|
|
@ -2,7 +2,7 @@ name: tetra_stats
|
|||
description: Track your and other player stats in TETR.IO
|
||||
publish_to: 'none'
|
||||
|
||||
version: 1.6.2+22
|
||||
version: 1.6.9+35
|
||||
|
||||
environment:
|
||||
sdk: '>=3.0.0'
|
||||
|
@ -30,7 +30,7 @@ dependencies:
|
|||
fl_chart: ^0.66.0
|
||||
package_info_plus: ^5.0.1
|
||||
shared_preferences: ^2.1.1
|
||||
intl: ^0.18.0
|
||||
intl: ^0.19.0
|
||||
syncfusion_flutter_gauges: ^24.1.41
|
||||
file_selector: ^1.0.1
|
||||
file_picker: ^6.1.1
|
||||
|
|
|
@ -87,6 +87,9 @@
|
|||
"verdictBetter": "better",
|
||||
"verdictWorse": "worse",
|
||||
"smooth": "Smooth",
|
||||
"postSeason": "Off-season",
|
||||
"seasonStarts": "Season starts in:",
|
||||
"nanow": "Not avaliable for now...",
|
||||
"seasonEnds": "Season ends in ${countdown}",
|
||||
"seasonEnded": "Season has ended",
|
||||
"gamesUntilRanked": "${left} games until being ranked",
|
||||
|
@ -101,6 +104,17 @@
|
|||
"neverPlayedTL": "That user never played Tetra League",
|
||||
"botTL": "Bots are not allowed to play Tetra League",
|
||||
"anonTL": "Guests are not allowed to play Tetra League",
|
||||
"quickPlay": "Quick Play",
|
||||
"expert": "Expert",
|
||||
"withMods": "With mods",
|
||||
"withModsPlural":{
|
||||
"zero": "with $n mods",
|
||||
"one": "with $n mod",
|
||||
"two": "with $n mods",
|
||||
"few": "with $n mods",
|
||||
"many": "with $n mods",
|
||||
"other": "with $n mods"
|
||||
},
|
||||
"exportDB": "Export local database",
|
||||
"exportDBDescription": "It contains states and Tetra League records of the tracked players and list of tracked players.",
|
||||
"desktopExportAlertTitle": "Desktop export",
|
||||
|
@ -141,7 +155,7 @@
|
|||
"stateViewTitle": "${nickname} account on ${date}",
|
||||
"statesViewTitle": "${number} states of ${nickname} account",
|
||||
"matchesViewTitle": "${nickname} TL matches",
|
||||
"statesViewEntry": "Level ${level}, ${gameTime} of gametime, ${friends} friends, ${rd} RD",
|
||||
"statesViewEntry": "${level} TR, ${glicko}±${rd} Glicko, ${games} игр сыграно",
|
||||
"stateRemoved": "${date} state was removed from database!",
|
||||
"matchRemoved": "${date} match was removed from database!",
|
||||
"viewAllMatches": "View all matches",
|
||||
|
@ -255,7 +269,7 @@
|
|||
"lbpcShort": "№ in local LB",
|
||||
"gamesPlayed": "Games\nplayed",
|
||||
"gamesWonTL": "Games\nWon",
|
||||
"winrate": "Winrate\nprecentage",
|
||||
"winrate": "Winrate",
|
||||
"level": "Level",
|
||||
"score": "Score",
|
||||
"spp": "Score\nPer Piece",
|
||||
|
|
|
@ -87,6 +87,9 @@
|
|||
"verdictBetter": "Лучше",
|
||||
"verdictWorse": "Хуже",
|
||||
"smooth": "Гладкий",
|
||||
"postSeason": "Внесезонье",
|
||||
"seasonStarts": "Сезон начнётся через:",
|
||||
"nanow": "Пока недоступно...",
|
||||
"seasonEnds": "Сезон закончится через ${countdown}",
|
||||
"seasonEnded": "Сезон закончился",
|
||||
"gamesUntilRanked": "${left} матчей до получения рейтинга",
|
||||
|
@ -101,6 +104,17 @@
|
|||
"neverPlayedTL": "Этот игрок никогда не играл в Тетра Лигу",
|
||||
"botTL": "Ботам нельзя играть в Тетра Лигу",
|
||||
"anonTL": "Гостям нельзя играть в Тетра Лигу",
|
||||
"quickPlay": "Быстрая Игра",
|
||||
"expert": "Эксперт",
|
||||
"withMods": "С модами",
|
||||
"withModsPlural":{
|
||||
"zero": "с $n модами",
|
||||
"one": "с $n модом",
|
||||
"two": "с $n модами",
|
||||
"few": "с $n модами",
|
||||
"many": "с $n модами",
|
||||
"other": "с $n модами"
|
||||
},
|
||||
"exportDB": "Экспортировать локальную базу данных",
|
||||
"exportDBDescription": "Она содержит состояния аккаунтов и их матчей в Тетра Лиге для отслеживаемых игроков и список таких игроков.",
|
||||
"desktopExportAlertTitle": "Экспорт на десктопе",
|
||||
|
@ -141,7 +155,7 @@
|
|||
"stateViewTitle": "Аккаунт ${nickname} ${date}",
|
||||
"statesViewTitle": "${number} состояний аккаунта ${nickname}",
|
||||
"matchesViewTitle": "Матчи аккаунта ${nickname}",
|
||||
"statesViewEntry": "${level} уровень, ${gameTime} сыграно, ${friends} друзей, ${rd} RD",
|
||||
"statesViewEntry": "${level} TR, ${glicko}±${rd} Glicko, ${games} игр сыграно",
|
||||
"stateRemoved": "Состояние от ${date} было удалено из локальной базы данных!",
|
||||
"matchRemoved": "Матч от ${date} был удален из локальной базы данных!",
|
||||
"viewAllMatches": "Все матчи",
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="360mm"
|
||||
height="150mm"
|
||||
viewBox="0 0 360 150"
|
||||
version="1.1"
|
||||
id="svg95853"
|
||||
inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"
|
||||
sodipodi:docname="40l.svg">
|
||||
<defs
|
||||
id="defs95847" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.5"
|
||||
inkscape:cx="469.93678"
|
||||
inkscape:cy="-94.189212"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:pagecheckerboard="true"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1009"
|
||||
inkscape:window-x="1676"
|
||||
inkscape:window-y="-4"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata95850">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,3.0000027)">
|
||||
<path
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 0,87 H 60.000002 V 57 H 30.000001 V -3.0000027 H 0 Z"
|
||||
id="path100330-7"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccccc" />
|
||||
<rect
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:0.7962963;stroke:none;stroke-width:1.88264012;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect100347"
|
||||
width="120"
|
||||
height="30"
|
||||
x="27"
|
||||
y="-90"
|
||||
transform="rotate(90)" />
|
||||
<path
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 120,-3 h 60 v 30 h -30 v 60 h -30 z"
|
||||
id="path100330-0"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccccc" />
|
||||
<path
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:0.6712963;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 120,147 V 87 h 30 v 30 h 60 v 30 z"
|
||||
id="path100330-0-8"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccccc" />
|
||||
<rect
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:0.83333331;stroke:none;stroke-width:1.88264012;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect100347-6"
|
||||
width="120"
|
||||
height="30.000002"
|
||||
x="-117"
|
||||
y="180"
|
||||
transform="rotate(-90)" />
|
||||
<rect
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.88264012;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect100347-9"
|
||||
width="120"
|
||||
height="30.000002"
|
||||
x="-117"
|
||||
y="240"
|
||||
transform="rotate(-90)" />
|
||||
<rect
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:0.8287037;stroke:none;stroke-width:1.88264012;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect100347-9-9"
|
||||
width="120"
|
||||
height="30.000002"
|
||||
x="240"
|
||||
y="117" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 1.8 KiB |
|
@ -0,0 +1,114 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="390mm"
|
||||
height="150.00002mm"
|
||||
viewBox="0 0 390 150.00002"
|
||||
version="1.1"
|
||||
id="svg95853"
|
||||
inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"
|
||||
sodipodi:docname="blitz.svg">
|
||||
<defs
|
||||
id="defs95847" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.35355339"
|
||||
inkscape:cx="560.53089"
|
||||
inkscape:cy="314.22402"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:pagecheckerboard="true"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1009"
|
||||
inkscape:window-x="1676"
|
||||
inkscape:window-y="-4"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata95850">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(1e-6,3.0000076)">
|
||||
<path
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M -1e-6,-3.000002 H 59.999998 V 26.999999 H 29.999999 v 60 h -30 z"
|
||||
id="path100330-0-8"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccccc" />
|
||||
<rect
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.88264012;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect100347-9"
|
||||
width="120"
|
||||
height="30.000002"
|
||||
x="-116.99999"
|
||||
y="120"
|
||||
transform="rotate(-90)" />
|
||||
<rect
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:0.81944442;stroke:none;stroke-width:1.88264012;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect100347-9-9"
|
||||
width="120"
|
||||
height="30.000002"
|
||||
x="120"
|
||||
y="116.99999" />
|
||||
<path
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:0.7962963;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 89.999986,26.999997 -29.999988,2e-6 -10e-6,30 h -30 v 29.999999 l 30.000001,10e-7 V 117 h 30.000007 z"
|
||||
id="path100330-7-0"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccccccc" />
|
||||
<path
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:0.6712963;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M -1e-6,147 V 86.999999 h 30 V 117 h 60.000003 v 30 z"
|
||||
id="path100330-0-8-4"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccccc" />
|
||||
<path
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 360,-3.0000057 V 56.999997 H 330 V 26.999994 H 270 V -3.0000057 Z"
|
||||
id="path100330-0-8-49"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccccc" />
|
||||
<path
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:0.81018519;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 270,56.999997 V 117 h 29.99999 V 86.999995 H 360 V 56.999997 Z"
|
||||
id="path100330-7-8-9"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccccc" />
|
||||
<rect
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:0.64351851;stroke:none;stroke-width:1.88264012;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect100347-9-9-0"
|
||||
width="120"
|
||||
height="30.000002"
|
||||
x="270"
|
||||
y="117" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 4.6 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.1 KiB |
|
@ -0,0 +1,110 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="420mm"
|
||||
height="150mm"
|
||||
viewBox="0 0 420 150"
|
||||
version="1.1"
|
||||
id="svg95853"
|
||||
inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"
|
||||
sodipodi:docname="league.svg">
|
||||
<defs
|
||||
id="defs95847" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.25"
|
||||
inkscape:cx="259.85326"
|
||||
inkscape:cy="119.35595"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:pagecheckerboard="true"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1009"
|
||||
inkscape:window-x="1676"
|
||||
inkscape:window-y="-4"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata95850">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(1e-6,3.0000076)">
|
||||
<rect
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:0.82407409;stroke:none;stroke-width:1.88264012;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect100347-9"
|
||||
width="120"
|
||||
height="30.000002"
|
||||
x="-3.0000076"
|
||||
y="-119.99999"
|
||||
transform="rotate(90)" />
|
||||
<path
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 300.00001,-2.9999986 V 57.000001 H 330 v -30 h 60 V -2.9999986 Z"
|
||||
id="path100330-7-8-9"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccccc" />
|
||||
<path
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M -1e-6,56.999992 V -3.000006 h 29.99998 v 29.999998 h 60.00001 v 30 z"
|
||||
id="path100330-7-8-9-2"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccccc" />
|
||||
<rect
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.88264012;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect100347-9-1"
|
||||
width="120"
|
||||
height="30.000002"
|
||||
x="-2.9999986"
|
||||
y="-180"
|
||||
transform="rotate(90)" />
|
||||
<rect
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:0.81018519;stroke:none;stroke-width:1.88264012;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect100347-9-1-2"
|
||||
width="120"
|
||||
height="30.000002"
|
||||
x="-270"
|
||||
y="-147"
|
||||
transform="scale(-1)" />
|
||||
<path
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:0.8287037;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 300.00001,146.99999 h 60 V 117 h -30 V 57.000001 h -30 z"
|
||||
id="path100330-7-8-9-3"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccccc" />
|
||||
<path
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:0.63425928;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 420,146.99998 H 358 V 117 h 32 V 56.999985 h 30 z"
|
||||
id="path100330-7-8-9-2-1"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccccc" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 1008 B |
After Width: | Height: | Size: 1.4 KiB |
|
@ -0,0 +1,111 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="390mm"
|
||||
height="150.00002mm"
|
||||
viewBox="0 0 390 150.00002"
|
||||
version="1.1"
|
||||
id="svg95853"
|
||||
inkscape:version="1.3.2 (091e20e, 2023-11-25)"
|
||||
sodipodi:docname="qp.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<defs
|
||||
id="defs95847" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#000000"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.32485136"
|
||||
inkscape:cx="763.42609"
|
||||
inkscape:cy="427.88801"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:pagecheckerboard="true"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:window-width="1732"
|
||||
inkscape:window-height="1052"
|
||||
inkscape:window-x="1920"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:deskcolor="#505050" />
|
||||
<metadata
|
||||
id="metadata95850">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,3.000003)">
|
||||
<path
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 0,86.999997 h 60 v -30 H 30 V -3.0000027 H 0 Z"
|
||||
id="path100330-7"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccccc" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:0.796078;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 120,-3.0000027 V 56.999996 H 90 V 26.999997 H 30 V -3.0000027 Z"
|
||||
id="path100330-7-3"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccccc" />
|
||||
<path
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 390,-3.000003 V 57 H 360 V 26.999997 h -60 v -30 z"
|
||||
id="path100330-0-8"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccccc" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:0.796078;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 300,117 V 57 h 30 v 30.000003 h 60 V 117 Z"
|
||||
id="path100330-0-8-5"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccccc" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:0.671296;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 120,56.999996 H 59.999997 v 30 H 90.000002 V 147.00001 H 120 Z"
|
||||
id="path100330-0-8-2"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccccc" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:0.670588;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 270,86.999998 h -60 v -30 h 30 V -3.0000027 h 30 z"
|
||||
id="path100330-7-0"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccccc" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 150,-3.0000027 V 56.999998 h 30 v -30 h 60 V -3.0000027 Z"
|
||||
id="path100330-7-3-6"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccccc" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:0.796078;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 150,56.999998 h 60 v 30 h -30 v 60.000012 h -30 z"
|
||||
id="path100330-0-8-2-1"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccccc" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 4.6 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 216 KiB |
|
@ -1,186 +1,186 @@
|
|||
import 'dart:io';
|
||||
import 'dart:ui';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
||||
import 'package:sqflite_common_ffi_web/sqflite_ffi_web.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:tetra_stats/data_objects/tetrio.dart';
|
||||
import 'package:tetra_stats/services/crud_exceptions.dart';
|
||||
import 'package:tetra_stats/services/tetrio_crud.dart';
|
||||
// import 'dart:io';
|
||||
// import 'dart:ui';
|
||||
// import 'package:flutter/foundation.dart';
|
||||
// import 'package:flutter/material.dart';
|
||||
// import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
||||
// import 'package:sqflite_common_ffi_web/sqflite_ffi_web.dart';
|
||||
// import 'package:test/test.dart';
|
||||
// import 'package:tetra_stats/data_objects/tetrio.dart';
|
||||
// import 'package:tetra_stats/services/crud_exceptions.dart';
|
||||
// import 'package:tetra_stats/services/tetrio_crud.dart';
|
||||
|
||||
void main() {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
DartPluginRegistrant.ensureInitialized();
|
||||
late TetrioService teto;
|
||||
setUp(() {
|
||||
if (kIsWeb) {
|
||||
sqfliteFfiInit();
|
||||
databaseFactory = databaseFactoryFfiWeb;
|
||||
} else if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) {
|
||||
sqfliteFfiInit();
|
||||
databaseFactory = databaseFactoryFfi;
|
||||
}
|
||||
teto = TetrioService();
|
||||
});
|
||||
// void main() {
|
||||
// WidgetsFlutterBinding.ensureInitialized();
|
||||
// DartPluginRegistrant.ensureInitialized();
|
||||
// late TetrioService teto;
|
||||
// setUp(() {
|
||||
// if (kIsWeb) {
|
||||
// sqfliteFfiInit();
|
||||
// databaseFactory = databaseFactoryFfiWeb;
|
||||
// } else if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) {
|
||||
// sqfliteFfiInit();
|
||||
// databaseFactory = databaseFactoryFfi;
|
||||
// }
|
||||
// teto = TetrioService();
|
||||
// });
|
||||
|
||||
test("Initialize TetrioServise", () async {
|
||||
teto.open();
|
||||
}); // a fucking MissingPluginException how does that even happening?
|
||||
// i guess i will be unable to test iteractions with DB
|
||||
// test("Initialize TetrioServise", () async {
|
||||
// teto.open();
|
||||
// }); // a fucking MissingPluginException how does that even happening?
|
||||
// // i guess i will be unable to test iteractions with DB
|
||||
|
||||
group("Test fetchPlayer with different players", () {
|
||||
// those tests exist in order to detect a tiny little change in Tetra Channel API in case of some update.
|
||||
test("dan63047 (user who have activity in tetra league)", () async {
|
||||
TetrioPlayer dan63047 = await teto.fetchPlayer("6098518e3d5155e6ec429cdc");
|
||||
expect(dan63047.userId, "6098518e3d5155e6ec429cdc");
|
||||
expect(dan63047.registrationTime != null, true);
|
||||
expect(dan63047.avatarRevision != null, true);
|
||||
expect(dan63047.connections != null, true);
|
||||
expect(dan63047.role, "user");
|
||||
expect(dan63047.distinguishment, null); // imagine if that one fails one day lol
|
||||
expect(dan63047.tlSeason1.glicko != null, true);
|
||||
//expect(dan63047.tlSeason1.rank != "z", true); lol
|
||||
expect(dan63047.tlSeason1.percentileRank != "z", true);
|
||||
expect(dan63047.tlSeason1.rating > -1, true);
|
||||
expect(dan63047.tlSeason1.gamesPlayed > 9, true);
|
||||
expect(dan63047.tlSeason1.gamesWon > 0, true);
|
||||
//expect(dan63047.tlSeason1.standing, -1);
|
||||
//expect(dan63047.tlSeason1.standingLocal, -1);
|
||||
expect(dan63047.tlSeason1.apm != null, true);
|
||||
expect(dan63047.tlSeason1.pps != null, true);
|
||||
expect(dan63047.tlSeason1.vs != null, true);
|
||||
expect(dan63047.tlSeason1.nerdStats != null, true);
|
||||
expect(dan63047.tlSeason1.estTr != null, true);
|
||||
expect(dan63047.tlSeason1.esttracc != null, true);
|
||||
expect(dan63047.tlSeason1.playstyle != null, true);
|
||||
});
|
||||
test("osk (sysop who have activity in tetra league)", () async {
|
||||
TetrioPlayer osk = await teto.fetchPlayer("5e32fc85ab319c2ab1beb07c");
|
||||
expect(osk.userId, "5e32fc85ab319c2ab1beb07c");
|
||||
expect(osk.registrationTime, null);
|
||||
expect(osk.country, "XM");
|
||||
expect(osk.avatarRevision != null, true);
|
||||
expect(osk.bannerRevision != null, true);
|
||||
expect(osk.connections != null, true);
|
||||
expect(osk.verified, true);
|
||||
expect(osk.role, "sysop");
|
||||
expect(osk.distinguishment != null, true);
|
||||
expect(osk.tlSeason1.glicko != null, true);
|
||||
expect(osk.tlSeason1.glicko != null, true);
|
||||
expect(osk.tlSeason1.rank == "z", true);
|
||||
expect(osk.tlSeason1.percentileRank != "z", true);
|
||||
expect(osk.tlSeason1.rating > -1, true);
|
||||
expect(osk.tlSeason1.gamesPlayed > 9, true);
|
||||
expect(osk.tlSeason1.gamesWon > 0, true);
|
||||
expect(osk.tlSeason1.standing, -1);
|
||||
expect(osk.tlSeason1.standingLocal, -1);
|
||||
expect(osk.tlSeason1.apm != null, true);
|
||||
expect(osk.tlSeason1.pps != null, true);
|
||||
expect(osk.tlSeason1.vs != null, true);
|
||||
expect(osk.tlSeason1.nerdStats != null, true);
|
||||
expect(osk.tlSeason1.estTr != null, true);
|
||||
expect(osk.tlSeason1.esttracc != null, true);
|
||||
expect(osk.tlSeason1.playstyle != null, true);
|
||||
});
|
||||
test("kagari (sysop who have zero activity)", () async {
|
||||
TetrioPlayer kagari = await teto.fetchPlayer("5e331c3ce24a5a3e258f7a1b");
|
||||
expect(kagari.userId, "5e331c3ce24a5a3e258f7a1b");
|
||||
expect(kagari.registrationTime, null);
|
||||
expect(kagari.country, "XM");
|
||||
expect(kagari.xp, 0);
|
||||
expect(kagari.gamesPlayed, -1);
|
||||
expect(kagari.gamesWon, -1);
|
||||
expect(kagari.gameTime, const Duration(seconds: -1));
|
||||
expect(kagari.avatarRevision != null, true);
|
||||
expect(kagari.bannerRevision != null, true);
|
||||
expect(kagari.connections, null);
|
||||
expect(kagari.verified, true);
|
||||
expect(kagari.distinguishment != null, true);
|
||||
expect(kagari.distinguishment!.detail, "kagarin");
|
||||
expect(kagari.friendCount, 1);
|
||||
expect(kagari.tlSeason1.glicko, null);
|
||||
expect(kagari.tlSeason1.rank, "z");
|
||||
expect(kagari.tlSeason1.percentileRank, "z");
|
||||
expect(kagari.tlSeason1.rating, -1);
|
||||
expect(kagari.tlSeason1.decaying, false);
|
||||
expect(kagari.tlSeason1.gamesPlayed, 0);
|
||||
expect(kagari.tlSeason1.gamesWon, 0);
|
||||
expect(kagari.tlSeason1.standing, -1);
|
||||
expect(kagari.tlSeason1.standingLocal, -1);
|
||||
expect(kagari.tlSeason1.apm, null);
|
||||
expect(kagari.tlSeason1.pps, null);
|
||||
expect(kagari.tlSeason1.vs, null);
|
||||
expect(kagari.tlSeason1.nerdStats, null);
|
||||
expect(kagari.tlSeason1.estTr, null);
|
||||
expect(kagari.tlSeason1.esttracc, null);
|
||||
expect(kagari.tlSeason1.playstyle, null);
|
||||
});
|
||||
test("furry (banned account)", () async {
|
||||
TetrioPlayer furry = await teto.fetchPlayer("5eea0ff69a1ba76c20347086");
|
||||
expect(furry.userId, "5eea0ff69a1ba76c20347086");
|
||||
expect(furry.registrationTime, DateTime.parse("2020-06-17T12:43:34.790Z"));
|
||||
expect(furry.role, "banned");
|
||||
expect(furry.badges.isEmpty, true);
|
||||
expect(furry.badstanding, false);
|
||||
expect(furry.xp, 0);
|
||||
expect(furry.supporterTier, 0);
|
||||
expect(furry.verified, false);
|
||||
expect(furry.connections, null);
|
||||
expect(furry.gamesPlayed, 0);
|
||||
expect(furry.gamesWon, 0);
|
||||
expect(furry.gameTime, Duration.zero);
|
||||
expect(furry.tlSeason1.glicko, null);
|
||||
expect(furry.tlSeason1.rank, "z");
|
||||
expect(furry.tlSeason1.percentileRank, "z");
|
||||
expect(furry.tlSeason1.rating, -1);
|
||||
expect(furry.tlSeason1.decaying, false);
|
||||
expect(furry.tlSeason1.gamesPlayed, 0);
|
||||
expect(furry.tlSeason1.gamesWon, 0);
|
||||
expect(furry.tlSeason1.standing, -1);
|
||||
expect(furry.tlSeason1.standingLocal, -1);
|
||||
expect(furry.tlSeason1.apm, null);
|
||||
expect(furry.tlSeason1.pps, null);
|
||||
expect(furry.tlSeason1.vs, null);
|
||||
expect(furry.tlSeason1.nerdStats, null);
|
||||
expect(furry.tlSeason1.estTr, null);
|
||||
expect(furry.tlSeason1.esttracc, null);
|
||||
expect(furry.tlSeason1.playstyle, null);
|
||||
});
|
||||
test("oskwarefan (anon account)", () async {
|
||||
TetrioPlayer oskwarefan = await teto.fetchPlayer("646cb8273e887a054d64febe");
|
||||
expect(oskwarefan.userId, "646cb8273e887a054d64febe");
|
||||
expect(oskwarefan.registrationTime, DateTime.parse("2023-05-23T12:57:11.481Z"));
|
||||
expect(oskwarefan.role, "anon");
|
||||
expect(oskwarefan.xp > 0, true);
|
||||
expect(oskwarefan.gamesPlayed > -1, true);
|
||||
expect(oskwarefan.gamesWon > -1, true);
|
||||
expect(oskwarefan.gameTime.isNegative, false);
|
||||
expect(oskwarefan.country, null);
|
||||
expect(oskwarefan.verified, false);
|
||||
expect(oskwarefan.connections, null);
|
||||
expect(oskwarefan.friendCount, 0);
|
||||
expect(oskwarefan.tlSeason1.glicko, null);
|
||||
expect(oskwarefan.tlSeason1.rank, "z");
|
||||
expect(oskwarefan.tlSeason1.percentileRank, "z");
|
||||
expect(oskwarefan.tlSeason1.rating, -1);
|
||||
expect(oskwarefan.tlSeason1.decaying, true); // ??? why true?
|
||||
expect(oskwarefan.tlSeason1.gamesPlayed, 0);
|
||||
expect(oskwarefan.tlSeason1.gamesWon, 0);
|
||||
expect(oskwarefan.tlSeason1.standing, -1);
|
||||
expect(oskwarefan.tlSeason1.standingLocal, -1);
|
||||
expect(oskwarefan.tlSeason1.apm, null);
|
||||
expect(oskwarefan.tlSeason1.pps, null);
|
||||
expect(oskwarefan.tlSeason1.vs, null);
|
||||
expect(oskwarefan.tlSeason1.nerdStats, null);
|
||||
expect(oskwarefan.tlSeason1.estTr, null);
|
||||
expect(oskwarefan.tlSeason1.esttracc, null);
|
||||
expect(oskwarefan.tlSeason1.playstyle, null);
|
||||
});
|
||||
// group("Test fetchPlayer with different players", () {
|
||||
// // those tests exist in order to detect a tiny little change in Tetra Channel API in case of some update.
|
||||
// test("dan63047 (user who have activity in tetra league)", () async {
|
||||
// TetrioPlayer dan63047 = await teto.fetchPlayer("6098518e3d5155e6ec429cdc");
|
||||
// expect(dan63047.userId, "6098518e3d5155e6ec429cdc");
|
||||
// expect(dan63047.registrationTime != null, true);
|
||||
// expect(dan63047.avatarRevision != null, true);
|
||||
// expect(dan63047.connections != null, true);
|
||||
// expect(dan63047.role, "user");
|
||||
// expect(dan63047.distinguishment, null); // imagine if that one fails one day lol
|
||||
// expect(dan63047.tlSeason1.glicko != null, true);
|
||||
// //expect(dan63047.tlSeason1.rank != "z", true); lol
|
||||
// expect(dan63047.tlSeason1.percentileRank != "z", true);
|
||||
// expect(dan63047.tlSeason1.tr > -1, true);
|
||||
// expect(dan63047.tlSeason1.gamesPlayed > 9, true);
|
||||
// expect(dan63047.tlSeason1.gamesWon > 0, true);
|
||||
// //expect(dan63047.tlSeason1.standing, -1);
|
||||
// //expect(dan63047.tlSeason1.standingLocal, -1);
|
||||
// expect(dan63047.tlSeason1.apm != null, true);
|
||||
// expect(dan63047.tlSeason1.pps != null, true);
|
||||
// expect(dan63047.tlSeason1.vs != null, true);
|
||||
// expect(dan63047.tlSeason1.nerdStats != null, true);
|
||||
// expect(dan63047.tlSeason1.estTr != null, true);
|
||||
// expect(dan63047.tlSeason1.esttracc != null, true);
|
||||
// expect(dan63047.tlSeason1.playstyle != null, true);
|
||||
// });
|
||||
// test("osk (sysop who have activity in tetra league)", () async {
|
||||
// TetrioPlayer osk = await teto.fetchPlayer("5e32fc85ab319c2ab1beb07c");
|
||||
// expect(osk.userId, "5e32fc85ab319c2ab1beb07c");
|
||||
// expect(osk.registrationTime, null);
|
||||
// expect(osk.country, "XM");
|
||||
// expect(osk.avatarRevision != null, true);
|
||||
// expect(osk.bannerRevision != null, true);
|
||||
// expect(osk.connections != null, true);
|
||||
// expect(osk.verified, true);
|
||||
// expect(osk.role, "sysop");
|
||||
// expect(osk.distinguishment != null, true);
|
||||
// expect(osk.tlSeason1.glicko != null, true);
|
||||
// expect(osk.tlSeason1.glicko != null, true);
|
||||
// expect(osk.tlSeason1.rank == "z", true);
|
||||
// expect(osk.tlSeason1.percentileRank != "z", true);
|
||||
// expect(osk.tlSeason1.tr > -1, true);
|
||||
// expect(osk.tlSeason1.gamesPlayed > 9, true);
|
||||
// expect(osk.tlSeason1.gamesWon > 0, true);
|
||||
// expect(osk.tlSeason1.standing, -1);
|
||||
// expect(osk.tlSeason1.standingLocal, -1);
|
||||
// expect(osk.tlSeason1.apm != null, true);
|
||||
// expect(osk.tlSeason1.pps != null, true);
|
||||
// expect(osk.tlSeason1.vs != null, true);
|
||||
// expect(osk.tlSeason1.nerdStats != null, true);
|
||||
// expect(osk.tlSeason1.estTr != null, true);
|
||||
// expect(osk.tlSeason1.esttracc != null, true);
|
||||
// expect(osk.tlSeason1.playstyle != null, true);
|
||||
// });
|
||||
// test("kagari (sysop who have zero activity)", () async {
|
||||
// TetrioPlayer kagari = await teto.fetchPlayer("5e331c3ce24a5a3e258f7a1b");
|
||||
// expect(kagari.userId, "5e331c3ce24a5a3e258f7a1b");
|
||||
// expect(kagari.registrationTime, null);
|
||||
// expect(kagari.country, "XM");
|
||||
// expect(kagari.xp, 0);
|
||||
// expect(kagari.gamesPlayed, -1);
|
||||
// expect(kagari.gamesWon, -1);
|
||||
// expect(kagari.gameTime, const Duration(seconds: -1));
|
||||
// expect(kagari.avatarRevision != null, true);
|
||||
// expect(kagari.bannerRevision != null, true);
|
||||
// expect(kagari.connections, null);
|
||||
// expect(kagari.verified, true);
|
||||
// expect(kagari.distinguishment != null, true);
|
||||
// expect(kagari.distinguishment!.detail, "kagarin");
|
||||
// expect(kagari.friendCount, 1);
|
||||
// expect(kagari.tlSeason1.glicko, null);
|
||||
// expect(kagari.tlSeason1.rank, "z");
|
||||
// expect(kagari.tlSeason1.percentileRank, "z");
|
||||
// expect(kagari.tlSeason1.tr, -1);
|
||||
// expect(kagari.tlSeason1.decaying, false);
|
||||
// expect(kagari.tlSeason1.gamesPlayed, 0);
|
||||
// expect(kagari.tlSeason1.gamesWon, 0);
|
||||
// expect(kagari.tlSeason1.standing, -1);
|
||||
// expect(kagari.tlSeason1.standingLocal, -1);
|
||||
// expect(kagari.tlSeason1.apm, null);
|
||||
// expect(kagari.tlSeason1.pps, null);
|
||||
// expect(kagari.tlSeason1.vs, null);
|
||||
// expect(kagari.tlSeason1.nerdStats, null);
|
||||
// expect(kagari.tlSeason1.estTr, null);
|
||||
// expect(kagari.tlSeason1.esttracc, null);
|
||||
// expect(kagari.tlSeason1.playstyle, null);
|
||||
// });
|
||||
// test("furry (banned account)", () async {
|
||||
// TetrioPlayer furry = await teto.fetchPlayer("5eea0ff69a1ba76c20347086");
|
||||
// expect(furry.userId, "5eea0ff69a1ba76c20347086");
|
||||
// expect(furry.registrationTime, DateTime.parse("2020-06-17T12:43:34.790Z"));
|
||||
// expect(furry.role, "banned");
|
||||
// expect(furry.badges.isEmpty, true);
|
||||
// expect(furry.badstanding, false);
|
||||
// expect(furry.xp, 0);
|
||||
// expect(furry.supporterTier, 0);
|
||||
// expect(furry.verified, false);
|
||||
// expect(furry.connections, null);
|
||||
// expect(furry.gamesPlayed, 0);
|
||||
// expect(furry.gamesWon, 0);
|
||||
// expect(furry.gameTime, Duration.zero);
|
||||
// expect(furry.tlSeason1.glicko, null);
|
||||
// expect(furry.tlSeason1.rank, "z");
|
||||
// expect(furry.tlSeason1.percentileRank, "z");
|
||||
// expect(furry.tlSeason1.tr, -1);
|
||||
// expect(furry.tlSeason1.decaying, false);
|
||||
// expect(furry.tlSeason1.gamesPlayed, 0);
|
||||
// expect(furry.tlSeason1.gamesWon, 0);
|
||||
// expect(furry.tlSeason1.standing, -1);
|
||||
// expect(furry.tlSeason1.standingLocal, -1);
|
||||
// expect(furry.tlSeason1.apm, null);
|
||||
// expect(furry.tlSeason1.pps, null);
|
||||
// expect(furry.tlSeason1.vs, null);
|
||||
// expect(furry.tlSeason1.nerdStats, null);
|
||||
// expect(furry.tlSeason1.estTr, null);
|
||||
// expect(furry.tlSeason1.esttracc, null);
|
||||
// expect(furry.tlSeason1.playstyle, null);
|
||||
// });
|
||||
// test("oskwarefan (anon account)", () async {
|
||||
// TetrioPlayer oskwarefan = await teto.fetchPlayer("646cb8273e887a054d64febe");
|
||||
// expect(oskwarefan.userId, "646cb8273e887a054d64febe");
|
||||
// expect(oskwarefan.registrationTime, DateTime.parse("2023-05-23T12:57:11.481Z"));
|
||||
// expect(oskwarefan.role, "anon");
|
||||
// expect(oskwarefan.xp > 0, true);
|
||||
// expect(oskwarefan.gamesPlayed > -1, true);
|
||||
// expect(oskwarefan.gamesWon > -1, true);
|
||||
// expect(oskwarefan.gameTime.isNegative, false);
|
||||
// expect(oskwarefan.country, null);
|
||||
// expect(oskwarefan.verified, false);
|
||||
// expect(oskwarefan.connections, null);
|
||||
// expect(oskwarefan.friendCount, 0);
|
||||
// expect(oskwarefan.tlSeason1.glicko, null);
|
||||
// expect(oskwarefan.tlSeason1.rank, "z");
|
||||
// expect(oskwarefan.tlSeason1.percentileRank, "z");
|
||||
// expect(oskwarefan.tlSeason1.tr, -1);
|
||||
// expect(oskwarefan.tlSeason1.decaying, true); // ??? why true?
|
||||
// expect(oskwarefan.tlSeason1.gamesPlayed, 0);
|
||||
// expect(oskwarefan.tlSeason1.gamesWon, 0);
|
||||
// expect(oskwarefan.tlSeason1.standing, -1);
|
||||
// expect(oskwarefan.tlSeason1.standingLocal, -1);
|
||||
// expect(oskwarefan.tlSeason1.apm, null);
|
||||
// expect(oskwarefan.tlSeason1.pps, null);
|
||||
// expect(oskwarefan.tlSeason1.vs, null);
|
||||
// expect(oskwarefan.tlSeason1.nerdStats, null);
|
||||
// expect(oskwarefan.tlSeason1.estTr, null);
|
||||
// expect(oskwarefan.tlSeason1.esttracc, null);
|
||||
// expect(oskwarefan.tlSeason1.playstyle, null);
|
||||
// });
|
||||
|
||||
test("not existing account", () async {
|
||||
var future = teto.fetchPlayer("hasdbashdbs");
|
||||
await expectLater(future, throwsA(isA<TetrioPlayerNotExist>()));
|
||||
});
|
||||
});
|
||||
}
|
||||
// test("not existing account", () async {
|
||||
// var future = teto.fetchPlayer("hasdbashdbs");
|
||||
// await expectLater(future, throwsA(isA<TetrioPlayerNotExist>()));
|
||||
// });
|
||||
// });
|
||||
// }
|