2024-09-14 22:05:50 +00:00
import ' dart:async ' ;
2024-09-17 22:17:34 +00:00
import ' dart:ui ' as ui ;
2024-09-21 23:11:31 +00:00
import ' dart:math ' ;
2024-07-27 19:10:45 +00:00
import ' package:flutter/foundation.dart ' ;
import ' package:flutter/gestures.dart ' ;
import ' package:flutter/material.dart ' hide Badge ;
2024-09-25 22:55:49 +00:00
import ' package:flutter/services.dart ' ;
2024-07-27 19:10:45 +00:00
import ' package:flutter_markdown/flutter_markdown.dart ' ;
import ' package:flutter_svg/flutter_svg.dart ' ;
2024-09-29 22:02:19 +00:00
import ' package:http/http.dart ' ;
2024-07-27 19:10:45 +00:00
import ' package:intl/intl.dart ' ;
2024-08-08 23:01:46 +00:00
import ' package:syncfusion_flutter_charts/charts.dart ' ;
2024-07-27 19:10:45 +00:00
import ' package:syncfusion_flutter_gauges/gauges.dart ' ;
2024-09-05 21:42:21 +00:00
import ' package:tetra_stats/data_objects/badge.dart ' ;
import ' package:tetra_stats/data_objects/beta_record.dart ' ;
2024-09-16 22:37:25 +00:00
import ' package:tetra_stats/data_objects/cutoff_tetrio.dart ' ;
2024-09-05 21:42:21 +00:00
import ' package:tetra_stats/data_objects/distinguishment.dart ' ;
import ' package:tetra_stats/data_objects/est_tr.dart ' ;
import ' package:tetra_stats/data_objects/nerd_stats.dart ' ;
import ' package:tetra_stats/data_objects/news.dart ' ;
import ' package:tetra_stats/data_objects/news_entry.dart ' ;
import ' package:tetra_stats/data_objects/p1nkl0bst3r.dart ' ;
import ' package:tetra_stats/data_objects/playstyle.dart ' ;
import ' package:tetra_stats/data_objects/record_extras.dart ' ;
import ' package:tetra_stats/data_objects/record_single.dart ' ;
import ' package:tetra_stats/data_objects/singleplayer_stream.dart ' ;
import ' package:tetra_stats/data_objects/summaries.dart ' ;
import ' package:tetra_stats/data_objects/tetra_league.dart ' ;
import ' package:tetra_stats/data_objects/tetra_league_beta_stream.dart ' ;
import ' package:tetra_stats/data_objects/tetrio_constants.dart ' ;
import ' package:tetra_stats/data_objects/tetrio_player.dart ' ;
2024-09-12 20:28:55 +00:00
import ' package:tetra_stats/data_objects/tetrio_player_from_leaderboard.dart ' ;
import ' package:tetra_stats/data_objects/tetrio_players_leaderboard.dart ' ;
2024-07-27 19:10:45 +00:00
import ' package:tetra_stats/gen/strings.g.dart ' ;
2024-08-08 23:01:46 +00:00
import ' package:tetra_stats/services/crud_exceptions.dart ' ;
2024-08-09 22:52:50 +00:00
import ' package:tetra_stats/utils/colors_functions.dart ' ;
2024-07-27 19:10:45 +00:00
import ' package:tetra_stats/utils/numers_formats.dart ' ;
import ' package:tetra_stats/utils/relative_timestamps.dart ' ;
import ' package:tetra_stats/utils/text_shadow.dart ' ;
2024-08-13 22:45:28 +00:00
import ' package:tetra_stats/views/singleplayer_record_view.dart ' ;
2024-08-06 22:24:31 +00:00
import ' package:tetra_stats/views/tl_match_view.dart ' ;
2024-10-03 21:00:41 +00:00
import ' package:tetra_stats/views/compare_view_tiles.dart ' ;
2024-08-13 22:45:28 +00:00
import ' package:tetra_stats/widgets/finesse_thingy.dart ' ;
2024-08-09 22:52:50 +00:00
import ' package:tetra_stats/widgets/graphs.dart ' ;
2024-08-13 22:45:28 +00:00
import ' package:tetra_stats/widgets/lineclears_thingy.dart ' ;
2024-08-06 22:24:31 +00:00
import ' package:tetra_stats/widgets/list_tile_trailing_stats.dart ' ;
2024-08-13 22:45:28 +00:00
import ' package:tetra_stats/widgets/sp_trailing_stats.dart ' ;
2024-07-27 19:10:45 +00:00
import ' package:tetra_stats/widgets/text_timestamp.dart ' ;
import ' package:tetra_stats/main.dart ' ;
2024-08-06 22:24:31 +00:00
import ' package:tetra_stats/widgets/tl_progress_bar.dart ' ;
2024-07-27 19:10:45 +00:00
import ' package:tetra_stats/widgets/user_thingy.dart ' ;
2024-09-11 22:41:02 +00:00
import ' package:transparent_image/transparent_image.dart ' ;
2024-09-19 22:38:31 +00:00
import ' package:vector_math/vector_math_64.dart ' hide Colors ;
2024-07-27 19:10:45 +00:00
2024-08-31 23:00:26 +00:00
var fDiff = NumberFormat ( " +#,###.####;-#,###.#### " ) ;
2024-09-10 21:22:17 +00:00
late Future < FetchResults > _data ;
late Future < News > _newsData ;
2024-08-31 23:00:26 +00:00
2024-07-27 19:10:45 +00:00
class MainView extends StatefulWidget {
final String ? player ;
/// The very first view, that user see when he launch this programm.
/// By default it loads my or defined in preferences user stats, but
/// if [player] username or id provided, it loads his stats. Also it hides menu drawer and three dots menu.
const MainView ( { super . key , this . player } ) ;
@ override
State < MainView > createState ( ) = > _MainState ( ) ;
}
2024-08-06 22:24:31 +00:00
enum Page { home , leaderboards , leagueAverages , calculator , settings }
2024-08-09 22:52:50 +00:00
enum Cards { overview , tetraLeague , quickPlay , sprint , blitz }
2024-08-24 14:41:07 +00:00
enum CardMod { info , records , ex , exRecords }
2024-08-01 23:20:36 +00:00
Map < Cards , String > cardsTitles = {
Cards . overview: " Overview " ,
Cards . tetraLeague: t . tetraLeague ,
Cards . quickPlay: t . quickPlay ,
2024-08-09 22:52:50 +00:00
//Cards.quickPlayExpert: "${t.quickPlay} ${t.expert}",
2024-08-01 23:20:36 +00:00
Cards . sprint: t . sprint ,
Cards . blitz: t . blitz ,
2024-08-09 22:52:50 +00:00
//Cards.other: t.other
2024-08-01 23:20:36 +00:00
} ;
2024-07-29 20:58:17 +00:00
late ScrollController controller ;
2024-07-27 19:10:45 +00:00
class _MainState extends State < MainView > with TickerProviderStateMixin {
2024-08-08 23:01:46 +00:00
int destination = 0 ;
2024-08-04 22:23:08 +00:00
String _searchFor = " 6098518e3d5155e6ec429cdc " ;
final TextEditingController _searchController = TextEditingController ( ) ;
2024-07-29 20:58:17 +00:00
@ override
void initState ( ) {
2024-08-03 17:52:20 +00:00
teto . open ( ) ;
2024-07-29 20:58:17 +00:00
controller = ScrollController ( ) ;
2024-09-10 21:22:17 +00:00
changePlayer ( _searchFor ) ;
2024-07-29 20:58:17 +00:00
super . initState ( ) ;
}
2024-09-10 21:22:17 +00:00
Future < FetchResults > _getData ( ) async {
TetrioPlayer player ;
try {
if ( _searchFor . startsWith ( " ds: " ) ) {
player = await teto . fetchPlayer ( _searchFor . substring ( 3 ) , isItDiscordID: true ) ; // we trying to get him with that
} else {
player = await teto . fetchPlayer ( _searchFor ) ; // Otherwise it's probably a user id or username
}
} on TetrioPlayerNotExist {
return FetchResults ( false , null , [ ] , null , null , TetrioPlayerNotExist ( ) ) ;
}
late Summaries summaries ;
late Cutoffs cutoffs ;
List < dynamic > requests = await Future . wait ( [
teto . fetchSummaries ( player . userId ) ,
teto . fetchCutoffsBeanserver ( ) ,
] ) ;
List < TetraLeague > states = await teto . getStates ( player . userId , season: currentSeason ) ;
summaries = requests [ 0 ] ;
cutoffs = requests [ 1 ] ;
bool isTracking = await teto . isPlayerTracking ( player . userId ) ;
if ( isTracking ) { // if tracked - save data to local DB
await teto . storeState ( summaries . league ) ;
}
return FetchResults ( true , player , states , summaries , cutoffs , null ) ;
}
2024-08-04 22:23:08 +00:00
void changePlayer ( String player ) {
setState ( ( ) {
_searchFor = player ;
2024-09-10 21:22:17 +00:00
_data = _getData ( ) ;
_newsData = teto . fetchNews ( _searchFor ) ;
2024-08-04 22:23:08 +00:00
} ) ;
}
2024-07-29 20:58:17 +00:00
@ override
void dispose ( ) {
controller . dispose ( ) ;
2024-08-04 22:23:08 +00:00
_searchController . dispose ( ) ;
2024-07-29 20:58:17 +00:00
super . dispose ( ) ;
}
2024-08-31 23:00:26 +00:00
NavigationRailDestination getDestinationButton ( IconData icon , String title ) {
return NavigationRailDestination (
icon: Tooltip (
message: title ,
child: Icon ( icon )
) ,
selectedIcon: Icon ( icon ) ,
label: Text ( title ) ,
) ;
}
2024-07-27 19:10:45 +00:00
@ override
Widget build ( BuildContext context ) {
2024-08-01 23:20:36 +00:00
return Scaffold (
2024-08-04 22:23:08 +00:00
drawer: SearchDrawer ( changePlayer: changePlayer , controller: _searchController ) ,
2024-08-01 23:20:36 +00:00
body: LayoutBuilder (
builder: ( BuildContext context , BoxConstraints constraints ) {
return Row (
2024-08-15 21:55:45 +00:00
mainAxisAlignment: MainAxisAlignment . center ,
2024-08-01 23:20:36 +00:00
children: [
2024-09-10 21:22:17 +00:00
TweenAnimationBuilder (
child: NavigationRail (
leading: FloatingActionButton (
elevation: 0 ,
onPressed: ( ) {
Scaffold . of ( context ) . openDrawer ( ) ;
} ,
child: const Icon ( Icons . search ) ,
) ,
trailing: IconButton (
onPressed: ( ) {
// Add your onPressed code here!
} ,
icon: const Icon ( Icons . more_horiz_rounded ) ,
) ,
destinations: [
getDestinationButton ( Icons . home , " Home " ) ,
getDestinationButton ( Icons . data_thresholding_outlined , " Graphs " ) ,
getDestinationButton ( Icons . leaderboard , " Leaderboards " ) ,
getDestinationButton ( Icons . compress , " Cutoffs " ) ,
getDestinationButton ( Icons . calculate , " Calc " ) ,
2024-09-13 22:00:11 +00:00
getDestinationButton ( Icons . info_outline , " Information " ) ,
2024-09-10 21:22:17 +00:00
getDestinationButton ( Icons . storage , " Saved Data " ) ,
getDestinationButton ( Icons . settings , " Settings " ) ,
] ,
selectedIndex: destination ,
onDestinationSelected: ( value ) {
setState ( ( ) {
destination = value ;
} ) ;
} ,
) ,
duration: Durations . long4 ,
tween: Tween < double > ( begin: 0 , end: 1 ) ,
2024-09-11 22:41:02 +00:00
curve: Easing . standard ,
2024-09-10 21:22:17 +00:00
builder: ( context , value , child ) {
return Container (
transform: Matrix4 . translationValues ( - 80 + value * 80 , 0 , 0 ) ,
2024-09-11 22:41:02 +00:00
child: Opacity ( opacity: value , child: child ) ,
2024-09-10 21:22:17 +00:00
) ;
} ,
) ,
2024-08-15 21:55:45 +00:00
Expanded (
child: switch ( destination ) {
0 = > DestinationHome ( searchFor: _searchFor , constraints: constraints ) ,
1 = > DestinationGraphs ( searchFor: _searchFor , constraints: constraints ) ,
2 = > DestinationLeaderboards ( constraints: constraints ) ,
2024-09-15 16:38:07 +00:00
3 = > DestinationCutoffs ( constraints: constraints ) ,
2024-09-19 22:38:31 +00:00
4 = > DestinationCalculator ( constraints: constraints ) ,
2024-08-15 21:55:45 +00:00
_ = > Text ( " Unknown destination $ destination " )
} ,
)
2024-08-08 23:01:46 +00:00
] ) ;
} ,
) ) ;
}
}
2024-09-19 22:38:31 +00:00
class DestinationCalculator extends StatefulWidget {
final BoxConstraints constraints ;
const DestinationCalculator ( { super . key , required this . constraints } ) ;
@ override
State < DestinationCalculator > createState ( ) = > _DestinationCalculatorState ( ) ;
}
2024-09-21 23:11:31 +00:00
enum CalcCards {
calc ,
damage
}
2024-09-26 22:14:45 +00:00
class Damage {
final int clear ;
final double combo ;
final double b2b ;
final int surge ;
final int pc ;
final double multiplier ;
2024-09-25 22:55:49 +00:00
2024-09-26 22:14:45 +00:00
int get total = > ( ( clear + combo + b2b + surge + pc ) * multiplier ) . floor ( ) ;
2024-09-25 22:55:49 +00:00
2024-09-26 22:14:45 +00:00
const Damage ( this . clear , this . combo , this . b2b , this . surge , this . pc , this . multiplier ) ;
2024-09-25 22:55:49 +00:00
2024-09-26 22:14:45 +00:00
@ override
String toString ( ) {
return total . toString ( ) ;
}
2024-09-25 22:55:49 +00:00
2024-09-26 22:14:45 +00:00
Damage operator + ( Damage other ) {
return Damage (
clear + other . clear ,
combo + other . combo ,
b2b + other . b2b ,
surge + other . surge ,
pc + other . pc ,
multiplier ) ;
}
}
2024-09-25 22:55:49 +00:00
2024-09-21 23:11:31 +00:00
class ClearData {
final String title ;
final Lineclears lineclear ;
final int lines ;
final bool miniSpin ;
final bool spin ;
bool perfectClear = false ;
2024-09-22 22:17:41 +00:00
int id = - 1 ;
2024-09-21 23:11:31 +00:00
ClearData ( this . title , this . lineclear , this . lines , this . miniSpin , this . spin ) ;
2024-09-22 22:17:41 +00:00
ClearData cloneWith ( int i ) {
ClearData newOne = ClearData ( title , lineclear , lines , miniSpin , spin ) . . id = i ;
return newOne ;
}
2024-09-21 23:11:31 +00:00
bool get difficultClear {
if ( lines = = 0 ) return false ;
if ( lines > = 4 | | miniSpin | | spin ) return true ;
else return false ;
}
void togglePC ( ) {
perfectClear = ! perfectClear ;
}
2024-09-26 22:14:45 +00:00
Damage dealsDamage ( int combo , int b2b , Rules rules ) {
if ( lines = = 0 ) return Damage ( 0 , 0 , 0 , 0 , 0 , rules . multiplier ) ;
int clearDamage = 0 ;
2024-09-22 22:17:41 +00:00
2024-09-21 23:11:31 +00:00
if ( spin ) {
2024-09-26 22:14:45 +00:00
if ( lines < = 5 ) clearDamage + = garbage [ lineclear ] ! ;
else clearDamage + = garbage [ Lineclears . TSPIN_PENTA ] ! + 2 * ( lines - 5 ) ;
2024-09-21 23:11:31 +00:00
} else if ( miniSpin ) {
2024-09-26 22:14:45 +00:00
clearDamage + = garbage [ lineclear ] ! ;
2024-09-21 23:11:31 +00:00
} else {
2024-09-26 22:14:45 +00:00
if ( lines < = 5 ) clearDamage + = garbage [ lineclear ] ! ;
else clearDamage + = garbage [ Lineclears . PENTA ] ! + ( lines - 5 ) ;
2024-09-21 23:11:31 +00:00
}
2024-09-22 22:17:41 +00:00
// Ok i can't figure out how b2b and combo works
// From tetrio.js:
// const n = e.cm.constants.garbage.BACKTOBACK_BONUS * (Math.floor(1 + Math.log1p((t.stats.btb - 1) * e.cm.constants.garbage.BACKTOBACK_BONUS_LOG)) + (t.stats.btb - 1 == 1 ? 0 : (1 + Math.log1p((t.stats.btb - 1) * e.cm.constants.garbage.BACKTOBACK_BONUS_LOG) % 1) / 3));
// if (h && (d += n),
2024-09-26 22:14:45 +00:00
double b2bDamage = 0 ;
2024-09-25 22:55:49 +00:00
if ( difficultClear & & b2b > = 1 & & rules . b2b ) {
2024-09-26 22:14:45 +00:00
if ( rules . b2bChaining ) b2bDamage + = BACKTOBACK_BONUS * ( ( 1 + log ( ( b2b + 1 ) * BACKTOBACK_BONUS_LOG ) ) . floor ( ) + ( b2b + 1 = = 1 ? 0 : ( 1 + log ( ( b2b + 1 ) * BACKTOBACK_BONUS_LOG ) % 1 ) / 3 ) ) ; // but it should be b2b-1 ???
else b2bDamage + = 1 ; // if b2b chaining off
}
int surgeDamage = 0 ;
if ( ! difficultClear & & rules . surge ) {
2024-09-22 22:17:41 +00:00
}
// From tetrio.js:
// if (t.stats.combo > 1)
// if (p += e.cm.constants.scoring.COMBO * (t.stats.combo - 1),
// "multiplier" === t.setoptions.combotable)
// d *= 1 + e.cm.constants.garbage.COMBO_BONUS * (t.stats.combo - 1),
// t.stats.combo > 2 && (d = Math.max(Math.log1p(e.cm.constants.garbage.COMBO_MINIFIER * (t.stats.combo - 1) * e.cm.constants.garbage.COMBO_MINIFIER_LOG), d));
// else {
// const n = e.cm.constants.garbage.combotable[t.setoptions.combotable] || [0];
// d += n[Math.max(0, Math.min(t.stats.combo - 2, n.length - 1))]
// }
2024-09-26 22:14:45 +00:00
double comboDamage = 0 ;
2024-09-26 22:34:27 +00:00
2024-09-25 22:55:49 +00:00
if ( rules . combo ) {
2024-09-26 22:14:45 +00:00
if ( combo > 1 ) {
if ( lines = = 1 & & rules . comboTable ! = ComboTables . multiplier ) comboDamage + = combotable [ rules . comboTable ] ! [ max ( 0 , min ( combo - 1 , combotable [ rules . comboTable ] ! . length - 1 ) ) ] ;
else comboDamage = ( clearDamage + b2bDamage ) * ( 1 + COMBO_BONUS * ( combo - 1 ) ) ;
2024-09-25 22:55:49 +00:00
}
if ( combo > 2 ) {
2024-09-26 22:14:45 +00:00
comboDamage = max ( log ( COMBO_MINIFIER * ( combo - 1 ) * COMBO_MINIFIER_LOG ) , comboDamage ) ;
2024-09-25 22:55:49 +00:00
}
2024-09-21 23:11:31 +00:00
}
2024-09-22 22:17:41 +00:00
2024-09-26 22:14:45 +00:00
int pcDamage = 0 ;
if ( perfectClear ) pcDamage + = rules . pcDamage ;
2024-09-24 22:57:37 +00:00
2024-09-26 22:14:45 +00:00
return Damage ( clearDamage , comboDamage , b2bDamage , surgeDamage , pcDamage , rules . multiplier ) ;
2024-09-21 23:11:31 +00:00
}
}
Map < String , List < ClearData > > clearsExisting = {
" No Spin Clears " : [
ClearData ( " No lineclear (Break Combo) " , Lineclears . ZERO , 0 , false , false ) ,
ClearData ( " Single " , Lineclears . SINGLE , 1 , false , false ) ,
ClearData ( " Double " , Lineclears . DOUBLE , 2 , false , false ) ,
ClearData ( " Triple " , Lineclears . TRIPLE , 3 , false , false ) ,
ClearData ( " Quad " , Lineclears . QUAD , 4 , false , false )
] ,
" Spins " : [
ClearData ( " Spin Zero " , Lineclears . TSPIN , 0 , false , true ) ,
ClearData ( " Spin Single " , Lineclears . TSPIN_SINGLE , 1 , false , true ) ,
ClearData ( " Spin Double " , Lineclears . TSPIN_DOUBLE , 2 , false , true ) ,
2024-09-22 22:17:41 +00:00
ClearData ( " Spin Triple " , Lineclears . TSPIN_TRIPLE , 3 , false , true ) ,
ClearData ( " Spin Quad " , Lineclears . TSPIN_QUAD , 4 , false , true ) ,
2024-09-21 23:11:31 +00:00
] ,
" Mini spins " : [
ClearData ( " Mini Spin Zero " , Lineclears . TSPIN_MINI , 0 , true , false ) ,
ClearData ( " Mini Spin Single " , Lineclears . TSPIN_MINI_SINGLE , 1 , true , false ) ,
ClearData ( " Mini Spin Double " , Lineclears . TSPIN_MINI_DOUBLE , 2 , true , false ) ,
2024-09-22 22:17:41 +00:00
ClearData ( " Mini Spin Triple " , Lineclears . TSPIN_MINI_TRIPLE , 3 , true , false ) ,
2024-09-21 23:11:31 +00:00
]
} ;
2024-09-25 22:55:49 +00:00
class Rules {
bool combo = true ;
bool b2b = true ;
bool b2bChaining = false ;
bool surge = true ;
int surgeInitAmount = 4 ;
int surgeInitAtB2b = 4 ;
ComboTables comboTable = ComboTables . multiplier ;
int pcDamage = 5 ;
int pcB2B = 1 ;
double multiplier = 1.0 ;
}
const TextStyle mainToggleInRules = TextStyle ( fontSize: 18 , fontWeight: ui . FontWeight . w800 ) ;
2024-09-19 22:38:31 +00:00
class _DestinationCalculatorState extends State < DestinationCalculator > {
double ? apm ;
double ? pps ;
double ? vs ;
NerdStats ? nerdStats ;
EstTr ? estTr ;
Playstyle ? playstyle ;
TextEditingController ppsController = TextEditingController ( ) ;
TextEditingController apmController = TextEditingController ( ) ;
TextEditingController vsController = TextEditingController ( ) ;
2024-09-21 23:11:31 +00:00
List < ClearData > clears = [ ] ;
2024-09-25 22:55:49 +00:00
Map < String , int > customClearsChoice = {
" No Spin Clears " : 5 ,
" Spins " : 5
} ;
2024-09-22 22:17:41 +00:00
int idCounter = 0 ;
2024-09-25 22:55:49 +00:00
Rules rules = Rules ( ) ;
2024-09-21 23:11:31 +00:00
2024-09-26 22:34:27 +00:00
CalcCards card = CalcCards . calc ;
2024-09-19 22:38:31 +00:00
@ override
void initState ( ) {
super . initState ( ) ;
}
@ override
void dispose ( ) {
super . dispose ( ) ;
}
void calc ( ) {
apm = double . tryParse ( apmController . text ) ;
pps = double . tryParse ( ppsController . text ) ;
vs = double . tryParse ( vsController . text ) ;
if ( apm ! = null & & pps ! = null & & vs ! = null ) {
nerdStats = NerdStats ( apm ! , pps ! , vs ! ) ;
estTr = EstTr ( apm ! , pps ! , vs ! , nerdStats ! . app , nerdStats ! . dss , nerdStats ! . dsp , nerdStats ! . gbe ) ;
playstyle = Playstyle ( apm ! , pps ! , nerdStats ! . app , nerdStats ! . vsapm , nerdStats ! . dsp , nerdStats ! . gbe , estTr ! . srarea , estTr ! . statrank ) ;
setState ( ( ) { } ) ;
} else {
ScaffoldMessenger . of ( context ) . showSnackBar ( const SnackBar ( content: Text ( " Please, enter valid values " ) ) ) ;
}
}
2024-09-21 23:11:31 +00:00
void calcDamage ( ) {
for ( ClearData lineclear in clears ) {
}
}
Widget getCalculator ( ) {
2024-09-19 22:38:31 +00:00
return SingleChildScrollView (
child: Column (
children: [
Card (
child: Center ( child: Padding (
padding: const EdgeInsets . only ( bottom: 8.0 ) ,
child: Column (
children: [
Text ( " Stats Calucator " , style: const TextStyle ( fontFamily: " Eurostile Round Extended " , fontSize: 42 ) ) ,
] ,
) ,
) ) ,
) ,
Card (
child: Padding (
2024-09-21 23:11:31 +00:00
padding: const EdgeInsets . fromLTRB ( 16.0 , 8.0 , 16.0 , 8.0 ) ,
2024-09-19 22:38:31 +00:00
child: Row (
children: [
Expanded (
2024-09-21 23:11:31 +00:00
child: Padding (
padding: const EdgeInsets . fromLTRB ( 12.0 , 0.0 , 12.0 , 0.0 ) ,
2024-09-19 22:38:31 +00:00
child: TextField (
2024-09-21 23:11:31 +00:00
onSubmitted: ( value ) = > calc ( ) ,
controller: apmController ,
keyboardType: TextInputType . number ,
decoration: const InputDecoration ( suffix: Text ( " APM " ) , alignLabelWithHint: true , hintText: " Enter your APM " ) ,
) ,
)
) ,
2024-09-19 22:38:31 +00:00
Expanded (
2024-09-21 23:11:31 +00:00
child: Padding (
padding: const EdgeInsets . fromLTRB ( 12.0 , 0.0 , 12.0 , 0.0 ) ,
child: TextField (
onSubmitted: ( value ) = > calc ( ) ,
controller: ppsController ,
keyboardType: TextInputType . number ,
decoration: const InputDecoration ( suffix: Text ( " PPS " ) , alignLabelWithHint: true , hintText: " Enter your PPS " ) ,
) ,
)
2024-09-19 22:38:31 +00:00
) ,
2024-09-21 23:11:31 +00:00
Expanded (
child: Padding (
padding: const EdgeInsets . fromLTRB ( 12.0 , 0.0 , 12.0 , 0.0 ) ,
child: TextField (
onSubmitted: ( value ) = > calc ( ) ,
controller: vsController ,
keyboardType: TextInputType . number ,
decoration: const InputDecoration ( suffix: Text ( " VS " ) , alignLabelWithHint: true , hintText: " Enter your VS " ) ,
) ,
)
) ,
2024-09-19 22:38:31 +00:00
TextButton (
onPressed: ( ) = > calc ( ) ,
child: Text ( t . calc ) ,
) ,
] ,
) ,
) ,
) ,
2024-09-21 23:11:31 +00:00
if ( nerdStats ! = null ) Card (
child: NerdStatsThingy ( nerdStats: nerdStats ! )
) ,
if ( playstyle ! = null ) Card (
child: GraphsThingy ( nerdStats: nerdStats ! , playstyle: playstyle ! , apm: apm ! , pps: pps ! , vs: vs ! )
2024-09-19 22:38:31 +00:00
)
] ,
) ,
) ;
}
2024-09-21 23:11:31 +00:00
Widget getDamageCalculator ( ) {
List < Widget > rSideWidgets = [ ] ;
2024-09-22 22:17:41 +00:00
List < Widget > lSideWidgets = [ ] ;
2024-09-21 23:11:31 +00:00
for ( var key in clearsExisting . keys ) {
rSideWidgets . add ( Text ( key ) ) ;
for ( ClearData data in clearsExisting [ key ] ! ) rSideWidgets . add ( Card (
child: ListTile (
2024-09-22 22:17:41 +00:00
title: Text ( data . title ) ,
2024-09-25 22:55:49 +00:00
subtitle: Text ( " ${ data . dealsDamage ( 0 , 0 , rules ) } damage ${ data . difficultClear ? " , difficult " : " " } " , style: TextStyle ( color: Colors . grey ) ) ,
2024-09-22 22:17:41 +00:00
trailing: Icon ( Icons . arrow_forward_ios ) ,
onTap: ( ) {
setState ( ( ) {
clears . add ( data . cloneWith ( idCounter ) ) ;
} ) ;
idCounter + + ;
} ,
) ,
2024-09-21 23:11:31 +00:00
) ) ;
2024-09-25 22:55:49 +00:00
if ( key ! = " Mini spins " ) rSideWidgets . add ( Card (
child: ListTile (
title: Text ( " Custom " ) ,
trailing: Row (
mainAxisSize: MainAxisSize . min ,
children: [
SizedBox ( width: 30.0 , child: TextField (
keyboardType: TextInputType . number ,
inputFormatters: [ FilteringTextInputFormatter . digitsOnly ] ,
decoration: InputDecoration ( hintText: " 5 " ) ,
onChanged: ( value ) = > customClearsChoice [ key ] = int . parse ( value ) ,
) ) ,
Text ( " Lines " , style: TextStyle ( fontSize: 18 ) ) ,
Icon ( Icons . arrow_forward_ios )
] ,
) ,
onTap: ( ) {
setState ( ( ) {
clears . add ( ClearData ( " ${ key = = " Spins " ? " Spin " : " " } ${ clearNames [ min ( customClearsChoice [ key ] ! , clearNames . length - 1 ) ] } ( ${ customClearsChoice [ key ] ! } Lines) " , key = = " Spins " ? Lineclears . TSPIN_PENTA : Lineclears . PENTA , customClearsChoice [ key ] ! , false , key = = " Spins " ) . cloneWith ( idCounter ) ) ;
} ) ;
idCounter + + ;
} ,
) ,
) ) ;
2024-10-05 22:32:09 +00:00
rSideWidgets . add ( const Divider ( ) ) ;
2024-09-21 23:11:31 +00:00
}
2024-09-22 22:17:41 +00:00
int combo = - 1 ;
int b2b = - 1 ;
2024-09-26 22:14:45 +00:00
Damage totalDamage = Damage ( 0 , 0 , 0 , 0 , 0 , rules . multiplier ) ;
int totalDamageNumber = 0 ;
2024-09-22 22:17:41 +00:00
for ( ClearData lineclear in clears ) {
if ( lineclear . difficultClear ) b2b + + ; else if ( lineclear . lines > 0 ) b2b = - 1 ;
if ( lineclear . lines > 0 ) combo + + ; else combo = - 1 ;
2024-09-26 22:14:45 +00:00
Damage dmg = lineclear . dealsDamage ( combo , b2b , rules ) ;
2024-09-22 22:17:41 +00:00
lSideWidgets . add (
ListTile (
2024-09-24 22:57:37 +00:00
key: ValueKey ( lineclear . id ) ,
2024-09-22 22:17:41 +00:00
leading: Row (
mainAxisSize: MainAxisSize . min ,
children: [
IconButton ( onPressed: ( ) { setState ( ( ) { clears . removeWhere ( ( element ) = > element . id = = lineclear . id , ) ; } ) ; } , icon: Icon ( Icons . clear ) ) ,
2024-09-25 22:55:49 +00:00
if ( lineclear . lines > 0 ) IconButton ( onPressed: ( ) { setState ( ( ) { lineclear . togglePC ( ) ; } ) ; } , icon: Icon ( Icons . local_parking_outlined , color: lineclear . perfectClear ? Colors . white : Colors . grey . shade800 ) ) ,
2024-09-22 22:17:41 +00:00
] ,
) ,
2024-09-24 22:57:37 +00:00
title: Text ( " ${ lineclear . title } ${ lineclear . perfectClear ? " PC " : " " } ${ combo > 0 ? " , $ {combo } combo " : " " } $ { b2b > 0 ? " , B2Bx ${ b2b } " : " " } " ),
2024-09-26 22:14:45 +00:00
subtitle: lineclear . lines > 0 ? Text ( " ${ dmg . clear } from clear, ${ dmg . combo } from combo, ${ dmg . b2b } from B2B, ${ dmg . surge } from Surge and ${ dmg . pc } from PC " , style: TextStyle ( color: Colors . grey ) ) : null ,
2024-09-25 22:55:49 +00:00
trailing: lineclear . lines > 0 ? Padding (
2024-09-24 22:57:37 +00:00
padding: const EdgeInsets . only ( right: 10.0 ) ,
child: Text ( dmg . toString ( ) , style: TextStyle ( fontSize: 36 , fontWeight: ui . FontWeight . w100 ) ) ,
2024-09-25 22:55:49 +00:00
) : null ,
2024-09-22 22:17:41 +00:00
)
) ;
totalDamage + = dmg ;
2024-09-26 22:14:45 +00:00
totalDamageNumber + = dmg . total ;
2024-09-22 22:17:41 +00:00
}
2024-09-21 23:11:31 +00:00
return Column (
children: [
Card (
child: Center ( child: Padding (
padding: const EdgeInsets . only ( bottom: 8.0 ) ,
child: Column (
children: [
Text ( " Damage Calucator " , style: const TextStyle ( fontFamily: " Eurostile Round Extended " , fontSize: 42 ) ) ,
] ,
) ,
) ) ,
) ,
Row (
2024-09-22 22:17:41 +00:00
crossAxisAlignment: CrossAxisAlignment . start ,
2024-09-21 23:11:31 +00:00
children: [
SizedBox (
width: 350.0 ,
child: DefaultTabController ( length: 2 ,
child: Column (
mainAxisSize: MainAxisSize . min ,
children: [
Card (
child: TabBar ( tabs: [
Tab ( text: " Actions " ) ,
Tab ( text: " Rules " ) ,
] ) ,
) ,
SizedBox (
height: widget . constraints . maxHeight - 164 ,
child: TabBarView ( children: [
SingleChildScrollView (
child: Column (
children: rSideWidgets ,
) ,
) ,
SingleChildScrollView (
2024-09-25 22:55:49 +00:00
child: Column (
children: [
Card (
child: ListTile (
title: Text ( " Multiplier " , style: mainToggleInRules ) ,
trailing: SizedBox ( width: 90.0 , child: TextField (
keyboardType: TextInputType . number ,
inputFormatters: [ FilteringTextInputFormatter . allow ( RegExp ( r'[0-9.]' ) ) ] ,
decoration: InputDecoration ( hintText: rules . multiplier . toString ( ) ) ,
onChanged: ( value ) = > setState ( ( ) { rules . multiplier = double . parse ( value ) ; } ) ,
) ) ,
) ,
) ,
Card (
child: Column (
children: [
ListTile (
title: Text ( " Combo " , style: mainToggleInRules ) ,
trailing: Switch ( value: rules . combo , onChanged: ( v ) = > setState ( ( ) { rules . combo = v ; } ) ) ,
) ,
if ( rules . combo ) ListTile (
title: Text ( " Combo Table " ) ,
)
] ,
) ,
) ,
Card (
child: Column (
children: [
ListTile (
title: Text ( " Back-To-Back (B2B) " , style: mainToggleInRules ) ,
trailing: Switch ( value: rules . b2b , onChanged: ( v ) = > setState ( ( ) { rules . b2b = v ; } ) ) ,
) ,
if ( rules . b2b ) ListTile (
title: Text ( " Back-To-Back Chaining " ) ,
trailing: Switch ( value: rules . b2bChaining , onChanged: ( v ) = > setState ( ( ) { rules . b2bChaining = v ; } ) ) ,
) ,
] ,
) ,
) ,
Card (
child: Column (
children: [
ListTile (
title: Text ( " Surge " , style: mainToggleInRules ) ,
trailing: Switch ( value: rules . surge , onChanged: ( v ) = > setState ( ( ) { rules . surge = v ; } ) ) ,
) ,
if ( rules . surge ) ListTile (
title: Text ( " Starts at B2B " ) ,
trailing: SizedBox ( width: 90.0 , child: TextField (
keyboardType: TextInputType . number ,
inputFormatters: [ FilteringTextInputFormatter . digitsOnly ] ,
decoration: InputDecoration ( hintText: rules . surgeInitAtB2b . toString ( ) ) ,
onChanged: ( value ) = > setState ( ( ) { rules . surgeInitAtB2b = int . parse ( value ) ; } ) ,
) ) ,
) ,
if ( rules . surge ) ListTile (
title: Text ( " Start amount " ) ,
trailing: SizedBox ( width: 90.0 , child: TextField (
keyboardType: TextInputType . number ,
inputFormatters: [ FilteringTextInputFormatter . digitsOnly ] ,
decoration: InputDecoration ( hintText: rules . surgeInitAmount . toString ( ) ) ,
onChanged: ( value ) = > setState ( ( ) { rules . surgeInitAmount = int . parse ( value ) ; } ) ,
) ) ,
) ,
] ,
) ,
)
] ,
2024-09-21 23:11:31 +00:00
) ,
)
] ) ,
)
] ,
)
) ,
) ,
SizedBox (
width: widget . constraints . maxWidth - 350 - 80 ,
2024-09-22 22:17:41 +00:00
height: widget . constraints . maxHeight - 148 ,
child: clears . isEmpty ? Center ( child: Text ( " Click on the actions on the left to add them here " , textAlign: ui . TextAlign . center ) ) :
2024-09-24 22:57:37 +00:00
Card (
child: Column (
children: [
Expanded (
child: ReorderableListView (
onReorder: ( oldIndex , newIndex ) {
setState ( ( ) {
if ( oldIndex < newIndex ) {
newIndex - = 1 ;
}
final ClearData item = clears . removeAt ( oldIndex ) ;
clears . insert ( newIndex , item ) ;
} ) ;
} ,
2024-09-22 22:17:41 +00:00
children: lSideWidgets ,
2024-09-24 22:57:37 +00:00
) ,
) ,
Divider ( ) ,
Column (
mainAxisSize: MainAxisSize . min ,
children: [
Padding (
padding: const EdgeInsets . fromLTRB ( 16.0 , 0.0 , 34.0 , 0.0 ) ,
child: Row (
children: [
2024-09-25 22:55:49 +00:00
Text ( " Total damage: " , style: TextStyle ( fontSize: 36 , fontWeight: ui . FontWeight . w100 ) ) ,
2024-09-24 22:57:37 +00:00
Spacer ( ) ,
2024-09-26 22:14:45 +00:00
Text ( totalDamageNumber . toString ( ) , style: TextStyle ( fontFamily: " Eurostile Round Extended " , fontSize: 36 , fontWeight: ui . FontWeight . w100 ) )
2024-09-24 22:57:37 +00:00
] ,
) ,
) ,
2024-09-26 22:14:45 +00:00
Text ( totalDamage . toString ( ) , style: TextStyle ( fontFamily: " Eurostile Round Extended " , fontSize: 36 , fontWeight: ui . FontWeight . w100 ) ) ,
2024-09-24 22:57:37 +00:00
ElevatedButton . icon ( onPressed: ( ) { setState ( ( ) { clears . clear ( ) ; } ) ; } , icon: const Icon ( Icons . clear ) , label: Text ( " Clear all " ) , style: const ButtonStyle ( shape: WidgetStatePropertyAll ( RoundedRectangleBorder ( borderRadius: BorderRadius . all ( Radius . circular ( 12.0 ) ) ) ) ) )
] ,
)
] ,
2024-09-21 23:11:31 +00:00
) ,
) ,
)
] ,
)
] ,
) ;
}
@ override
Widget build ( BuildContext context ) {
return Column (
children: [
SizedBox (
height: widget . constraints . maxHeight - 32 ,
2024-09-26 22:34:27 +00:00
child: switch ( card ) {
CalcCards . calc = > getCalculator ( ) ,
CalcCards . damage = > getDamageCalculator ( )
}
2024-09-21 23:11:31 +00:00
) ,
SegmentedButton < CalcCards > (
showSelectedIcon: false ,
segments: < ButtonSegment < CalcCards > > [
const ButtonSegment < CalcCards > (
value: CalcCards . calc ,
label: Text ( ' Stats Calculator ' ) ,
) ,
ButtonSegment < CalcCards > (
value: CalcCards . damage ,
label: Text ( ' Damage Calculator ' ) ,
) ,
] ,
2024-09-26 22:34:27 +00:00
selected: < CalcCards > { card } ,
2024-09-21 23:11:31 +00:00
onSelectionChanged: ( Set < CalcCards > newSelection ) {
setState ( ( ) {
2024-09-26 22:34:27 +00:00
card = newSelection . first ;
2024-09-21 23:11:31 +00:00
} ) ; } )
] ,
) ;
}
2024-09-19 22:38:31 +00:00
}
2024-09-16 22:37:25 +00:00
class FetchCutoffsResults {
late bool success ;
CutoffsTetrio ? cutoffs ;
Exception ? exception ;
FetchCutoffsResults ( this . success , this . cutoffs , this . exception ) ;
}
2024-09-14 22:05:50 +00:00
class DestinationCutoffs extends StatefulWidget {
final BoxConstraints constraints ;
const DestinationCutoffs ( { super . key , required this . constraints } ) ;
@ override
2024-09-15 16:38:07 +00:00
State < DestinationCutoffs > createState ( ) = > _DestinationCutoffsState ( ) ;
2024-09-14 22:05:50 +00:00
}
2024-09-15 16:38:07 +00:00
class _DestinationCutoffsState extends State < DestinationCutoffs > {
2024-09-16 22:37:25 +00:00
Future < CutoffsTetrio > fetch ( ) async {
TetrioPlayerFromLeaderboard top1 ;
CutoffsTetrio cutoffs ;
List < dynamic > requests = await Future . wait ( [
teto . fetchCutoffsTetrio ( ) ,
teto . fetchTopOneFromTheLeaderboard ( ) ,
] ) ;
cutoffs = requests [ 0 ] ;
top1 = requests [ 1 ] ;
cutoffs . data [ " top1 " ] = CutoffTetrio (
pos: 1 ,
percentile: 0.00 ,
tr: top1 . tr ,
targetTr: 25000 ,
apm: top1 . apm ,
pps: top1 . pps ,
vs: top1 . vs ,
count: 1 ,
countPercentile: 0.0
) ;
return cutoffs ;
}
2024-09-14 22:05:50 +00:00
@ override
Widget build ( BuildContext context ) {
2024-09-16 22:37:25 +00:00
return FutureBuilder < CutoffsTetrio > (
future: fetch ( ) ,
builder: ( context , snapshot ) {
2024-09-17 22:17:34 +00:00
switch ( snapshot . connectionState ) {
case ConnectionState . none:
case ConnectionState . waiting:
return const Center ( child: CircularProgressIndicator ( ) ) ;
case ConnectionState . active:
case ConnectionState . done:
if ( snapshot . hasData ) {
return SingleChildScrollView (
child: Column (
children: [
Card (
2024-09-19 22:38:31 +00:00
child: Center ( child: Padding (
padding: const EdgeInsets . only ( bottom: 8.0 ) ,
child: Column (
children: [
Text ( " Tetra League State " , style: const TextStyle ( fontFamily: " Eurostile Round Extended " , fontSize: 42 ) ) ,
Text ( " as of ${ timestamp ( snapshot . data ! . timestamp ) } " ) ,
] ,
) ,
) ) ,
2024-09-17 22:17:34 +00:00
) ,
2024-09-19 22:38:31 +00:00
Padding (
padding: const EdgeInsets . only ( bottom: 4.0 ) ,
child: Card (
child: Column (
children: [
Row (
children: [
Padding (
padding: const EdgeInsets . only ( left: 8.0 ) ,
child: Column (
children: [
Padding (
padding: const EdgeInsets . only ( bottom: 12.0 ) ,
child: Text ( " Actual " ) ,
) ,
Text ( " Target " )
]
) ,
) ,
Expanded (
child: Padding (
padding: const EdgeInsets . fromLTRB ( 12.0 , 8.0 , 12.0 , 8.0 ) ,
child: SfLinearGauge (
minimum: 0.00000000 ,
maximum: 25000.0000 ,
showTicks: false ,
showLabels: false ,
ranges: [
for ( var cutoff in snapshot . data ! . data . keys ) LinearGaugeRange (
position: LinearElementPosition . outside ,
startValue: snapshot . data ! . data [ cutoff ] ! . tr ,
startWidth: 20.0 ,
endWidth: 20.0 ,
endValue: switch ( cutoff ) {
" top1 " = > 25000.00 ,
" x+ " = > snapshot . data ! . data [ " top1 " ] ! . tr ,
_ = > snapshot . data ! . data [ ranks [ ranks . indexOf ( cutoff ) + 1 ] ] ! . tr
} ,
color: cutoff ! = " top1 " ? rankColors [ cutoff ] : Colors . grey . shade800 ,
) ,
for ( var cutoff in snapshot . data ! . data . keys ) LinearGaugeRange (
position: LinearElementPosition . inside ,
startValue: snapshot . data ! . data [ cutoff ] ! . targetTr ,
endValue: switch ( cutoff ) {
" top1 " = > 25000.00 ,
" x+ " = > snapshot . data ! . data [ " top1 " ] ! . targetTr ,
_ = > snapshot . data ! . data [ ranks [ ranks . indexOf ( cutoff ) + 1 ] ] ! . targetTr
} ,
color: cutoff ! = " top1 " ? rankColors [ cutoff ] : null ,
) ,
for ( var cutoff in snapshot . data ! . data . keys . skip ( 1 ) ) if ( snapshot . data ! . data [ cutoff ] ! . tr < snapshot . data ! . data [ cutoff ] ! . targetTr ) LinearGaugeRange (
position: LinearElementPosition . cross ,
startValue: snapshot . data ! . data [ cutoff ] ! . tr ,
endValue: snapshot . data ! . data [ cutoff ] ! . targetTr ,
color: Colors . green ,
) ,
for ( var cutoff in snapshot . data ! . data . keys . skip ( 1 ) ) if ( snapshot . data ! . data [ ranks [ ranks . indexOf ( cutoff ) + 1 ] ] ! . tr > snapshot . data ! . data [ ranks [ ranks . indexOf ( cutoff ) + 1 ] ] ! . targetTr ) LinearGaugeRange (
position: LinearElementPosition . cross ,
startValue: snapshot . data ! . data [ ranks [ ranks . indexOf ( cutoff ) + 1 ] ] ! . targetTr ,
endValue: snapshot . data ! . data [ ranks [ ranks . indexOf ( cutoff ) + 1 ] ] ! . tr ,
color: Colors . red ,
) ,
] ,
markerPointers: [
for ( var cutoff in snapshot . data ! . data . keys ) LinearWidgetPointer ( child: Container ( child: Text ( intf . format ( snapshot . data ! . data [ cutoff ] ! . tr ) , style: TextStyle ( fontSize: 12 ) ) , transform: Matrix4 . compose ( Vector3 ( 0 , 35 , 0 ) , Quaternion . axisAngle ( Vector3 ( 0 , 0 , 1 ) , - 1 ) , Vector3 ( 1 , 1 , 1 ) ) , height: 45.0 ) , value: snapshot . data ! . data [ cutoff ] ! . tr , position: LinearElementPosition . outside , offset: 20 ) ,
for ( var cutoff in snapshot . data ! . data . keys ) LinearWidgetPointer ( child: Container ( child: Text ( intf . format ( snapshot . data ! . data [ cutoff ] ! . targetTr ) , textAlign: ui . TextAlign . right , style: TextStyle ( fontSize: 12 ) ) , transform: Matrix4 . compose ( Vector3 ( - 15 , 0 , 0 ) , Quaternion . axisAngle ( Vector3 ( 0 , 0 , 1 ) , - 1 ) , Vector3 ( 1 , 1 , 1 ) ) , height: 45.0 , transformAlignment: Alignment . topRight ) , value: snapshot . data ! . data [ cutoff ] ! . targetTr , position: LinearElementPosition . inside , offset: 6 )
] ,
) ,
) ,
) ,
] ,
2024-09-17 22:17:34 +00:00
) ,
] ,
) ,
) ,
) ,
2024-09-19 22:38:31 +00:00
Table (
defaultVerticalAlignment: TableCellVerticalAlignment . middle ,
border: TableBorder . all ( color: Colors . grey . shade900 ) ,
columnWidths: const {
0 : FixedColumnWidth ( 48 ) ,
1 : FixedColumnWidth ( 155 ) ,
2 : FixedColumnWidth ( 140 ) ,
3 : FixedColumnWidth ( 160 ) ,
4 : FixedColumnWidth ( 150 ) ,
5 : FixedColumnWidth ( 90 ) ,
6 : FixedColumnWidth ( 130 ) ,
7 : FixedColumnWidth ( 120 ) ,
8 : FixedColumnWidth ( 125 ) ,
9 : FixedColumnWidth ( 70 ) ,
} ,
children: [
TableRow (
children: [
Text ( " 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 ( " Cutoff 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 ( " Target TR " , textAlign: TextAlign . right , style: TextStyle ( fontFamily: " Eurostile Round " , fontSize: 24 , fontWeight: FontWeight . w100 , color: Colors . white ) ) ,
) ,
Padding (
padding: const EdgeInsets . only ( right: 8.0 ) ,
child: Text ( " State " , textAlign: TextAlign . right , style: const TextStyle ( fontFamily: " Eurostile Round " , fontSize: 14 , 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 ) ) ,
) ,
Padding (
padding: const EdgeInsets . only ( right: 8.0 ) ,
child: Text ( " More info " , 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 ) if ( rank ! = " top1 " ) 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 ] ! . targetTr ) , textAlign: TextAlign . right , style: const TextStyle ( fontFamily: " Eurostile Round " , fontSize: 24 , 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 ) ,
2024-09-17 22:17:34 +00:00
children: [
2024-09-19 22:38:31 +00:00
if ( rank = = " x+ " ) TextSpan ( text: " № 1 is ${ f2 . format ( snapshot . data ! . data [ " top1 " ] ! . tr ) } TR " , style: const TextStyle ( color: Colors . white60 , shadows: null ) )
else TextSpan ( text: snapshot . data ! . data [ ranks [ ranks . indexOf ( rank ) + 1 ] ] ! . tr > snapshot . data ! . data [ ranks [ ranks . indexOf ( rank ) + 1 ] ] ! . targetTr ? " Inflated from ${ NumberFormat . compact ( ) . format ( snapshot . data ! . data [ rank ] ! . targetTr ) } TR " : " Not inflated " , style: TextStyle ( color: snapshot . data ! . data [ ranks [ ranks . indexOf ( rank ) + 1 ] ] ! . tr > snapshot . data ! . data [ ranks [ ranks . indexOf ( rank ) + 1 ] ] ! . targetTr ? Colors . white : Colors . white60 , shadows: null ) ) ,
TextSpan ( text: " \n " , style: const TextStyle ( color: Colors . white60 , shadows: null ) ) ,
if ( rank = = " d " ) TextSpan ( text: " Well... " , style: const TextStyle ( color: Colors . white60 , shadows: null ) )
else TextSpan ( text: snapshot . data ! . data [ rank ] ! . tr < snapshot . data ! . data [ rank ] ! . targetTr ? " Deflated untill ${ NumberFormat . compact ( ) . format ( snapshot . data ! . data [ rank ] ! . targetTr ) } TR " : " Not deflated " , style: TextStyle ( color: snapshot . data ! . data [ rank ] ! . tr < snapshot . data ! . data [ rank ] ! . targetTr ? Colors . white : Colors . white60 , shadows: null ) )
2024-09-17 22:17:34 +00:00
]
2024-09-19 22:38:31 +00:00
) ) ,
) ,
Padding (
padding: const EdgeInsets . only ( right: 8.0 ) ,
child: Text ( snapshot . data ? . data [ rank ] ? . apm ! = null ? f2 . format ( snapshot . data ! . data [ rank ] ! . apm ) : " -.-- " , textAlign: TextAlign . right , style: TextStyle ( fontFamily: " Eurostile Round " , fontSize: 28 , fontWeight: FontWeight . w100 , color: snapshot . data ? . data [ rank ] ? . apm ! = null ? Colors . white : Colors . grey , shadows: textShadow ) ) ,
) ,
Padding (
padding: const EdgeInsets . only ( right: 8.0 ) ,
child: Text ( snapshot . data ? . data [ rank ] ? . pps ! = null ? f2 . format ( snapshot . data ! . data [ rank ] ! . pps ) : " -.-- " , textAlign: TextAlign . right , style: TextStyle ( fontFamily: " Eurostile Round " , fontSize: 28 , fontWeight: FontWeight . w100 , color: snapshot . data ? . data [ rank ] ? . pps ! = null ? Colors . white : Colors . grey , shadows: textShadow ) ) ,
) ,
Padding (
padding: const EdgeInsets . only ( right: 8.0 ) ,
child: Text ( snapshot . data ? . data [ rank ] ? . vs ! = null ? f2 . format ( snapshot . data ! . data [ rank ] ! . vs ) : " -.-- " , textAlign: TextAlign . right , style: TextStyle ( fontFamily: " Eurostile Round " , fontSize: 28 , fontWeight: FontWeight . w100 , color: snapshot . data ? . data [ rank ] ? . vs ! = null ? Colors . white : Colors . grey , shadows: textShadow ) ) ,
) ,
Padding (
padding: const EdgeInsets . only ( right: 8.0 ) ,
child: Text ( " ${ snapshot . data ? . data [ rank ] ? . apm ! = null & & snapshot . data ? . data [ rank ] ? . pps ! = null ? f3 . format ( snapshot . data ! . data [ rank ] ! . apm ! / ( snapshot . data ! . data [ rank ] ! . pps ! * 60 ) ) : " -.--- " } APP \n ${ snapshot . data ? . data [ rank ] ? . apm ! = null & & snapshot . data ? . data [ rank ] ? . vs ! = null ? f3 . format ( snapshot . data ! . data [ rank ] ! . vs ! / snapshot . data ! . data [ rank ] ! . apm ! ) : " -.--- " } VS/APM " , textAlign: TextAlign . right , style: TextStyle ( fontFamily: " Eurostile Round " , fontSize: 14 , fontWeight: FontWeight . w100 , color: snapshot . data ? . data [ rank ] ? . apm ! = null & & snapshot . data ? . data [ rank ] ? . pps ! = null & & snapshot . data ? . data [ rank ] ? . vs ! = null ? Colors . white : Colors . grey , 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 ) ,
2024-09-17 22:17:34 +00:00
children: [
2024-09-19 22:38:31 +00:00
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 ) )
2024-09-17 22:17:34 +00:00
]
2024-09-19 22:38:31 +00:00
) )
) ,
Padding (
padding: const EdgeInsets . only ( right: 8.0 ) ,
child: TextButton ( child: Text ( " View " , textAlign: TextAlign . right , style: const TextStyle ( fontFamily: " Eurostile Round " , fontSize: 14 , fontWeight: FontWeight . w500 , color: Colors . white ) ) , onPressed: ( ) {
} , ) ,
) ,
]
)
] ,
2024-09-17 22:17:34 +00:00
)
]
) ,
) ;
}
if ( snapshot . hasError ) { return FutureError ( snapshot ) ; }
}
return Text ( " huh? " ) ;
}
2024-09-15 16:38:07 +00:00
) ;
2024-09-14 22:05:50 +00:00
}
}
2024-08-08 23:01:46 +00:00
class DestinationLeaderboards extends StatefulWidget {
final BoxConstraints constraints ;
const DestinationLeaderboards ( { super . key , required this . constraints } ) ;
@ override
State < DestinationLeaderboards > createState ( ) = > _DestinationLeaderboardsState ( ) ;
}
2024-09-14 22:05:50 +00:00
enum Leaderboards {
tl ,
2024-09-29 22:02:19 +00:00
fullTL ,
2024-09-14 22:05:50 +00:00
xp ,
2024-09-15 16:38:07 +00:00
ar ,
sprint ,
blitz ,
zenith ,
zenithex ,
2024-09-14 22:05:50 +00:00
}
2024-08-08 23:01:46 +00:00
class _DestinationLeaderboardsState extends State < DestinationLeaderboards > {
2024-08-17 23:39:20 +00:00
//Duration postSeasonLeft = seasonStart.difference(DateTime.now());
2024-09-15 16:38:07 +00:00
final Map < Leaderboards , String > leaderboards = {
2024-09-29 22:02:19 +00:00
Leaderboards . tl: " Tetra League (Current Season) " ,
Leaderboards . fullTL: " Tetra League (Current Season, full one) " ,
2024-09-15 16:38:07 +00:00
Leaderboards . xp: " XP " ,
Leaderboards . ar: " Acievement Points " ,
Leaderboards . sprint: " 40 Lines " ,
Leaderboards . blitz: " Blitz " ,
Leaderboards . zenith: " Quick Play " ,
Leaderboards . zenithex: " Quick Play Expert " ,
} ;
2024-09-14 22:05:50 +00:00
Leaderboards _currentLb = Leaderboards . tl ;
2024-09-29 22:02:19 +00:00
final StreamController < List < dynamic > > _dataStreamController = StreamController < List < dynamic > > . broadcast ( ) ;
2024-09-14 22:05:50 +00:00
late final ScrollController _scrollController ;
2024-09-15 16:38:07 +00:00
Stream < List < dynamic > > get dataStream = > _dataStreamController . stream ;
List < dynamic > list = [ ] ;
2024-09-14 22:05:50 +00:00
bool _isFetchingData = false ;
2024-09-15 16:38:07 +00:00
String ? prisecter ;
2024-10-01 21:46:43 +00:00
List < DropdownMenuEntry > _countries = [ for ( MapEntry e in t . countries . entries ) DropdownMenuEntry ( value: e . key , label: e . value ) ] ;
List < DropdownMenuEntry > _stats = [ for ( MapEntry e in chartsShortTitles . entries ) DropdownMenuEntry ( value: e . key , label: e . value ) ] ;
String ? _country ;
Stats stat = Stats . tr ;
2024-09-14 22:05:50 +00:00
Future < void > _fetchData ( ) async {
if ( _isFetchingData ) {
// Avoid fetching new data while already fetching
return ;
}
try {
_isFetchingData = true ;
setState ( ( ) { } ) ;
final items = switch ( _currentLb ) {
2024-10-01 21:46:43 +00:00
Leaderboards . tl = > await teto . fetchTetrioLeaderboard ( prisecter: prisecter , country: _country ) ,
Leaderboards . fullTL = > ( await teto . fetchTLLeaderboard ( ) ) . getStatRankingFromLB ( stat , country: _country ? ? " " ) ,
Leaderboards . xp = > await teto . fetchTetrioLeaderboard ( prisecter: prisecter , lb: " xp " , country: _country ) ,
Leaderboards . ar = > await teto . fetchTetrioLeaderboard ( prisecter: prisecter , lb: " ar " , country: _country ) ,
Leaderboards . sprint = > await teto . fetchTetrioRecordsLeaderboard ( prisecter: prisecter , country: _country ) ,
Leaderboards . blitz = > await teto . fetchTetrioRecordsLeaderboard ( prisecter: prisecter , lb: " blitz_global " , country: _country ) ,
Leaderboards . zenith = > await teto . fetchTetrioRecordsLeaderboard ( prisecter: prisecter , lb: " zenith_global " , country: _country ) ,
Leaderboards . zenithex = > await teto . fetchTetrioRecordsLeaderboard ( prisecter: prisecter , lb: " zenithex_global " , country: _country ) ,
2024-09-14 22:05:50 +00:00
} ;
list . addAll ( items ) ;
_dataStreamController . add ( list ) ;
2024-09-15 16:38:07 +00:00
prisecter = list . last . prisecter . toString ( ) ;
2024-09-14 22:05:50 +00:00
} catch ( e ) {
_dataStreamController . addError ( e ) ;
} finally {
// Set to false when data fetching is complete
_isFetchingData = false ;
setState ( ( ) { } ) ;
}
}
@ override
void initState ( ) {
super . initState ( ) ;
_scrollController = ScrollController ( ) ;
_fetchData ( ) ;
_scrollController . addListener ( ( ) {
_scrollController . addListener ( ( ) {
final maxScroll = _scrollController . position . maxScrollExtent ;
final currentScroll = _scrollController . position . pixels ;
2024-09-29 22:02:19 +00:00
if ( currentScroll = = maxScroll & & _currentLb ! = Leaderboards . fullTL ) {
2024-09-14 22:05:50 +00:00
// When the last item is fully visible, load the next page.
_fetchData ( ) ;
}
} ) ;
} ) ;
}
2024-08-08 23:01:46 +00:00
2024-09-29 22:02:19 +00:00
static TextStyle trailingStyle = TextStyle ( fontSize: 28 ) ;
2024-08-08 23:01:46 +00:00
@ override
Widget build ( BuildContext context ) {
return Row (
children: [
SizedBox (
width: 350.0 ,
height: widget . constraints . maxHeight ,
child: Column (
children: [
2024-08-09 22:52:50 +00:00
const Card (
2024-08-08 23:01:46 +00:00
child: Row (
mainAxisSize: MainAxisSize . min ,
children: [
2024-08-09 22:52:50 +00:00
Spacer ( ) ,
Text ( " Leaderboards " , style: TextStyle ( fontFamily: " Eurostile Round Extended " , fontSize: 36 ) ) ,
Spacer ( )
2024-08-08 23:01:46 +00:00
] ,
) ,
) ,
Expanded (
child: ListView . builder (
itemCount: leaderboards . length ,
itemBuilder: ( BuildContext context , int index ) {
return Card (
2024-09-29 22:02:19 +00:00
surfaceTintColor: index = = 1 ? Colors . redAccent : theme . colorScheme . primary ,
2024-08-08 23:01:46 +00:00
child: ListTile (
2024-09-14 22:05:50 +00:00
title: Text ( leaderboards . values . elementAt ( index ) ) ,
2024-09-29 22:02:19 +00:00
subtitle: index = = 1 ? Text ( " Heavy, but allows you to sort players by their stats " , style: TextStyle ( color: Colors . grey , fontSize: 12 ) ) : null ,
2024-09-14 22:05:50 +00:00
onTap: ( ) {
_currentLb = leaderboards . keys . elementAt ( index ) ;
list . clear ( ) ;
2024-09-15 16:38:07 +00:00
prisecter = null ;
2024-09-14 22:05:50 +00:00
_fetchData ( ) ;
} ,
2024-08-08 23:01:46 +00:00
) ,
) ;
}
) ,
) ,
] ,
) ,
) ,
SizedBox (
width: widget . constraints . maxWidth - 350 - 88 ,
2024-09-14 22:05:50 +00:00
child: Card (
2024-09-15 16:38:07 +00:00
child: StreamBuilder < List < dynamic > > (
2024-09-14 22:05:50 +00:00
stream: dataStream ,
builder: ( context , snapshot ) {
switch ( snapshot . connectionState ) {
case ConnectionState . none:
case ConnectionState . waiting:
return const Center ( child: CircularProgressIndicator ( ) ) ;
case ConnectionState . active:
case ConnectionState . done:
if ( snapshot . hasData ) {
return Column (
children: [
Text ( leaderboards [ _currentLb ] ! , style: TextStyle ( fontFamily: " Eurostile Round Extended " , fontSize: 28 , height: 0.9 ) ) ,
2024-10-01 21:46:43 +00:00
Row (
mainAxisAlignment: MainAxisAlignment . center ,
children: [
DropdownMenu (
leadingIcon: Icon ( Icons . public ) ,
inputDecorationTheme: InputDecorationTheme (
isDense: true ,
) ,
textStyle: TextStyle ( fontSize: 14 , height: 0.9 ) ,
dropdownMenuEntries: _countries ,
initialSelection: " " ,
onSelected: ( ( value ) {
_country = value as String ? ;
list . clear ( ) ;
prisecter = null ;
_isFetchingData = false ;
setState ( ( ) { _fetchData ( ) ; } ) ;
} )
) ,
if ( _currentLb = = Leaderboards . fullTL ) SizedBox ( width: 5.0 ) ,
if ( _currentLb = = Leaderboards . fullTL ) DropdownMenu (
leadingIcon: Icon ( Icons . sort ) ,
inputDecorationTheme: InputDecorationTheme (
isDense: true ,
) ,
textStyle: TextStyle ( fontSize: 14 , height: 0.9 ) ,
dropdownMenuEntries: _stats ,
initialSelection: stat ,
onSelected: ( ( value ) {
stat = value ;
list . clear ( ) ;
prisecter = null ;
_isFetchingData = false ;
setState ( ( ) { _fetchData ( ) ; } ) ;
} )
)
] ,
) ,
2024-10-05 22:32:09 +00:00
const Divider ( ) ,
2024-09-14 22:05:50 +00:00
Expanded (
child: ListView . builder (
controller: _scrollController ,
itemCount: list . length ,
2024-09-29 22:02:19 +00:00
prototypeItem: ListTile (
leading: Text ( " 0 " ) ,
title: Text ( " ehhh... " , style: TextStyle ( fontSize: 22 ) ) ,
trailing: SizedBox ( height: 36 , width: 1 ) ,
subtitle: const Text ( " eh... " , style: TextStyle ( color: Colors . grey , fontSize: 12 ) ) ,
) ,
2024-09-14 22:05:50 +00:00
itemBuilder: ( BuildContext context , int index ) {
return ListTile (
leading: Text ( intf . format ( index + 1 ) ) ,
2024-09-15 16:38:07 +00:00
title: Text ( snapshot . data ! [ index ] . username , style: TextStyle ( fontSize: 22 ) ) ,
2024-09-29 22:02:19 +00:00
trailing: switch ( _currentLb ) {
Leaderboards . tl = > Row (
mainAxisSize: MainAxisSize . min ,
children: [
Text ( " ${ f2 . format ( snapshot . data ! [ index ] . tr ) } TR " , style: trailingStyle ) ,
Image . asset ( " res/tetrio_tl_alpha_ranks/ ${ snapshot . data ! [ index ] . rank } .png " , height: 36 )
] ,
) ,
Leaderboards . fullTL = > Row (
mainAxisSize: MainAxisSize . min ,
children: [
Text ( " ${ f2 . format ( snapshot . data ! [ index ] . tr ) } TR " , style: trailingStyle ) ,
Image . asset ( " res/tetrio_tl_alpha_ranks/ ${ snapshot . data ! [ index ] . rank } .png " , height: 36 )
] ,
) ,
Leaderboards . xp = > Text ( " LVL ${ f2 . format ( snapshot . data ! [ index ] . level ) } " , style: trailingStyle ) ,
Leaderboards . ar = > Text ( " ${ intf . format ( snapshot . data ! [ index ] . ar ) } AR " , style: trailingStyle ) ,
Leaderboards . sprint = > Text ( get40lTime ( snapshot . data ! [ index ] . stats . finalTime . inMicroseconds ) , style: trailingStyle ) ,
Leaderboards . blitz = > Text ( intf . format ( snapshot . data ! [ index ] . stats . score ) , style: trailingStyle ) ,
Leaderboards . zenith = > Text ( " ${ f2 . format ( snapshot . data ! [ index ] . stats . zenith ! . altitude ) } m " , style: trailingStyle ) ,
Leaderboards . zenithex = > Text ( " ${ f2 . format ( snapshot . data ! [ index ] . stats . zenith ! . altitude ) } m " , style: trailingStyle )
} ,
2024-09-15 16:38:07 +00:00
subtitle: Text ( switch ( _currentLb ) {
Leaderboards . tl = > " ${ f2 . format ( snapshot . data ! [ index ] . apm ) } APM, ${ f2 . format ( snapshot . data ! [ index ] . pps ) } PPS, ${ f2 . format ( snapshot . data ! [ index ] . vs ) } VS, ${ f2 . format ( snapshot . data ! [ index ] . nerdStats . app ) } APP, ${ f2 . format ( snapshot . data ! [ index ] . nerdStats . vsapm ) } VS/APM " ,
2024-09-29 22:02:19 +00:00
Leaderboards . fullTL = > " ${ f2 . format ( snapshot . data ! [ index ] . apm ) } APM, ${ f2 . format ( snapshot . data ! [ index ] . pps ) } PPS, ${ f2 . format ( snapshot . data ! [ index ] . vs ) } VS, ${ f2 . format ( snapshot . data ! [ index ] . nerdStats . app ) } APP, ${ f2 . format ( snapshot . data ! [ index ] . nerdStats . vsapm ) } VS/APM " ,
2024-09-15 16:38:07 +00:00
Leaderboards . xp = > " ${ f2 . format ( snapshot . data ! [ index ] . xp ) } XP ${ snapshot . data ! [ index ] . playtime . isNegative ? " " : " , $ {playtime(snapshot.data![index].playtime) } of gametime " } " ,
Leaderboards . ar = > " ${ snapshot . data ! [ index ] . ar_counts } " ,
2024-09-29 22:02:19 +00:00
Leaderboards . sprint = > " ${ intf . format ( snapshot . data ! [ index ] . stats . finesse . faults ) } FF, ${ f2 . format ( snapshot . data ! [ index ] . stats . kpp ) } KPP, ${ f2 . format ( snapshot . data ! [ index ] . stats . kps ) } KPS, ${ f2 . format ( snapshot . data ! [ index ] . stats . pps ) } PPS, ${ intf . format ( snapshot . data ! [ index ] . stats . piecesPlaced ) } P " ,
2024-09-15 16:38:07 +00:00
Leaderboards . blitz = > " lvl ${ snapshot . data ! [ index ] . stats . level } , ${ f2 . format ( snapshot . data ! [ index ] . stats . pps ) } PPS, ${ f2 . format ( snapshot . data ! [ index ] . stats . spp ) } SPP " ,
Leaderboards . zenith = > " ${ f2 . format ( snapshot . data ! [ index ] . aggregateStats . apm ) } APM, ${ f2 . format ( snapshot . data ! [ index ] . aggregateStats . pps ) } PPS, ${ intf . format ( snapshot . data ! [ index ] . stats . kills ) } KO's, ${ f2 . format ( snapshot . data ! [ index ] . stats . cps ) } climb speed ( ${ f2 . format ( snapshot . data ! [ index ] . stats . zenith ! . peakrank ) } peak), ${ intf . format ( snapshot . data ! [ index ] . stats . topBtB ) } B2B " ,
Leaderboards . zenithex = > " ${ f2 . format ( snapshot . data ! [ index ] . aggregateStats . apm ) } APM, ${ f2 . format ( snapshot . data ! [ index ] . aggregateStats . pps ) } PPS, ${ intf . format ( snapshot . data ! [ index ] . stats . kills ) } KO's, ${ f2 . format ( snapshot . data ! [ index ] . stats . cps ) } climb speed ( ${ f2 . format ( snapshot . data ! [ index ] . stats . zenith ! . peakrank ) } peak), ${ intf . format ( snapshot . data ! [ index ] . stats . topBtB ) } B2B "
} , style: TextStyle ( color: Colors . grey , fontSize: 12 ) ) ,
2024-09-14 22:05:50 +00:00
) ;
}
) ,
) ,
] ,
) ;
}
if ( snapshot . hasError ) { return FutureError ( snapshot ) ; }
}
return Text ( " huh? " ) ;
} ,
2024-08-09 22:52:50 +00:00
) ,
2024-08-08 23:01:46 +00:00
) ,
) ,
] ,
) ;
}
}
class DestinationGraphs extends StatefulWidget {
final String searchFor ;
//final Function setState;
final BoxConstraints constraints ;
const DestinationGraphs ( { super . key , required this . searchFor , required this . constraints } ) ;
@ override
State < DestinationGraphs > createState ( ) = > _DestinationGraphsState ( ) ;
}
2024-09-09 22:38:52 +00:00
enum Graph {
history ,
leagueState ,
leagueCutoffs
}
2024-08-08 23:01:46 +00:00
class _DestinationGraphsState extends State < DestinationGraphs > {
bool fetchData = false ;
bool _gamesPlayedInsteadOfDateAndTime = false ;
late ZoomPanBehavior _zoomPanBehavior ;
2024-09-12 20:28:55 +00:00
late TooltipBehavior _historyTooltipBehavior ;
2024-08-08 23:01:46 +00:00
late TooltipBehavior _tooltipBehavior ;
2024-09-13 22:00:11 +00:00
late TooltipBehavior _leagueTooltipBehavior ;
2024-08-08 23:01:46 +00:00
String yAxisTitle = " " ;
bool _smooth = false ;
2024-09-12 20:28:55 +00:00
final List < DropdownMenuItem < Stats > > _yAxis = [ for ( MapEntry e in chartsShortTitles . entries ) DropdownMenuItem ( value: e . key , child: Text ( e . value ) ) ] ;
2024-09-09 22:38:52 +00:00
Graph _graph = Graph . history ;
2024-09-12 20:28:55 +00:00
Stats _Ychart = Stats . tr ;
Stats _Xchart = Stats . tr ;
2024-09-09 22:38:52 +00:00
int _season = currentSeason - 1 ;
2024-08-17 23:39:20 +00:00
//Duration postSeasonLeft = seasonStart.difference(DateTime.now());
2024-08-08 23:01:46 +00:00
@ override
void initState ( ) {
2024-09-12 20:28:55 +00:00
_historyTooltipBehavior = TooltipBehavior (
2024-08-08 23:01:46 +00:00
color: Colors . black ,
borderColor: Colors . white ,
enable: true ,
animationDuration: 0 ,
builder: ( dynamic data , dynamic point , dynamic series ,
int pointIndex , int seriesIndex ) {
return Padding (
padding: const EdgeInsets . all ( 8.0 ) ,
child: Column (
mainAxisSize: MainAxisSize . min ,
2024-08-01 23:20:36 +00:00
children: [
2024-08-08 23:01:46 +00:00
Padding (
padding: const EdgeInsets . only ( bottom: 8.0 ) ,
child: Text (
" ${ f4 . format ( data . stat ) } $ yAxisTitle " ,
style: const TextStyle ( fontFamily: " Eurostile Round " , fontSize: 20 ) ,
) ,
) ,
Text ( _gamesPlayedInsteadOfDateAndTime ? t . gamesPlayed ( games: t . games ( n: data . gamesPlayed ) ) : timestamp ( data . timestamp ) )
] ,
) ,
) ;
}
) ;
2024-09-12 20:28:55 +00:00
_tooltipBehavior = TooltipBehavior (
color: Colors . black ,
borderColor: Colors . white ,
enable: true ,
animationDuration: 0 ,
builder: ( dynamic data , dynamic point , dynamic series ,
int pointIndex , int seriesIndex ) {
return Padding (
padding: const EdgeInsets . all ( 8.0 ) ,
child: Column (
mainAxisSize: MainAxisSize . min ,
children: [
Padding (
padding: const EdgeInsets . only ( bottom: 8.0 ) ,
child: Text (
" ${ data . nickname } ( ${ data . rank . toUpperCase ( ) } ) " ,
style: const TextStyle ( fontFamily: " Eurostile Round Extended " , fontSize: 20 ) ,
) ,
) ,
Text ( ' ${ f4 . format ( data . x ) } ${ chartsShortTitles [ _Xchart ] } \n ${ f4 . format ( data . y ) } ${ chartsShortTitles [ _Ychart ] } ' )
] ,
) ,
) ;
}
) ;
2024-09-13 22:00:11 +00:00
_leagueTooltipBehavior = TooltipBehavior (
color: Colors . black ,
borderColor: Colors . white ,
enable: true ,
animationDuration: 0 ,
builder: ( dynamic data , dynamic point , dynamic series ,
int pointIndex , int seriesIndex ) {
return Padding (
padding: const EdgeInsets . all ( 8.0 ) ,
child: Column (
mainAxisSize: MainAxisSize . min ,
children: [
Padding (
padding: const EdgeInsets . only ( bottom: 8.0 ) ,
child: Text (
" ${ f4 . format ( point . y ) } $ yAxisTitle " ,
style: const TextStyle ( fontFamily: " Eurostile Round " , fontSize: 20 ) ,
) ,
) ,
Text ( timestamp ( data . ts ) )
] ,
) ,
) ;
}
) ;
2024-08-08 23:01:46 +00:00
_zoomPanBehavior = ZoomPanBehavior (
enablePinching: true ,
enableSelectionZooming: true ,
enableMouseWheelZooming : true ,
enablePanning: true ,
) ;
super . initState ( ) ;
}
2024-09-12 20:28:55 +00:00
Future < List < Map < Stats , List < _HistoryChartSpot > > > > getHistoryData ( bool fetchHistory ) async {
2024-08-08 23:01:46 +00:00
if ( fetchHistory ) {
try {
var history = await teto . fetchAndsaveTLHistory ( widget . searchFor ) ;
if ( context . mounted ) ScaffoldMessenger . of ( context ) . showSnackBar ( SnackBar ( content: Text ( t . fetchAndsaveTLHistoryResult ( number: history . length ) ) ) ) ;
} on TetrioHistoryNotExist {
if ( context . mounted ) ScaffoldMessenger . of ( context ) . showSnackBar ( SnackBar ( content: Text ( t . noHistorySaved ) ) ) ;
} on P1nkl0bst3rForbidden {
if ( context . mounted ) ScaffoldMessenger . of ( context ) . showSnackBar ( SnackBar ( content: Text ( t . errors . p1nkl0bst3rForbidden ) ) ) ;
} on P1nkl0bst3rInternalProblem {
if ( context . mounted ) ScaffoldMessenger . of ( context ) . showSnackBar ( SnackBar ( content: Text ( t . errors . p1nkl0bst3rinternal ) ) ) ;
} on P1nkl0bst3rTooManyRequests {
if ( context . mounted ) ScaffoldMessenger . of ( context ) . showSnackBar ( SnackBar ( content: Text ( t . errors . p1nkl0bst3rTooManyRequests ) ) ) ;
}
}
2024-09-08 22:10:51 +00:00
List < List < TetraLeague > > states = await Future . wait < List < TetraLeague > > ( [
teto . getStates ( widget . searchFor , season: 1 ) , teto . getStates ( widget . searchFor , season: 2 ) ,
] ) ;
2024-09-12 20:28:55 +00:00
List < Map < Stats , List < _HistoryChartSpot > > > historyData = [ ] ; // [season][metric][spot]
for ( int season = 0 ; season < currentSeason ; season + + ) {
if ( states [ season ] . length > = 2 ) {
Map < Stats , List < _HistoryChartSpot > > statsMap = { } ;
for ( var stat in Stats . values ) statsMap [ stat ] = [ for ( var tl in states [ season ] ) if ( tl . getStatByEnum ( stat ) ! = null ) _HistoryChartSpot ( tl . timestamp , tl . gamesPlayed , tl . rank , tl . getStatByEnum ( stat ) ! . toDouble ( ) ) ] ;
historyData . add ( statsMap ) ;
} else {
historyData . add ( { } ) ;
break ;
}
2024-08-08 23:01:46 +00:00
}
fetchData = false ;
2024-09-08 22:10:51 +00:00
return historyData ;
2024-08-08 23:01:46 +00:00
}
2024-09-12 20:28:55 +00:00
Future < List < _MyScatterSpot > > getTetraLeagueData ( Stats x , Stats y ) async {
TetrioPlayersLeaderboard leaderboard = await teto . fetchTLLeaderboard ( ) ;
List < _MyScatterSpot > _spots = [
for ( TetrioPlayerFromLeaderboard entry in leaderboard . leaderboard )
_MyScatterSpot (
entry . getStatByEnum ( x ) . toDouble ( ) ,
entry . getStatByEnum ( y ) . toDouble ( ) ,
entry . userId ,
entry . username ,
entry . rank ,
rankColors [ entry . rank ] ? ? Colors . white
)
] ;
return _spots ;
}
Widget getHistoryGraph ( ) {
return FutureBuilder < List < Map < Stats , List < _HistoryChartSpot > > > > (
future: getHistoryData ( fetchData ) ,
builder: ( context , snapshot ) {
switch ( snapshot . connectionState ) {
case ConnectionState . none:
case ConnectionState . waiting:
case ConnectionState . active:
return const Center ( child: CircularProgressIndicator ( ) ) ;
case ConnectionState . done:
2024-09-14 22:05:50 +00:00
if ( snapshot . hasData ) {
2024-09-12 20:28:55 +00:00
List < _HistoryChartSpot > selectedGraph = snapshot . data ! [ _season ] [ _Ychart ] ! ;
yAxisTitle = chartsShortTitles [ _Ychart ] ! ;
return SfCartesianChart (
tooltipBehavior: _historyTooltipBehavior ,
zoomPanBehavior: _zoomPanBehavior ,
primaryXAxis: _gamesPlayedInsteadOfDateAndTime ? const NumericAxis ( ) : const DateTimeAxis ( ) ,
primaryYAxis: const NumericAxis (
rangePadding: ChartRangePadding . additional ,
) ,
margin: const EdgeInsets . all ( 0 ) ,
series: < CartesianSeries > [
if ( _gamesPlayedInsteadOfDateAndTime ) StepLineSeries < _HistoryChartSpot , int > (
enableTooltip: true ,
dataSource: selectedGraph ,
animationDuration: 0 ,
opacity: _smooth ? 0 : 1 ,
xValueMapper: ( _HistoryChartSpot data , _ ) = > data . gamesPlayed ,
yValueMapper: ( _HistoryChartSpot data , _ ) = > data . stat ,
color: Theme . of ( context ) . colorScheme . primary ,
trendlines: < Trendline > [
Trendline (
isVisible: _smooth ,
period: ( selectedGraph . length / 175 ) . floor ( ) ,
type: TrendlineType . movingAverage ,
color: Theme . of ( context ) . colorScheme . primary )
] ,
)
else StepLineSeries < _HistoryChartSpot , DateTime > (
enableTooltip: true ,
dataSource: selectedGraph ,
animationDuration: 0 ,
opacity: _smooth ? 0 : 1 ,
xValueMapper: ( _HistoryChartSpot data , _ ) = > data . timestamp ,
yValueMapper: ( _HistoryChartSpot data , _ ) = > data . stat ,
color: Theme . of ( context ) . colorScheme . primary ,
trendlines: < Trendline > [
Trendline (
isVisible: _smooth ,
period: ( selectedGraph . length / 175 ) . floor ( ) ,
type: TrendlineType . movingAverage ,
color: Theme . of ( context ) . colorScheme . primary )
] ,
) ,
] ,
) ;
2024-09-14 22:05:50 +00:00
} else { return FutureError ( snapshot ) ; }
2024-09-12 20:28:55 +00:00
}
}
) ;
}
Widget getLeagueState ( ) {
return FutureBuilder < List < _MyScatterSpot > > (
future: getTetraLeagueData ( _Xchart , _Ychart ) ,
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 ) {
return SfCartesianChart (
tooltipBehavior: _tooltipBehavior ,
zoomPanBehavior: _zoomPanBehavior ,
//primaryXAxis: CategoryAxis(),
series: [
ScatterSeries (
enableTooltip: true ,
dataSource: snapshot . data ,
animationDuration: 0 ,
pointColorMapper: ( data , _ ) = > data . color ,
xValueMapper: ( data , _ ) = > data . x ,
yValueMapper: ( data , _ ) = > data . y ,
onPointTap: ( point ) = > Navigator . push ( context , MaterialPageRoute ( builder: ( context ) = > MainView ( player: snapshot . data ! [ point . pointIndex ! ] . nickname ) , maintainState: false ) ) ,
)
] ,
) ;
2024-09-14 22:05:50 +00:00
} else { return FutureError ( snapshot ) ; }
2024-09-12 20:28:55 +00:00
}
}
) ;
}
Widget getCutoffsHistory ( ) {
2024-09-13 22:00:11 +00:00
return FutureBuilder < List < Cutoffs > > (
future: teto . fetchCutoffsHistory ( ) ,
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 ) {
yAxisTitle = chartsShortTitles [ _Ychart ] ! ;
return SfCartesianChart (
tooltipBehavior: _leagueTooltipBehavior ,
zoomPanBehavior: _zoomPanBehavior ,
2024-09-14 22:05:50 +00:00
primaryXAxis: const DateTimeAxis ( ) ,
primaryYAxis: NumericAxis (
// isInversed: true,
maximum: switch ( _Ychart ) {
Stats . tr = > 25000.0 ,
Stats . gxe = > 100.00 ,
_ = > null
} ,
2024-09-13 22:00:11 +00:00
) ,
margin: const EdgeInsets . all ( 0 ) ,
series: < CartesianSeries > [
for ( String rank in ranks ) StepLineSeries < Cutoffs , DateTime > (
enableTooltip: true ,
dataSource: snapshot . data ,
animationDuration: 0 ,
2024-09-14 22:05:50 +00:00
//opacity: 0.5,
2024-09-13 22:00:11 +00:00
xValueMapper: ( Cutoffs data , _ ) = > data . ts ,
2024-09-14 22:05:50 +00:00
yValueMapper: ( Cutoffs data , _ ) = > switch ( _Ychart ) {
Stats . glicko = > data . glicko [ rank ] ,
Stats . gxe = > data . gxe [ rank ] ,
_ = > data . tr [ rank ]
} ,
color: rankColors [ rank ] !
2024-09-13 22:00:11 +00:00
)
] ,
) ;
2024-09-14 22:05:50 +00:00
} else { return FutureError ( snapshot ) ; }
2024-09-13 22:00:11 +00:00
}
}
) ;
2024-09-12 20:28:55 +00:00
}
2024-08-08 23:01:46 +00:00
@ override
Widget build ( BuildContext context ) {
2024-09-09 22:38:52 +00:00
return Column (
mainAxisSize: MainAxisSize . min ,
children: [
2024-09-12 20:28:55 +00:00
SingleChildScrollView (
scrollDirection: Axis . vertical ,
child: Column (
mainAxisSize: MainAxisSize . min ,
children: [
Card (
child: Wrap (
spacing: 20 ,
crossAxisAlignment: WrapCrossAlignment . center ,
children: [
if ( _graph = = Graph . history ) 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 ) {
setState ( ( ) {
_season = value ! ;
} ) ;
}
2024-08-07 22:42:04 +00:00
) ,
2024-09-12 20:28:55 +00:00
] ,
) ,
if ( _graph ! = Graph . leagueCutoffs ) Row (
mainAxisSize: MainAxisSize . min ,
children: [
const Padding ( padding: EdgeInsets . all ( 8.0 ) , child: Text ( " X: " , style: TextStyle ( fontSize: 22 ) ) ) ,
DropdownButton (
items: switch ( _graph ) {
Graph . history = > [ DropdownMenuItem ( value: false , child: Text ( " Date & Time " ) ) , DropdownMenuItem ( value: true , child: Text ( " Games Played " ) ) ] ,
Graph . leagueState = > _yAxis ,
Graph . leagueCutoffs = > [ ] ,
} ,
value: _graph = = Graph . history ? _gamesPlayedInsteadOfDateAndTime : _Xchart ,
onChanged: ( value ) {
setState ( ( ) {
if ( _graph = = Graph . history )
_gamesPlayedInsteadOfDateAndTime = value ! as bool ;
else _Xchart = value ! as Stats ;
} ) ;
}
2024-09-09 22:38:52 +00:00
) ,
] ,
) ,
2024-09-12 20:28:55 +00:00
Row (
mainAxisSize: MainAxisSize . min ,
children: [
const Padding ( padding: EdgeInsets . all ( 8.0 ) , child: Text ( " Y: " , style: TextStyle ( fontSize: 22 ) ) ) ,
DropdownButton < Stats > (
2024-09-14 22:05:50 +00:00
items: _graph = = Graph . leagueCutoffs ? [ DropdownMenuItem ( value: Stats . tr , child: Text ( chartsShortTitles [ Stats . tr ] ! ) ) , DropdownMenuItem ( value: Stats . glicko , child: Text ( chartsShortTitles [ Stats . glicko ] ! ) ) , DropdownMenuItem ( value: Stats . gxe , child: Text ( chartsShortTitles [ Stats . gxe ] ! ) ) ] : _yAxis ,
2024-09-12 20:28:55 +00:00
value: _Ychart ,
onChanged: ( value ) {
setState ( ( ) {
_Ychart = value ! ;
} ) ;
}
) ,
] ,
) ,
if ( _graph ! = Graph . leagueState ) Row (
mainAxisSize: MainAxisSize . min ,
children: [
Checkbox ( value: _smooth ,
checkColor: Colors . black ,
onChanged: ( ( value ) {
setState ( ( ) {
_smooth = value ! ;
} ) ;
} ) ) ,
Text ( t . smooth , style: const TextStyle ( color: Colors . white , fontSize: 22 ) )
] ,
) ,
IconButton ( onPressed: ( ) = > _zoomPanBehavior . reset ( ) , icon: const Icon ( Icons . refresh ) , alignment: Alignment . center , )
2024-09-09 22:38:52 +00:00
] ,
2024-09-12 20:28:55 +00:00
) ,
) ,
Card (
child: SizedBox (
width: MediaQuery . of ( context ) . size . width - 88 ,
height: MediaQuery . of ( context ) . size . height - 96 ,
child: Padding ( padding: const EdgeInsets . fromLTRB ( 40 , 30 , 40 , 30 ) ,
child: switch ( _graph ) {
Graph . history = > getHistoryGraph ( ) ,
Graph . leagueState = > getLeagueState ( ) ,
Graph . leagueCutoffs = > getCutoffsHistory ( )
} ,
)
) ,
)
] ,
) ,
2024-09-09 22:38:52 +00:00
) ,
SegmentedButton < Graph > (
showSelectedIcon: false ,
segments: < ButtonSegment < Graph > > [
const ButtonSegment < Graph > (
value: Graph . history ,
label: Text ( ' Player History ' ) ) ,
ButtonSegment < Graph > (
value: Graph . leagueState ,
label: Text ( ' League State ' ) ) ,
ButtonSegment < Graph > (
value: Graph . leagueCutoffs ,
label: Text ( ' League Cutoffs ' ) ,
) ,
] ,
selected: < Graph > { _graph } ,
onSelectionChanged: ( Set < Graph > newSelection ) {
setState ( ( ) {
_graph = newSelection . first ;
2024-09-12 20:28:55 +00:00
switch ( newSelection . first ) {
case Graph . leagueCutoffs:
case Graph . history:
_Ychart = Stats . tr ;
case Graph . leagueState:
_Ychart = Stats . apm ;
}
2024-09-09 22:38:52 +00:00
} ) ; } )
] ,
2024-08-08 23:01:46 +00:00
) ;
}
}
class _HistoryChartSpot {
final DateTime timestamp ;
final int gamesPlayed ;
final String rank ;
final double stat ;
const _HistoryChartSpot ( this . timestamp , this . gamesPlayed , this . rank , this . stat ) ;
}
2024-09-12 20:28:55 +00:00
class _MyScatterSpot {
num x ;
num y ;
String id ;
String nickname ;
String rank ;
Color color ;
_MyScatterSpot ( this . x , this . y , this . id , this . nickname , this . rank , this . color ) ;
}
2024-08-08 23:01:46 +00:00
class DestinationHome extends StatefulWidget {
final String searchFor ;
//final Function setState;
final BoxConstraints constraints ;
const DestinationHome ( { super . key , required this . searchFor , required this . constraints } ) ;
@ override
State < DestinationHome > createState ( ) = > _DestinationHomeState ( ) ;
}
2024-08-15 21:55:45 +00:00
class FetchResults {
bool success ;
TetrioPlayer ? player ;
2024-09-08 22:10:51 +00:00
List < TetraLeague > states ;
2024-08-15 21:55:45 +00:00
Summaries ? summaries ;
2024-09-01 21:44:19 +00:00
Cutoffs ? cutoffs ;
2024-08-15 21:55:45 +00:00
Exception ? exception ;
2024-09-08 22:10:51 +00:00
FetchResults ( this . success , this . player , this . states , this . summaries , this . cutoffs , this . exception ) ;
2024-08-15 21:55:45 +00:00
}
class RecordSummary extends StatelessWidget {
final RecordSingle ? record ;
final bool hideRank ;
final bool ? betterThanRankAverage ;
final MapEntry ? closestAverage ;
final bool ? betterThanClosestAverage ;
final String ? rank ;
const RecordSummary ( { super . key , required this . record , this . betterThanRankAverage , this . closestAverage , this . betterThanClosestAverage , this . rank , this . hideRank = false } ) ;
@ override
Widget build ( BuildContext context ) {
return Row (
mainAxisSize: MainAxisSize . min ,
children: [
if ( closestAverage ! = null & & record ! = null ) Padding ( padding: const EdgeInsets . only ( right: 8.0 ) ,
child: Image . asset ( " res/tetrio_tl_alpha_ranks/ ${ closestAverage ! . key } .png " , height: 96 ) )
else ! hideRank ? Image . asset ( " res/tetrio_tl_alpha_ranks/z.png " , height: 96 ) : Container ( ) ,
if ( record ! = null ) Column (
crossAxisAlignment: hideRank ? CrossAxisAlignment . center : CrossAxisAlignment . start ,
mainAxisSize: MainAxisSize . min ,
children: [
RichText (
textAlign: hideRank ? TextAlign . center : TextAlign . start ,
text: TextSpan (
text: switch ( record ! . gamemode ) {
" 40l " = > get40lTime ( record ! . stats . finalTime . inMicroseconds ) ,
" blitz " = > NumberFormat . decimalPattern ( ) . format ( record ! . stats . score ) ,
" 5mblast " = > get40lTime ( record ! . stats . finalTime . inMicroseconds ) ,
" zenith " = > " ${ f2 . format ( record ! . stats . zenith ! . altitude ) } m " ,
" zenithex " = > " ${ f2 . format ( record ! . stats . zenith ! . altitude ) } m " ,
_ = > record ! . stats . score . toString ( )
} ,
style: const TextStyle ( fontFamily: " Eurostile Round " , fontSize: 36 , fontWeight: FontWeight . w500 , color: Colors . white , height: 0.9 ) ,
) ,
) ,
RichText (
textAlign: hideRank ? TextAlign . center : TextAlign . start ,
text: TextSpan (
text: " " ,
style: const TextStyle ( fontFamily: " Eurostile Round " , fontSize: 14 , color: Colors . grey ) ,
children: [
2024-08-24 14:41:07 +00:00
if ( rank ! = null & & rank ! = " z " & & rank ! = " x+ " ) TextSpan ( text: " $ {t.verdictGeneral(n: switch(record!.gamemode){
2024-08-15 21:55:45 +00:00
" 40l " = > readableTimeDifference ( record ! . stats . finalTime , sprintAverages [ rank ] ! ) ,
" blitz " = > readableIntDifference ( record ! . stats . score , blitzAverages [ rank ] ! ) ,
_ = > record ! . stats . score . toString ( )
} , verdict: betterThanRankAverage ? ? false ? t . verdictBetter : t . verdictWorse , rank: rank ! . toUpperCase ( ) ) } \ n " , style: TextStyle(
color: betterThanClosestAverage ? ? false ? Colors . greenAccent : Colors . redAccent
) )
else if ( ( rank = = null | | rank = = " z " ) & & closestAverage ! = null ) TextSpan ( text: " $ {t.verdictGeneral(n: switch(record!.gamemode){
" 40l " = > readableTimeDifference ( record ! . stats . finalTime , closestAverage ! . value ) ,
" blitz " = > readableIntDifference ( record ! . stats . score , closestAverage ! . value ) ,
_ = > record ! . stats . score . toString ( )
} , verdict: betterThanClosestAverage ? ? false ? t . verdictBetter : t . verdictWorse , rank: closestAverage ! . key . toUpperCase ( ) ) } \ n " , style: TextStyle(
color: betterThanClosestAverage ? ? false ? Colors . greenAccent : Colors . redAccent
) ) ,
if ( record ! . rank ! = - 1 ) TextSpan ( text: " № ${ intf . format ( record ! . rank ) } " , style: TextStyle ( color: getColorOfRank ( record ! . rank ) ) ) ,
if ( record ! . rank ! = - 1 & & record ! . countryRank ! = - 1 ) const TextSpan ( text: " • " ) ,
if ( record ! . countryRank ! = - 1 ) TextSpan ( text: " № ${ intf . format ( record ! . countryRank ) } local " , style: TextStyle ( color: getColorOfRank ( record ! . countryRank ) ) ) ,
const TextSpan ( text: " \n " ) ,
TextSpan ( text: timestamp ( record ! . timestamp ) ) ,
]
) ,
) ,
] ,
2024-08-31 23:00:26 +00:00
) else if ( hideRank ) RichText ( text: const TextSpan (
2024-08-15 21:55:45 +00:00
text: " --- " ,
2024-08-31 23:00:26 +00:00
style: TextStyle ( fontFamily: " Eurostile Round " , fontSize: 36 , fontWeight: FontWeight . w500 , color: Colors . grey ) ,
2024-08-15 21:55:45 +00:00
) ,
)
] ,
) ;
}
}
2024-09-05 21:42:21 +00:00
class LeagueCard extends StatelessWidget {
final TetraLeague league ;
final bool showSeasonNumber ;
const LeagueCard ( { super . key , required this . league , this . showSeasonNumber = false } ) ;
@ override
Widget build ( BuildContext context ) {
return Card (
child: Padding (
padding: const EdgeInsets . fromLTRB ( 20.0 , 8.0 , 20.0 , 12.0 ) ,
child: Center (
child: Column (
crossAxisAlignment: CrossAxisAlignment . center ,
children: [
2024-09-08 22:10:51 +00:00
if ( showSeasonNumber ) Row (
crossAxisAlignment: CrossAxisAlignment . baseline ,
textBaseline: TextBaseline . alphabetic ,
children: [
Text ( " Season ${ league . season } " , style: TextStyle ( fontFamily: " Eurostile Round Extended " , fontSize: 28 , height: 0.9 ) ) ,
Spacer ( ) ,
Text (
" ${ seasonStarts . elementAtOrNull ( league . season - 1 ) ! = null ? timestamp ( seasonStarts [ league . season - 1 ] ) : " --- " } — ${ seasonEnds . elementAtOrNull ( league . season - 1 ) ! = null ? timestamp ( seasonEnds [ league . season - 1 ] ) : " --- " } " ,
textAlign: TextAlign . center ,
style: TextStyle ( color: Colors . grey ) ) ,
] ,
)
else Text ( " Tetra League " , style: TextStyle ( fontFamily: " Eurostile Round Extended " , fontSize: 28 , height: 0.9 ) ) ,
2024-10-05 22:32:09 +00:00
const Divider ( ) ,
2024-09-05 21:42:21 +00:00
TLRatingThingy ( userID: " " , tlData: league , showPositions: true ) ,
2024-10-05 22:32:09 +00:00
const Divider ( ) ,
2024-09-05 21:42:21 +00:00
Text ( " ${ league . apm ! = null ? f2 . format ( league . apm ) : " -.-- " } APM • ${ league . pps ! = null ? f2 . format ( league . pps ) : " -.-- " } PPS • ${ league . vs ! = null ? f2 . format ( league . vs ) : " -.-- " } VS • ${ league . nerdStats ! = null ? f2 . format ( league . nerdStats ! . app ) : " -.-- " } APP • ${ league . nerdStats ! = null ? f2 . format ( league . nerdStats ! . vsapm ) : " -.-- " } VS/APM " , style: const TextStyle ( color: Colors . grey ) )
] ,
) ,
) ,
) ,
) ;
}
}
2024-09-10 21:22:17 +00:00
class _DestinationHomeState extends State < DestinationHome > with SingleTickerProviderStateMixin {
2024-08-15 21:55:45 +00:00
Cards rightCard = Cards . overview ;
2024-08-09 22:52:50 +00:00
CardMod cardMod = CardMod . info ;
2024-08-17 23:39:20 +00:00
//Duration postSeasonLeft = seasonStart.difference(DateTime.now());
2024-08-09 22:52:50 +00:00
late Map < Cards , List < ButtonSegment < CardMod > > > modeButtons ;
2024-08-13 22:45:28 +00:00
late MapEntry ? closestAverageBlitz ;
2024-08-09 22:52:50 +00:00
late bool blitzBetterThanClosestAverage ;
2024-08-13 22:45:28 +00:00
late MapEntry ? closestAverageSprint ;
2024-08-09 22:52:50 +00:00
late bool sprintBetterThanClosestAverage ;
2024-09-10 21:22:17 +00:00
late AnimationController _transition ;
2024-09-11 22:41:02 +00:00
late final Animation < Offset > _offsetAnimation ;
2024-08-09 22:52:50 +00:00
bool ? sprintBetterThanRankAverage ;
bool ? blitzBetterThanRankAverage ;
Widget getOverviewCard ( Summaries summaries ) {
2024-08-15 21:55:45 +00:00
return Column (
2024-08-09 22:52:50 +00:00
children: [
2024-08-31 23:00:26 +00:00
const Card (
2024-08-09 22:52:50 +00:00
child: Padding (
padding: EdgeInsets . only ( bottom: 4.0 ) ,
child: Center (
child: Column (
mainAxisSize: MainAxisSize . min ,
crossAxisAlignment: CrossAxisAlignment . center ,
children: [
Text ( " Overview " , style: TextStyle ( fontFamily: " Eurostile Round Extended " , fontSize: 42 ) ) ,
] ,
) ,
) ,
) ,
) ,
2024-09-05 21:42:21 +00:00
LeagueCard ( league: summaries . league ) ,
2024-08-15 21:55:45 +00:00
Row (
crossAxisAlignment: CrossAxisAlignment . start ,
children: [
Expanded (
child: Card (
child: Padding (
padding: const EdgeInsets . fromLTRB ( 20.0 , 8.0 , 20.0 , 12.0 ) ,
child: Column (
crossAxisAlignment: CrossAxisAlignment . center ,
children: [
2024-08-31 23:00:26 +00:00
const Text ( " 40 Lines " , style: TextStyle ( fontFamily: " Eurostile Round Extended " , fontSize: 28 , height: 0.9 ) ) ,
2024-10-05 22:32:09 +00:00
const Divider ( ) ,
2024-08-15 21:55:45 +00:00
RecordSummary ( record: summaries . sprint , betterThanClosestAverage: sprintBetterThanClosestAverage , betterThanRankAverage: sprintBetterThanRankAverage , closestAverage: closestAverageSprint , rank: summaries . league . percentileRank ) ,
2024-10-05 22:32:09 +00:00
const Divider ( ) ,
2024-08-31 23:00:26 +00:00
Text ( " ${ summaries . sprint ! = null ? intf . format ( summaries . sprint ! . stats . piecesPlaced ) : " --- " } P • ${ summaries . sprint ! = null ? f2 . format ( summaries . sprint ! . stats . pps ) : " --- " } PPS • ${ summaries . sprint ! = null ? f2 . format ( summaries . sprint ! . stats . kpp ) : " --- " } KPP " , style: const TextStyle ( color: Colors . grey ) )
2024-08-15 21:55:45 +00:00
] ,
) ,
) ,
) ,
) ,
Expanded (
child: Card (
child: Padding (
padding: const EdgeInsets . fromLTRB ( 20.0 , 8.0 , 20.0 , 12.0 ) ,
child: Column (
crossAxisAlignment: CrossAxisAlignment . center ,
children: [
2024-08-31 23:00:26 +00:00
const Text ( " Blitz " , style: TextStyle ( fontFamily: " Eurostile Round Extended " , fontSize: 28 , height: 0.9 ) ) ,
2024-10-05 22:32:09 +00:00
const Divider ( ) ,
2024-08-15 21:55:45 +00:00
RecordSummary ( record: summaries . blitz , betterThanClosestAverage: blitzBetterThanClosestAverage , betterThanRankAverage: blitzBetterThanRankAverage , closestAverage: closestAverageBlitz , rank: summaries . league . percentileRank ) ,
2024-10-05 22:32:09 +00:00
const Divider ( ) ,
2024-08-31 23:00:26 +00:00
Text ( " Level ${ summaries . blitz ! = null ? intf . format ( summaries . blitz ! . stats . level ) : " -- " } • ${ summaries . blitz ! = null ? f2 . format ( summaries . blitz ! . stats . spp ) : " -.-- " } SPP • ${ summaries . blitz ! = null ? f2 . format ( summaries . blitz ! . stats . pps ) : " --- " } PPS " , style: const TextStyle ( color: Colors . grey ) )
2024-08-15 21:55:45 +00:00
] ,
) ,
) ,
) ,
) ,
] ,
) ,
Row (
crossAxisAlignment: CrossAxisAlignment . start ,
children: [
Expanded (
child: Card (
child: Padding (
padding: const EdgeInsets . fromLTRB ( 20.0 , 8.0 , 20.0 , 14.0 ) ,
child: Column (
crossAxisAlignment: CrossAxisAlignment . center ,
children: [
2024-08-31 23:00:26 +00:00
const Text ( " QP " , style: TextStyle ( fontFamily: " Eurostile Round Extended " , fontSize: 28 , height: 0.9 ) ) ,
2024-10-05 22:32:09 +00:00
const Divider ( ) ,
2024-08-15 21:55:45 +00:00
RecordSummary ( record: summaries . zenith , hideRank: true ) ,
2024-10-05 22:32:09 +00:00
const Divider ( ) ,
2024-08-31 23:00:26 +00:00
Text ( " Overall PB: ${ ( summaries . achievements . isNotEmpty & & summaries . achievements . firstWhere ( ( e ) = > e . k = = 18 ) . v ! = null ) ? f2 . format ( summaries . achievements . firstWhere ( ( e ) = > e . k = = 18 ) . v ! ) : " -.-- " } m " , style: const TextStyle ( color: Colors . grey ) )
2024-08-15 21:55:45 +00:00
] ,
) ,
) ,
) ,
) ,
Expanded (
child: Card (
child: Padding (
padding: const EdgeInsets . fromLTRB ( 20.0 , 8.0 , 20.0 , 14.0 ) ,
child: Column (
crossAxisAlignment: CrossAxisAlignment . center ,
children: [
2024-08-31 23:00:26 +00:00
const Text ( " QP Expert " , style: TextStyle ( fontFamily: " Eurostile Round Extended " , fontSize: 28 , height: 0.9 ) ) ,
2024-10-05 22:32:09 +00:00
const Divider ( ) ,
2024-08-15 21:55:45 +00:00
RecordSummary ( record: summaries . zenithEx , hideRank: true , ) ,
2024-10-05 22:32:09 +00:00
const Divider ( ) ,
2024-08-31 23:00:26 +00:00
Text ( " Overall PB: ${ ( summaries . achievements . isNotEmpty & & summaries . achievements . firstWhere ( ( e ) = > e . k = = 19 ) . v ! = null ) ? f2 . format ( summaries . achievements . firstWhere ( ( e ) = > e . k = = 19 ) . v ! ) : " -.-- " } m " , style: const TextStyle ( color: Colors . grey ) )
2024-08-15 21:55:45 +00:00
] ,
) ,
) ,
) ,
) ,
] ,
) ,
Row (
crossAxisAlignment: CrossAxisAlignment . start ,
children: [
Expanded (
child: Card (
child: Padding (
padding: const EdgeInsets . fromLTRB ( 20.0 , 8.0 , 20.0 , 14.0 ) ,
child: Column (
crossAxisAlignment: CrossAxisAlignment . center ,
children: [
2024-08-31 23:00:26 +00:00
const Text ( " Zen " , style: TextStyle ( fontFamily: " Eurostile Round Extended " , fontSize: 28 , height: 0.9 ) ) ,
2024-08-15 21:55:45 +00:00
Text ( " Level ${ intf . format ( summaries . zen . level ) } " , style: const TextStyle ( fontFamily: " Eurostile Round " , fontSize: 36 , fontWeight: FontWeight . w500 , color: Colors . white ) ) ,
Text ( " Score ${ intf . format ( summaries . zen . score ) } " ) ,
2024-08-31 23:00:26 +00:00
Text ( " Level up requirement: ${ intf . format ( summaries . zen . scoreRequirement ) } " , style: const TextStyle ( color: Colors . grey ) )
2024-08-15 21:55:45 +00:00
] ,
) ,
) ,
) ,
) ,
Expanded (
child: Card (
child: Padding (
padding: const EdgeInsets . fromLTRB ( 20.0 , 0.0 , 20.0 , 20.0 ) ,
child: Column (
crossAxisAlignment: CrossAxisAlignment . center ,
children: [
Stack (
alignment: AlignmentDirectional . bottomStart ,
children: [
const Text ( " f " , style: TextStyle (
fontStyle: FontStyle . italic ,
fontSize: 65 ,
height: 1.2 ,
) ) ,
const Positioned ( left: 25 , top: 20 , child: Text ( " inesse " , style: TextStyle ( fontFamily: " Eurostile Round Extended " ) ) ) ,
Padding (
padding: const EdgeInsets . only ( left: 10.0 ) ,
2024-08-31 23:00:26 +00:00
child: Text ( " $ {(summaries.achievements.isNotEmpty && summaries.achievements.firstWhere((e) => e.k == 4).v != null && summaries.achievements.firstWhere((e) => e.k == 1).v != null) ?
f3 . format ( summaries . achievements . firstWhere ( ( e ) = > e . k = = 4 ) . v ! / summaries . achievements . firstWhere ( ( e ) = > e . k = = 1 ) . v ! * 100 ) : " --.--- " } % " , style: const TextStyle(
2024-08-15 21:55:45 +00:00
//shadows: textShadow,
fontFamily: " Eurostile Round Extended " ,
fontSize: 36 ,
fontWeight: FontWeight . w500 ,
color: Colors . white
) ) ,
)
] ,
) ,
Row (
children: [
2024-08-31 23:00:26 +00:00
const Text ( " Total pieces placed: " ) ,
const Spacer ( ) ,
Text ( ( summaries . achievements . isNotEmpty & & summaries . achievements . firstWhere ( ( e ) = > e . k = = 1 ) . v ! = null ) ? intf . format ( summaries . achievements . firstWhere ( ( e ) = > e . k = = 1 ) . v ! ) : " --- " ) ,
2024-08-15 21:55:45 +00:00
] ,
) ,
Row (
children: [
2024-08-31 23:00:26 +00:00
const Text ( " - Placed with perfect finesse: " ) ,
const Spacer ( ) ,
Text ( ( summaries . achievements . isNotEmpty & & summaries . achievements . firstWhere ( ( e ) = > e . k = = 4 ) . v ! = null ) ? intf . format ( summaries . achievements . firstWhere ( ( e ) = > e . k = = 4 ) . v ! ) : " --- " ) ,
2024-08-15 21:55:45 +00:00
] ,
)
] ,
) ,
) ,
) ,
) ,
] ,
) ,
2024-08-31 23:00:26 +00:00
if ( summaries . achievements . isNotEmpty ) Card (
2024-08-09 22:52:50 +00:00
child: Padding (
2024-08-31 23:00:26 +00:00
padding: const EdgeInsets . fromLTRB ( 20.0 , 0.0 , 20.0 , 0.0 ) ,
2024-08-09 22:52:50 +00:00
child: Column (
children: [
2024-08-15 21:55:45 +00:00
if ( summaries . achievements . firstWhere ( ( e ) = > e . k = = 16 ) . v ! = null ) Row (
2024-08-09 22:52:50 +00:00
children: [
2024-08-31 23:00:26 +00:00
const Text ( " Total height climbed in QP " ) ,
const Spacer ( ) ,
2024-08-15 21:55:45 +00:00
Text ( " ${ f2 . format ( summaries . achievements . firstWhere ( ( e ) = > e . k = = 16 ) . v ! ) } m " ) ,
] ,
) ,
if ( summaries . achievements . firstWhere ( ( e ) = > e . k = = 17 ) . v ! = null ) Row (
children: [
2024-08-31 23:00:26 +00:00
const Text ( " KO's in QP " ) ,
const Spacer ( ) ,
Text ( intf . format ( summaries . achievements . firstWhere ( ( e ) = > e . k = = 17 ) . v ! ) ) ,
2024-08-09 22:52:50 +00:00
] ,
)
] ,
) ,
) ,
) ,
]
) ;
}
2024-09-08 22:10:51 +00:00
Widget getTetraLeagueCard ( TetraLeague data , Cutoffs ? cutoffs , List < TetraLeague > states ) {
TetraLeague ? toCompare = states . length > = 2 ? states . elementAtOrNull ( states . length - 2 ) : null ;
2024-08-09 22:52:50 +00:00
return Column (
children: [
Card (
2024-09-05 21:42:21 +00:00
//surfaceTintColor: rankColors[data.rank],
2024-08-09 22:52:50 +00:00
child: Padding (
padding: const EdgeInsets . only ( bottom: 4.0 ) ,
child: Center (
child: Column (
mainAxisSize: MainAxisSize . min ,
crossAxisAlignment: CrossAxisAlignment . center ,
children: [
Text ( t . tetraLeague , style: const TextStyle ( fontFamily: " Eurostile Round Extended " , fontSize: 42 ) ) ,
2024-09-08 22:10:51 +00:00
//Text("${states.last.timestamp} ${states.last.tr}", textAlign: TextAlign.center)
2024-08-09 22:52:50 +00:00
] ,
) ,
) ,
) ,
) ,
2024-09-08 22:10:51 +00:00
TetraLeagueThingy ( league: data , toCompare: toCompare , cutoffs: cutoffs ) ,
2024-08-15 21:55:45 +00:00
if ( data . nerdStats ! = null ) Card (
2024-09-05 21:42:21 +00:00
//surfaceTintColor: rankColors[data.rank],
2024-08-09 22:52:50 +00:00
child: Row (
mainAxisSize: MainAxisSize . min ,
children: [
const Spacer ( ) ,
Text ( t . nerdStats , style: const TextStyle ( fontFamily: " Eurostile Round Extended " , fontSize: 42 ) ) ,
const Spacer ( )
] ,
) ,
) ,
2024-09-08 22:10:51 +00:00
if ( data . nerdStats ! = null ) NerdStatsThingy ( nerdStats: data . nerdStats ! , oldNerdStats: toCompare ? . nerdStats ) ,
2024-08-15 21:55:45 +00:00
if ( data . nerdStats ! = null ) GraphsThingy ( nerdStats: data . nerdStats ! , playstyle: data . playstyle ! , apm: data . apm ! , pps: data . pps ! , vs: data . vs ! )
2024-08-09 22:52:50 +00:00
] ,
) ;
}
2024-09-05 21:42:21 +00:00
Widget getPreviousSeasonsList ( Map < int , TetraLeague > pastLeague ) {
return Column (
children: [
Card (
child: Padding (
padding: const EdgeInsets . only ( bottom: 4.0 ) ,
child: Center (
child: Column (
mainAxisSize: MainAxisSize . min ,
crossAxisAlignment: CrossAxisAlignment . center ,
children: [
Text ( " Previous Seasons " , style: const TextStyle ( fontFamily: " Eurostile Round Extended " , fontSize: 42 ) ) ,
//Text("${t.seasonStarts} ${countdown(postSeasonLeft)}", textAlign: TextAlign.center)
] ,
) ,
) ,
) ,
) ,
for ( var key in pastLeague . keys ) Card (
child: LeagueCard ( league: pastLeague [ key ] ! , showSeasonNumber: true ) ,
)
] ,
) ;
}
2024-08-24 14:41:07 +00:00
Widget getListOfRecords ( String recentStream , String topStream , BoxConstraints constraints ) {
2024-08-12 23:07:59 +00:00
return Column (
children: [
2024-08-31 23:00:26 +00:00
const Card (
2024-08-12 23:07:59 +00:00
child: Padding (
2024-08-31 23:00:26 +00:00
padding: EdgeInsets . only ( bottom: 4.0 ) ,
2024-08-12 23:07:59 +00:00
child: Center (
child: Column (
mainAxisSize: MainAxisSize . min ,
crossAxisAlignment: CrossAxisAlignment . center ,
children: [
2024-08-31 23:00:26 +00:00
Text ( " Records " , style: TextStyle ( fontFamily: " Eurostile Round Extended " , fontSize: 42 ) ) ,
2024-08-24 14:41:07 +00:00
//Text("${t.seasonStarts} ${countdown(postSeasonLeft)}", textAlign: TextAlign.center)
2024-08-12 23:07:59 +00:00
] ,
) ,
) ,
) ,
) ,
Card (
2024-08-13 22:45:28 +00:00
clipBehavior: Clip . antiAlias ,
2024-08-24 14:41:07 +00:00
child: DefaultTabController ( length: 2 ,
child: Column (
mainAxisSize: MainAxisSize . min ,
children: [
2024-08-31 23:00:26 +00:00
const TabBar (
2024-08-24 14:41:07 +00:00
tabs: [
Tab ( text: " Recent " ) ,
Tab ( text: " Top " ) ,
] ,
) ,
SizedBox (
height: 400 ,
child: TabBarView (
2024-08-13 22:45:28 +00:00
children: [
2024-08-24 14:41:07 +00:00
FutureBuilder < SingleplayerStream > (
future: teto . fetchStream ( widget . searchFor , recentStream ) ,
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 ) {
return Column (
children: [
for ( int i = 0 ; i < snapshot . data ! . records . length ; i + + ) ListTile (
onTap: ( ) = > Navigator . push ( context , MaterialPageRoute ( builder: ( context ) = > SingleplayerRecordView ( record: snapshot . data ! . records [ i ] ) ) ) ,
leading: Text (
switch ( snapshot . data ! . records [ i ] . gamemode ) {
" 40l " = > " 40L " ,
" blitz " = > " BLZ " ,
" 5mblast " = > " 5MB " ,
" zenith " = > " QP " ,
" zenithex " = > " QPE " ,
String ( ) = > " huh " ,
} ,
style: const TextStyle ( fontFamily: " Eurostile Round " , fontSize: 28 , shadows: textShadow , height: 0.9 )
) ,
title: Text (
switch ( snapshot . data ! . records [ i ] . gamemode ) {
" 40l " = > get40lTime ( snapshot . data ! . records [ i ] . stats . finalTime . inMicroseconds ) ,
" blitz " = > t . blitzScore ( p: NumberFormat . decimalPattern ( ) . format ( snapshot . data ! . records [ i ] . stats . score ) ) ,
" 5mblast " = > get40lTime ( snapshot . data ! . records [ i ] . stats . finalTime . inMicroseconds ) ,
" zenith " = > " ${ f2 . format ( snapshot . data ! . records [ i ] . stats . zenith ! . altitude ) } m ${ ( snapshot . data ! . records [ i ] . extras as ZenithExtras ) . mods . isNotEmpty ? " ( $ {t.withModsPlural(n: (snapshot.data!.records[i].extras as ZenithExtras).mods.length) } ) " : " " } " ,
" zenithex " = > " ${ f2 . format ( snapshot . data ! . records [ i ] . stats . zenith ! . altitude ) } m ${ ( snapshot . data ! . records [ i ] . extras as ZenithExtras ) . mods . isNotEmpty ? " ( $ {t.withModsPlural(n: (snapshot.data!.records[i].extras as ZenithExtras).mods.length) } ) " : " " } " ,
String ( ) = > " huh " ,
} ,
style: const TextStyle ( fontSize: 18 ) ) ,
subtitle: Text ( timestamp ( snapshot . data ! . records [ i ] . timestamp ) , style: const TextStyle ( color: Colors . grey , height: 0.85 ) ) ,
trailing: SpTrailingStats ( snapshot . data ! . records [ i ] , snapshot . data ! . records [ i ] . gamemode )
)
] ,
) ;
}
2024-09-14 22:05:50 +00:00
if ( snapshot . hasError ) { return FutureError ( snapshot ) ; }
2024-08-24 14:41:07 +00:00
}
2024-08-31 23:00:26 +00:00
return const Text ( " what? " ) ;
2024-08-13 22:45:28 +00:00
} ,
) ,
2024-08-24 14:41:07 +00:00
FutureBuilder < SingleplayerStream > (
future: teto . fetchStream ( widget . searchFor , topStream ) ,
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 ) {
return Column (
children: [
for ( int i = 0 ; i < snapshot . data ! . records . length ; i + + ) ListTile (
onTap: ( ) = > Navigator . push ( context , MaterialPageRoute ( builder: ( context ) = > SingleplayerRecordView ( record: snapshot . data ! . records [ i ] ) ) ) ,
leading: Text (
" # ${ i + 1 } " ,
style: const TextStyle ( fontFamily: " Eurostile Round " , fontSize: 28 , shadows: textShadow , height: 0.9 )
) ,
title: Text (
switch ( snapshot . data ! . records [ i ] . gamemode ) {
" 40l " = > get40lTime ( snapshot . data ! . records [ i ] . stats . finalTime . inMicroseconds ) ,
" blitz " = > t . blitzScore ( p: NumberFormat . decimalPattern ( ) . format ( snapshot . data ! . records [ i ] . stats . score ) ) ,
" 5mblast " = > get40lTime ( snapshot . data ! . records [ i ] . stats . finalTime . inMicroseconds ) ,
" zenith " = > " ${ f2 . format ( snapshot . data ! . records [ i ] . stats . zenith ! . altitude ) } m ${ ( snapshot . data ! . records [ i ] . extras as ZenithExtras ) . mods . isNotEmpty ? " ( $ {t.withModsPlural(n: (snapshot.data!.records[i].extras as ZenithExtras).mods.length) } ) " : " " } " ,
" zenithex " = > " ${ f2 . format ( snapshot . data ! . records [ i ] . stats . zenith ! . altitude ) } m ${ ( snapshot . data ! . records [ i ] . extras as ZenithExtras ) . mods . isNotEmpty ? " ( $ {t.withModsPlural(n: (snapshot.data!.records[i].extras as ZenithExtras).mods.length) } ) " : " " } " ,
String ( ) = > " huh " ,
} ,
style: const TextStyle ( fontSize: 18 ) ) ,
subtitle: Text ( timestamp ( snapshot . data ! . records [ i ] . timestamp ) , style: const TextStyle ( color: Colors . grey , height: 0.85 ) ) ,
trailing: SpTrailingStats ( snapshot . data ! . records [ i ] , snapshot . data ! . records [ i ] . gamemode )
)
] ,
) ;
}
2024-09-14 22:05:50 +00:00
if ( snapshot . hasError ) { return FutureError ( snapshot ) ; }
2024-08-24 14:41:07 +00:00
}
2024-08-31 23:00:26 +00:00
return const Text ( " what? " ) ;
2024-08-13 22:45:28 +00:00
} ,
2024-08-24 14:41:07 +00:00
) ,
]
) ,
)
] ,
) ,
)
2024-08-13 22:45:28 +00:00
) ,
] ,
) ;
}
Widget getRecentTLrecords ( BoxConstraints constraints ) {
return Column (
children: [
Card (
child: Padding (
padding: const EdgeInsets . only ( bottom: 4.0 ) ,
child: Center (
child: Column (
mainAxisSize: MainAxisSize . min ,
crossAxisAlignment: CrossAxisAlignment . center ,
children: [
Text ( t . recent , style: const TextStyle ( fontFamily: " Eurostile Round Extended " , fontSize: 42 ) ) ,
] ,
) ,
) ,
) ,
) ,
Card (
clipBehavior: Clip . antiAlias ,
2024-08-12 23:07:59 +00:00
child: FutureBuilder < TetraLeagueBetaStream > (
2024-08-13 22:45:28 +00:00
future: teto . fetchTLStream ( widget . searchFor ) ,
2024-08-12 23:07:59 +00:00
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 ) {
2024-08-13 22:45:28 +00:00
return SizedBox ( height: constraints . maxHeight - 145 , child: _TLRecords ( userID: widget . searchFor , changePlayer: ( ) { } , data: snapshot . data ! . records , wasActiveInTL: snapshot . data ! . records . isNotEmpty , oldMathcesHere: false ) ) ;
2024-08-12 23:07:59 +00:00
}
2024-09-14 22:05:50 +00:00
if ( snapshot . hasError ) { return FutureError ( snapshot ) ; }
2024-08-12 23:07:59 +00:00
}
2024-08-31 23:00:26 +00:00
return const Text ( " what? " ) ;
2024-08-12 23:07:59 +00:00
} ,
) ,
) ,
] ,
) ;
}
2024-08-09 22:52:50 +00:00
Widget getZenithCard ( RecordSingle ? record ) {
return Column (
children: [
Card (
child: Padding (
padding: const EdgeInsets . only ( bottom: 4.0 ) ,
child: Center (
child: Column (
mainAxisSize: MainAxisSize . min ,
crossAxisAlignment: CrossAxisAlignment . center ,
children: [
Text ( t . quickPlay , style: const TextStyle ( fontFamily: " Eurostile Round Extended " , fontSize: 42 ) ) ,
2024-08-17 23:39:20 +00:00
//Text("Leaderboard reset in ${countdown(postSeasonLeft)}", textAlign: TextAlign.center),
2024-08-09 22:52:50 +00:00
] ,
) ,
) ,
) ,
) ,
ZenithThingy ( zenith: record ) ,
2024-08-13 22:45:28 +00:00
if ( record ! = null ) Row (
children: [
Expanded (
child: Card (
child: Column (
children: [
FinesseThingy ( record . stats . finesse , record . stats . finessePercentage ) ,
LineclearsThingy ( record . stats . clears , record . stats . lines , record . stats . holds , record . stats . tSpins , showMoreClears: true ) ,
if ( record . gamemode = = ' blitz ' ) Text ( " ${ f2 . format ( record . stats . kpp ) } KPP " )
] ,
) ,
) ,
) ,
Expanded (
child: Card (
child: SizedBox (
width: 300 ,
2024-08-31 23:00:26 +00:00
height: 318 ,
2024-08-13 22:45:28 +00:00
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 ) ,
2024-08-31 23:00:26 +00:00
child: Text ( getMoreNormalTime ( record . stats . finalTime ) , style: const TextStyle (
2024-08-13 22:45:28 +00:00
shadows: textShadow ,
fontFamily: " Eurostile Round Extended " ,
fontSize: 36 ,
fontWeight: FontWeight . w500 ,
color: Colors . white
) ) ,
)
] ,
) ,
SizedBox (
width: 300.0 ,
child: Table (
columnWidths: const {
0 : FixedColumnWidth ( 36 )
} ,
children: [
const TableRow (
children: [
Text ( " Floor " ) ,
Text ( " Split " , textAlign: TextAlign . right ) ,
Text ( " Total " , textAlign: TextAlign . right ) ,
]
) ,
2024-08-31 23:00:26 +00:00
for ( int i = 0 ; i < record . stats . zenith ! . splits . length ; i + + ) TableRow (
2024-08-13 22:45:28 +00:00
children: [
Text ( ( i + 1 ) . toString ( ) ) ,
2024-08-31 23:00:26 +00:00
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 ) ,
2024-08-13 22:45:28 +00:00
]
)
] ,
) ,
) ,
] ,
) ,
) ,
) ,
) ,
] ,
) ,
2024-08-09 22:52:50 +00:00
if ( record ! = null ) Card (
child: Row (
mainAxisSize: MainAxisSize . min ,
children: [
const Spacer ( ) ,
Text ( t . nerdStats , style: const TextStyle ( fontFamily: " Eurostile Round Extended " , fontSize: 42 ) ) ,
const Spacer ( )
] ,
) ,
) ,
if ( record ! = null ) NerdStatsThingy ( nerdStats: record . aggregateStats . nerdStats ) ,
if ( record ! = null ) GraphsThingy ( nerdStats: record . aggregateStats . nerdStats , playstyle: record . aggregateStats . playstyle , apm: record . aggregateStats . apm , pps: record . aggregateStats . pps , vs: record . aggregateStats . vs )
] ,
) ;
}
2024-08-12 23:07:59 +00:00
Widget getRecordCard ( RecordSingle ? record , bool ? betterThanRankAverage , MapEntry ? closestAverage , bool ? betterThanClosestAverage , String ? rank ) {
2024-08-13 22:45:28 +00:00
if ( record = = null ) {
2024-08-31 23:00:26 +00:00
return const Card (
child: Center ( child: Text ( " No record " , style: TextStyle ( fontSize: 42 ) ) ) ,
2024-08-13 22:45:28 +00:00
) ;
}
2024-08-09 22:52:50 +00:00
return Column (
children: [
2024-08-12 23:07:59 +00:00
Card (
child: Padding (
padding: const EdgeInsets . only ( bottom: 4.0 ) ,
child: Center (
child: Column (
mainAxisSize: MainAxisSize . min ,
crossAxisAlignment: CrossAxisAlignment . center ,
children: [
2024-08-13 22:45:28 +00:00
Text ( switch ( record . gamemode ) {
2024-08-12 23:07:59 +00:00
" 40l " = > t . sprint ,
" blitz " = > t . blitz ,
" 5mblast " = > " 5,000,000 Blast " ,
_ = > record . gamemode
} , style: const TextStyle ( fontFamily: " Eurostile Round Extended " , fontSize: 42 ) )
] ,
) ,
) ,
) ,
) ,
Card (
2024-08-13 22:45:28 +00:00
child: Column (
children: [
Row (
mainAxisSize: MainAxisSize . min ,
children: [
if ( closestAverage ! = null ) Padding ( padding: const EdgeInsets . only ( right: 8.0 ) ,
child: Image . asset ( " res/tetrio_tl_alpha_ranks/ ${ closestAverage . key } .png " , height: 96 )
) ,
Column (
crossAxisAlignment: CrossAxisAlignment . start ,
mainAxisSize: MainAxisSize . min ,
2024-08-12 23:07:59 +00:00
children: [
2024-08-13 22:45:28 +00:00
RichText ( text: TextSpan (
text: switch ( record . gamemode ) {
" 40l " = > get40lTime ( record . stats . finalTime . inMicroseconds ) ,
" blitz " = > NumberFormat . decimalPattern ( ) . format ( record . stats . score ) ,
" 5mblast " = > get40lTime ( record . stats . finalTime . inMicroseconds ) ,
_ = > record . stats . score . toString ( )
} ,
style: const TextStyle ( fontFamily: " Eurostile Round Extended " , fontSize: 36 , fontWeight: FontWeight . w500 , color: Colors . white ) ,
) ,
) ,
RichText ( text: TextSpan (
text: " " ,
style: const TextStyle ( fontFamily: " Eurostile Round " , fontSize: 14 , color: Colors . grey ) ,
children: [
2024-08-31 23:00:26 +00:00
if ( rank ! = null & & rank ! = " z " & & rank ! = " x+ " ) TextSpan ( text: " $ {t.verdictGeneral(n: switch(record.gamemode){
2024-08-13 22:45:28 +00:00
" 40l " = > readableTimeDifference ( record . stats . finalTime , sprintAverages [ rank ] ! ) ,
" blitz " = > readableIntDifference ( record . stats . score , blitzAverages [ rank ] ! ) ,
_ = > record . stats . score . toString ( )
} , verdict: betterThanRankAverage ? ? false ? t . verdictBetter : t . verdictWorse , rank: rank . toUpperCase ( ) ) } \ n " , style: TextStyle(
color: betterThanClosestAverage ? ? false ? Colors . greenAccent : Colors . redAccent
) )
2024-08-31 23:00:26 +00:00
else if ( ( rank = = null | | rank = = " z " | | rank = = " x+ " ) & & closestAverage ! = null ) TextSpan ( text: " $ {t.verdictGeneral(n: switch(record.gamemode){
2024-08-13 22:45:28 +00:00
" 40l " = > readableTimeDifference ( record . stats . finalTime , closestAverage . value ) ,
" blitz " = > readableIntDifference ( record . stats . score , closestAverage . value ) ,
_ = > record . stats . score . toString ( )
} , verdict: betterThanClosestAverage ? ? false ? t . verdictBetter : t . verdictWorse , rank: closestAverage . key . toUpperCase ( ) ) } \ n " , style: TextStyle(
color: betterThanClosestAverage ? ? false ? Colors . greenAccent : Colors . redAccent
) ) ,
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 ( record . timestamp ) ) ,
]
) ,
2024-08-12 23:07:59 +00:00
) ,
2024-08-13 22:45:28 +00:00
] ,
) ,
] ,
) ,
Row (
children: [
Expanded (
child: Table (
2024-08-12 23:07:59 +00:00
defaultColumnWidth: const IntrinsicColumnWidth ( ) ,
children: [
TableRow ( children: [
Text ( switch ( record . gamemode ) {
" 40l " = > record . stats . piecesPlaced . toString ( ) ,
" blitz " = > record . stats . level . toString ( ) ,
" 5mblast " = > NumberFormat . decimalPattern ( ) . format ( record . stats . spp ) ,
_ = > " What if "
2024-08-31 23:00:26 +00:00
} , textAlign: TextAlign . right , style: const TextStyle ( fontSize: 21 ) ) ,
2024-08-12 23:07:59 +00:00
Text ( switch ( record . gamemode ) {
" 40l " = > " Pieces " ,
" blitz " = > " Level " ,
" 5mblast " = > " SPP " ,
_ = > " i wanted to "
} , textAlign: TextAlign . left , style: const TextStyle ( fontSize: 21 ) ) ,
] ) ,
TableRow ( children: [
2024-08-31 23:00:26 +00:00
Text ( f2 . format ( record . stats . pps ) , textAlign: TextAlign . right , style: const TextStyle ( fontSize: 21 ) ) ,
const Text ( " PPS " , textAlign: TextAlign . left , style: TextStyle ( fontSize: 21 ) ) ,
2024-08-12 23:07:59 +00:00
] ) ,
TableRow ( children: [
Text ( switch ( record . gamemode ) {
" 40l " = > f2 . format ( record . stats . kpp ) ,
" blitz " = > f2 . format ( record . stats . spp ) ,
" 5mblast " = > record . stats . piecesPlaced . toString ( ) ,
_ = > " but god said "
2024-08-31 23:00:26 +00:00
} , textAlign: TextAlign . right , style: const TextStyle ( fontSize: 21 ) ) ,
2024-08-12 23:07:59 +00:00
Text ( switch ( record . gamemode ) {
" 40l " = > " KPP " ,
" blitz " = > " SPP " ,
" 5mblast " = > " Pieces " ,
_ = > " no "
} , textAlign: TextAlign . left , style: const TextStyle ( fontSize: 21 ) ) ,
] )
] ,
) ,
2024-08-13 22:45:28 +00:00
) ,
Expanded (
child: Table (
defaultColumnWidth: const IntrinsicColumnWidth ( ) ,
children: [
TableRow ( children: [
2024-08-31 23:00:26 +00:00
Text ( intf . format ( record . stats . inputs ) , textAlign: TextAlign . right , style: const TextStyle ( fontSize: 21 ) ) ,
const Text ( " Key presses " , textAlign: TextAlign . left , style: TextStyle ( fontSize: 21 ) ) ,
2024-08-13 22:45:28 +00:00
] ) ,
TableRow ( children: [
2024-08-31 23:00:26 +00:00
Text ( f2 . format ( record . stats . kps ) , textAlign: TextAlign . right , style: const TextStyle ( fontSize: 21 ) ) ,
const Text ( " KPS " , textAlign: TextAlign . left , style: TextStyle ( fontSize: 21 ) ) ,
2024-08-13 22:45:28 +00:00
] ) ,
TableRow ( children: [
Text ( switch ( record . gamemode ) {
" 40l " = > " " ,
" blitz " = > record . stats . piecesPlaced . toString ( ) ,
" 5mblast " = > record . stats . piecesPlaced . toString ( ) ,
_ = > " but god said "
2024-08-31 23:00:26 +00:00
} , textAlign: TextAlign . right , style: const TextStyle ( fontSize: 21 ) ) ,
2024-08-13 22:45:28 +00:00
Text ( switch ( record . gamemode ) {
" 40l " = > " " ,
" blitz " = > " Pieces " ,
" 5mblast " = > " Pieces " ,
_ = > " no "
} , textAlign: TextAlign . left , style: const TextStyle ( fontSize: 21 ) ) ,
] )
] ,
) ,
) ,
] ,
)
] ,
) ,
) ,
Card (
child: Center (
child: Column (
children: [
FinesseThingy ( record . stats . finesse , record . stats . finessePercentage ) ,
LineclearsThingy ( record . stats . clears , record . stats . lines , record . stats . holds , record . stats . tSpins ) ,
if ( record . gamemode = = ' blitz ' ) Text ( " ${ f2 . format ( record . stats . kpp ) } KPP " )
2024-08-12 23:07:59 +00:00
] ,
) ,
) ,
2024-08-13 22:45:28 +00:00
)
2024-08-09 22:52:50 +00:00
]
) ;
}
@ override
initState ( ) {
modeButtons = {
Cards . overview: [
const ButtonSegment < CardMod > (
value: CardMod . info ,
label: Text ( ' General ' ) ,
) ,
] ,
Cards . tetraLeague: [
2024-08-24 14:41:07 +00:00
const ButtonSegment < CardMod > (
value: CardMod . info ,
label: Text ( ' Standing ' ) ,
) ,
2024-09-05 21:42:21 +00:00
const ButtonSegment < CardMod > (
value: CardMod . ex , // yeah i misusing my own Enum shut the fuck up
label: Text ( ' Previous Seasons ' ) ,
) ,
2024-08-24 14:41:07 +00:00
const ButtonSegment < CardMod > (
value: CardMod . records ,
label: Text ( ' Recent Matches ' ) ,
) ,
2024-08-09 22:52:50 +00:00
] ,
Cards . quickPlay: [
const ButtonSegment < CardMod > (
value: CardMod . info ,
label: Text ( ' Normal ' ) ,
) ,
const ButtonSegment < CardMod > (
2024-08-24 14:41:07 +00:00
value: CardMod . records ,
label: Text ( ' Records ' ) ,
2024-08-09 22:52:50 +00:00
) ,
const ButtonSegment < CardMod > (
value: CardMod . ex ,
label: Text ( ' Expert ' ) ,
) ,
const ButtonSegment < CardMod > (
2024-08-24 14:41:07 +00:00
value: CardMod . exRecords ,
label: Text ( ' Expert Records ' ) ,
)
2024-08-09 22:52:50 +00:00
] ,
Cards . blitz: [
const ButtonSegment < CardMod > (
value: CardMod . info ,
label: Text ( ' PB ' ) ,
) ,
const ButtonSegment < CardMod > (
2024-08-24 14:41:07 +00:00
value: CardMod . records ,
label: Text ( ' Records ' ) ,
)
2024-08-09 22:52:50 +00:00
] ,
Cards . sprint: [
const ButtonSegment < CardMod > (
value: CardMod . info ,
label: Text ( ' PB ' ) ,
) ,
const ButtonSegment < CardMod > (
2024-08-24 14:41:07 +00:00
value: CardMod . records ,
label: Text ( ' Records ' ) ,
)
2024-08-09 22:52:50 +00:00
]
} ;
2024-09-10 21:22:17 +00:00
2024-09-11 22:41:02 +00:00
_transition = AnimationController ( vsync: this , duration: Durations . long4 ) ;
2024-09-10 21:22:17 +00:00
2024-09-11 22:41:02 +00:00
// _transition.addListener((){
// setState(() {
2024-09-10 21:22:17 +00:00
2024-09-11 22:41:02 +00:00
// });
// });
_offsetAnimation = Tween < Offset > (
begin: Offset . zero ,
end: const Offset ( 1.5 , 0.0 ) ,
) . animate ( CurvedAnimation (
parent: _transition ,
curve: Curves . elasticIn ,
) ) ;
2024-09-10 21:22:17 +00:00
2024-08-09 22:52:50 +00:00
super . initState ( ) ;
}
2024-08-08 23:01:46 +00:00
@ override
Widget build ( BuildContext context ) {
2024-08-15 21:55:45 +00:00
return FutureBuilder < FetchResults > (
2024-09-10 21:22:17 +00:00
future: _data ,
2024-08-15 21:55:45 +00:00
builder: ( context , snapshot ) {
switch ( snapshot . connectionState ) {
case ConnectionState . none:
case ConnectionState . waiting:
case ConnectionState . active:
return const Center ( child: CircularProgressIndicator ( ) ) ;
case ConnectionState . done:
2024-09-14 22:05:50 +00:00
if ( snapshot . hasError ) { return FutureError ( snapshot ) ; }
2024-08-15 21:55:45 +00:00
if ( snapshot . hasData ) {
2024-09-29 22:02:19 +00:00
if ( ! snapshot . data ! . success ) return FetchResultError ( snapshot . data ! ) ;
2024-08-24 14:41:07 +00:00
blitzBetterThanRankAverage = ( snapshot . data ! . summaries ! . league . rank ! = " z " & & snapshot . data ! . summaries ! . blitz ! = null & & snapshot . data ! . summaries ! . league . rank ! = " x+ " ) ? snapshot . data ! . summaries ! . blitz ! . stats . score > blitzAverages [ snapshot . data ! . summaries ! . league . rank ] ! : null ;
sprintBetterThanRankAverage = ( snapshot . data ! . summaries ! . league . rank ! = " z " & & snapshot . data ! . summaries ! . sprint ! = null & & snapshot . data ! . summaries ! . league . rank ! = " x+ " ) ? snapshot . data ! . summaries ! . sprint ! . stats . finalTime < sprintAverages [ snapshot . data ! . summaries ! . league . rank ] ! : null ;
2024-08-15 21:55:45 +00:00
if ( snapshot . data ! . summaries ! . sprint ! = null ) {
closestAverageSprint = sprintAverages . entries . singleWhere ( ( element ) = > element . value = = sprintAverages . values . reduce ( ( a , b ) = > ( a - snapshot . data ! . summaries ! . sprint ! . stats . finalTime ) . abs ( ) < ( b - snapshot . data ! . summaries ! . sprint ! . stats . finalTime ) . abs ( ) ? a : b ) ) ;
sprintBetterThanClosestAverage = snapshot . data ! . summaries ! . sprint ! . stats . finalTime < closestAverageSprint ! . value ;
}
if ( snapshot . data ! . summaries ! . blitz ! = null ) {
closestAverageBlitz = blitzAverages . entries . singleWhere ( ( element ) = > element . value = = blitzAverages . values . reduce ( ( a , b ) = > ( a - snapshot . data ! . summaries ! . blitz ! . stats . score ) . abs ( ) < ( b - snapshot . data ! . summaries ! . blitz ! . stats . score ) . abs ( ) ? a : b ) ) ;
blitzBetterThanClosestAverage = snapshot . data ! . summaries ! . blitz ! . stats . score > closestAverageBlitz ! . value ;
}
2024-09-10 21:22:17 +00:00
return TweenAnimationBuilder (
duration: Durations . long4 ,
tween: Tween < double > ( begin: 0 , end: 1 ) ,
2024-09-11 22:41:02 +00:00
curve: Easing . standard ,
2024-09-10 21:22:17 +00:00
builder: ( context , value , child ) {
return Container (
transform: Matrix4 . translationValues ( 0 , 600 - value * 600 , 0 ) ,
2024-09-11 22:41:02 +00:00
child: Opacity ( opacity: value , child: child ) ,
2024-09-10 21:22:17 +00:00
) ;
} ,
child: Row (
children: [
SizedBox (
width: 450 ,
child: Column (
children: [
NewUserThingy ( player: snapshot . data ! . player ! , showStateTimestamp: false , setState: setState ) ,
if ( snapshot . data ! . player ! . badges . isNotEmpty ) BadgesThingy ( badges: snapshot . data ! . player ! . badges ) ,
if ( snapshot . data ! . player ! . distinguishment ! = null ) DistinguishmentThingy ( snapshot . data ! . player ! . distinguishment ! ) ,
if ( snapshot . data ! . player ! . role = = " bot " ) FakeDistinguishmentThingy ( bot: true , botMaintainers: snapshot . data ! . player ! . botmaster ) ,
if ( snapshot . data ! . player ! . role = = " banned " ) FakeDistinguishmentThingy ( banned: true )
else if ( snapshot . data ! . player ! . badstanding = = true ) FakeDistinguishmentThingy ( badStanding: true ) ,
if ( snapshot . data ! . player ! . bio ! = null ) Card (
child: Column (
children: [
Row (
children: [
const Spacer ( ) ,
Text ( t . bio , style: const TextStyle ( fontFamily: " Eurostile Round Extended " ) ) ,
const Spacer ( )
] ,
) ,
Padding (
padding: const EdgeInsets . only ( bottom: 8.0 ) ,
child: MarkdownBody ( data: snapshot . data ! . player ! . bio ! , styleSheet: MarkdownStyleSheet ( textAlign: WrapAlignment . center ) ) ,
)
] ,
) ,
2024-08-08 23:01:46 +00:00
) ,
2024-09-10 21:22:17 +00:00
//if (testNews != null && testNews!.news.isNotEmpty)
Expanded (
child: FutureBuilder < News > (
future: _newsData ,
builder: ( context , snapshot ) {
switch ( snapshot . connectionState ) {
case ConnectionState . none:
case ConnectionState . waiting:
case ConnectionState . active:
return const Card ( child: Center ( child: CircularProgressIndicator ( ) ) ) ;
case ConnectionState . done:
if ( snapshot . hasData ) {
return NewsThingy ( snapshot . data ! ) ;
2024-09-14 22:05:50 +00:00
} else if ( snapshot . hasError ) { return FutureError ( snapshot ) ; }
2024-08-08 23:01:46 +00:00
}
2024-09-10 21:22:17 +00:00
return const Text ( " what? " ) ;
2024-08-08 23:01:46 +00:00
}
2024-09-10 21:22:17 +00:00
) ,
)
] ,
) ,
) ,
SizedBox (
width: widget . constraints . maxWidth - 450 - 80 ,
child: Column (
children: [
SizedBox (
height: rightCard ! = Cards . overview ? widget . constraints . maxHeight - 64 : widget . constraints . maxHeight - 32 ,
child: SingleChildScrollView (
2024-09-11 22:41:02 +00:00
child: SlideTransition (
position: _offsetAnimation ,
2024-09-10 21:22:17 +00:00
child: switch ( rightCard ) {
Cards . overview = > getOverviewCard ( snapshot . data ! . summaries ! ) ,
Cards . tetraLeague = > switch ( cardMod ) {
CardMod . info = > getTetraLeagueCard ( snapshot . data ! . summaries ! . league , snapshot . data ! . cutoffs , snapshot . data ! . states ) ,
CardMod . ex = > getPreviousSeasonsList ( snapshot . data ! . summaries ! . pastLeague ) ,
CardMod . records = > getRecentTLrecords ( widget . constraints ) ,
_ = > const Center ( child: Text ( " huh? " ) )
} ,
Cards . quickPlay = > switch ( cardMod ) {
CardMod . info = > getZenithCard ( snapshot . data ? . summaries ! . zenith ) ,
CardMod . records = > getListOfRecords ( " zenith/recent " , " zenith/top " , widget . constraints ) ,
CardMod . ex = > getZenithCard ( snapshot . data ? . summaries ! . zenithEx ) ,
CardMod . exRecords = > getListOfRecords ( " zenithex/recent " , " zenithex/top " , widget . constraints ) ,
} ,
Cards . sprint = > switch ( cardMod ) {
CardMod . info = > getRecordCard ( snapshot . data ? . summaries ! . sprint , sprintBetterThanRankAverage , closestAverageSprint , sprintBetterThanClosestAverage , snapshot . data ! . summaries ! . league . rank ) ,
CardMod . records = > getListOfRecords ( " 40l/recent " , " 40l/top " , widget . constraints ) ,
_ = > const Center ( child: Text ( " huh? " ) )
} ,
Cards . blitz = > switch ( cardMod ) {
CardMod . info = > getRecordCard ( snapshot . data ? . summaries ! . blitz , blitzBetterThanRankAverage , closestAverageBlitz , blitzBetterThanClosestAverage , snapshot . data ! . summaries ! . league . rank ) ,
CardMod . records = > getListOfRecords ( " blitz/recent " , " blitz/top " , widget . constraints ) ,
_ = > const Center ( child: Text ( " huh? " ) )
} ,
} ,
) ,
) ,
2024-08-08 23:01:46 +00:00
) ,
2024-09-10 21:22:17 +00:00
if ( modeButtons [ rightCard ] ! . length > 1 ) SegmentedButton < CardMod > (
showSelectedIcon: false ,
selected: < CardMod > { cardMod } ,
segments: modeButtons [ rightCard ] ! ,
onSelectionChanged: ( p0 ) {
setState ( ( ) {
cardMod = p0 . first ;
//_transition.;
} ) ;
2024-08-15 21:55:45 +00:00
} ,
) ,
2024-09-10 21:22:17 +00:00
SegmentedButton < Cards > (
showSelectedIcon: false ,
segments: < ButtonSegment < Cards > > [
const ButtonSegment < Cards > (
value: Cards . overview ,
//label: Text('Overview'),
icon: Icon ( Icons . calendar_view_day ) ) ,
ButtonSegment < Cards > (
value: Cards . tetraLeague ,
//label: Text('Tetra League'),
icon: SvgPicture . asset ( " res/icons/league.svg " , height: 16 , colorFilter: ColorFilter . mode ( theme . colorScheme . primary , BlendMode . modulate ) ) ) ,
ButtonSegment < Cards > (
value: Cards . quickPlay ,
//label: Text('Quick Play'),
icon: SvgPicture . asset ( " res/icons/qp.svg " , height: 16 , colorFilter: ColorFilter . mode ( theme . colorScheme . primary , BlendMode . modulate ) ) ) ,
ButtonSegment < Cards > (
value: Cards . sprint ,
//label: Text('40 Lines'),
icon: SvgPicture . asset ( " res/icons/40l.svg " , height: 16 , colorFilter: ColorFilter . mode ( theme . colorScheme . primary , BlendMode . modulate ) ) ) ,
ButtonSegment < Cards > (
value: Cards . blitz ,
//label: Text('Blitz'),
icon: SvgPicture . asset ( " res/icons/blitz.svg " , height: 16 , colorFilter: ColorFilter . mode ( theme . colorScheme . primary , BlendMode . modulate ) ) ) ,
] ,
selected: < Cards > { rightCard } ,
onSelectionChanged: ( Set < Cards > newSelection ) {
setState ( ( ) {
cardMod = CardMod . info ;
rightCard = newSelection . first ;
} ) ; } )
] ,
)
2024-08-15 21:55:45 +00:00
)
2024-09-10 21:22:17 +00:00
] ,
) ,
2024-08-15 21:55:45 +00:00
) ;
}
}
2024-08-31 23:00:26 +00:00
return const Text ( " End of FutureBuilder<FetchResults> " ) ;
2024-08-15 21:55:45 +00:00
} ,
) ;
2024-07-27 19:10:45 +00:00
}
}
class NewsThingy extends StatelessWidget {
final News news ;
2024-08-03 17:52:20 +00:00
const NewsThingy ( this . news , { super . key } ) ;
2024-07-27 19:10:45 +00:00
ListTile getNewsTile ( NewsEntry news ) {
Map < String , String > gametypes = {
" 40l " : t . sprint ,
" blitz " : t . blitz ,
2024-08-04 22:23:08 +00:00
" 5mblast " : " 5,000,000 Blast " ,
" zenith " : " Quick Play " ,
" zenithex " : " Quick Play Expert " ,
2024-07-27 19:10:45 +00:00
} ;
// Individuly handle each entry type
switch ( news . type ) {
case " leaderboard " :
return ListTile (
title: RichText (
text: TextSpan (
style: const TextStyle ( fontFamily: ' Eurostile Round ' , fontSize: 16 , color: Colors . white ) ,
text: t . newsParts . leaderboardStart ,
children: [
TextSpan ( text: " № ${ news . data [ " rank " ] } " , style: const TextStyle ( fontWeight: FontWeight . bold ) ) ,
TextSpan ( text: t . newsParts . leaderboardMiddle ) ,
TextSpan ( text: " № ${ gametypes [ news . data [ " gametype " ] ] } " , style: const TextStyle ( fontWeight: FontWeight . bold ) ) ,
]
)
) ,
subtitle: Text ( timestamp ( news . timestamp ) ) ,
) ;
case " personalbest " :
return ListTile (
title: RichText (
text: TextSpan (
style: const TextStyle ( fontFamily: ' Eurostile Round ' , fontSize: 16 , color: Colors . white ) ,
text: t . newsParts . personalbest ,
children: [
TextSpan ( text: " ${ gametypes [ news . data [ " gametype " ] ] } " , style: const TextStyle ( fontWeight: FontWeight . bold ) ) ,
TextSpan ( text: t . newsParts . personalbestMiddle ) ,
2024-08-04 22:23:08 +00:00
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 )
) ,
2024-07-27 19:10:45 +00:00
]
)
) ,
subtitle: Text ( timestamp ( news . timestamp ) ) ,
leading: Image . asset (
" res/icons/improvement-local.png " ,
height: 48 ,
width: 48 ,
errorBuilder: ( context , error , stackTrace ) {
return Image . asset ( " res/icons/kagari.png " , height: 64 , width: 64 ) ;
} ,
) ,
) ;
case " badge " :
return ListTile (
title: RichText (
text: TextSpan (
style: const TextStyle ( fontFamily: ' Eurostile Round ' , fontSize: 16 , color: Colors . white ) ,
text: t . newsParts . badgeStart ,
children: [
TextSpan ( text: " ${ news . data [ " label " ] } " , style: const TextStyle ( fontWeight: FontWeight . bold ) ) ,
TextSpan ( text: t . newsParts . badgeEnd )
]
)
) ,
subtitle: Text ( timestamp ( news . timestamp ) ) ,
leading: Image . asset (
" res/tetrio_badges/ ${ news . data [ " type " ] } .png " ,
height: 48 ,
width: 48 ,
errorBuilder: ( context , error , stackTrace ) {
return Image . asset ( " res/icons/kagari.png " , height: 64 , width: 64 ) ;
} ,
) ,
) ;
case " rankup " :
return ListTile (
title: RichText (
text: TextSpan (
style: const TextStyle ( fontFamily: ' Eurostile Round ' , fontSize: 16 , color: Colors . white ) ,
text: t . newsParts . rankupStart ,
children: [
TextSpan ( text: t . newsParts . rankupMiddle ( r: news . data [ " rank " ] . toString ( ) . toUpperCase ( ) ) , style: const TextStyle ( fontWeight: FontWeight . bold ) ) ,
TextSpan ( text: t . newsParts . rankupEnd )
]
)
) ,
subtitle: Text ( timestamp ( news . timestamp ) ) ,
leading: Image . asset (
" res/tetrio_tl_alpha_ranks/ ${ news . data [ " rank " ] } .png " ,
height: 48 ,
width: 48 ,
errorBuilder: ( context , error , stackTrace ) {
return Image . asset ( " res/icons/kagari.png " , height: 64 , width: 64 ) ;
} ,
) ,
) ;
case " supporter " :
return ListTile (
title: RichText (
text: TextSpan (
style: const TextStyle ( fontFamily: ' Eurostile Round ' , fontSize: 16 , color: Colors . white ) ,
text: t . newsParts . supporterStart ,
children: [
TextSpan ( text: t . newsParts . tetoSupporter , style: const TextStyle ( fontWeight: FontWeight . bold ) )
]
)
) ,
subtitle: Text ( timestamp ( news . timestamp ) ) ,
leading: Image . asset (
" res/icons/supporter-tag.png " ,
height: 48 ,
width: 48 ,
errorBuilder: ( context , error , stackTrace ) {
return Image . asset ( " res/icons/kagari.png " , height: 64 , width: 64 ) ;
} ,
) ,
) ;
case " supporter_gift " :
return ListTile (
title: RichText (
text: TextSpan (
style: const TextStyle ( fontFamily: ' Eurostile Round ' , fontSize: 16 , color: Colors . white ) ,
text: t . newsParts . supporterGiftStart ,
children: [
TextSpan ( text: t . newsParts . tetoSupporter , style: const TextStyle ( fontWeight: FontWeight . bold ) )
]
)
) ,
subtitle: Text ( timestamp ( news . timestamp ) ) ,
leading: Image . asset (
" res/icons/supporter-tag.png " ,
height: 48 ,
width: 48 ,
errorBuilder: ( context , error , stackTrace ) {
return Image . asset ( " res/icons/kagari.png " , height: 64 , width: 64 ) ;
} ,
) ,
) ;
default : // if type is unknown
return ListTile (
title: Text ( t . newsParts . unknownNews ( type: news . type ) ) ,
subtitle: Text ( timestamp ( news . timestamp ) ) ,
) ;
}
}
@ override
Widget build ( BuildContext context ) {
return Card (
child: SingleChildScrollView (
child: Column (
children: [
Row (
children: [
2024-08-03 17:52:20 +00:00
const Spacer ( ) ,
Text ( t . news , style: const TextStyle ( fontFamily: " Eurostile Round Extended " ) ) ,
const Spacer ( )
2024-07-27 19:10:45 +00:00
]
) ,
2024-08-09 22:52:50 +00:00
if ( news . news . isEmpty ) const Center ( child: Text ( " Empty list " ) )
2024-08-04 22:23:08 +00:00
else for ( NewsEntry entry in news . news ) getNewsTile ( entry )
2024-07-27 19:10:45 +00:00
] ,
) ,
) ,
) ;
}
}
class DistinguishmentThingy extends StatelessWidget {
final Distinguishment distinguishment ;
2024-08-03 17:52:20 +00:00
const DistinguishmentThingy ( this . distinguishment , { super . key } ) ;
2024-07-27 19:10:45 +00:00
List < InlineSpan > getDistinguishmentTitle ( String ? text ) {
// TWC champions don't have header in their distinguishments
if ( distinguishment . type = = " twc " ) return [ const TextSpan ( text: " TETR.IO World Champion " , style: TextStyle ( fontSize: 28 , fontWeight: FontWeight . bold , color: Colors . yellowAccent ) ) ] ;
// In case if it missing for some other reason, return this
if ( text = = null ) return [ const TextSpan ( text: " Header is missing " , style: TextStyle ( fontSize: 28 , fontWeight: FontWeight . bold , color: Colors . redAccent ) ) ] ;
// Handling placeholders for logos
var exploded = text . split ( " " ) ; // wtf PHP reference?
List < InlineSpan > result = [ ] ;
for ( String shit in exploded ) {
switch ( shit ) { // if %% thingy was found, insert svg of icon
case " %osk% " :
result . add ( WidgetSpan ( child: Padding (
padding: const EdgeInsets . only ( left: 8 ) ,
child: SvgPicture . asset ( " res/icons/osk.svg " , height: 28 ) ,
) ) ) ;
break ;
case " %tetrio% " :
result . add ( WidgetSpan ( child: Padding (
padding: const EdgeInsets . only ( left: 8 ) ,
child: SvgPicture . asset ( " res/icons/tetrio-logo.svg " , height: 28 ) ,
) ) ) ;
break ;
default : // if not, insert text span
result . add ( TextSpan ( text: " $ shit " , style: const TextStyle ( fontSize: 28 , fontWeight: FontWeight . bold , color: Colors . white ) ) ) ;
}
}
return result ;
}
/// Distinguishment title is barely predictable thing.
/// Receives [text], which is footer and returns sets of widgets for RichText widget
String getDistinguishmentSubtitle ( String ? text ) {
// TWC champions don't have footer in their distinguishments
if ( distinguishment . type = = " twc " ) return " ${ distinguishment . detail } TETR.IO World Championship " ;
// In case if it missing for some other reason, return this
if ( text = = null ) return " Footer is missing " ;
// If everything ok, return as it is
return text ;
}
Color getCardTint ( String type , String detail ) {
switch ( type ) {
case " staff " :
switch ( detail ) {
2024-08-03 17:52:20 +00:00
case " founder " : return const Color ( 0xAAFD82D4 ) ;
case " kagarin " : return const Color ( 0xAAFF0060 ) ;
case " team " : return const Color ( 0xAAFACC2E ) ;
case " team-minor " : return const Color ( 0xAAF5BD45 ) ;
case " administrator " : return const Color ( 0xAAFF4E8A ) ;
case " globalmod " : return const Color ( 0xAAE878FF ) ;
case " communitymod " : return const Color ( 0xAA4E68FB ) ;
case " alumni " : return const Color ( 0xAA6057DB ) ;
2024-07-27 19:10:45 +00:00
default : return theme . colorScheme . surface ;
}
case " champion " :
switch ( detail ) {
case " blitz " :
2024-08-03 17:52:20 +00:00
case " 40l " : return const Color ( 0xAACCF5F6 ) ;
case " league " : return const Color ( 0xAAFFDB31 ) ;
2024-07-27 19:10:45 +00:00
}
2024-08-03 17:52:20 +00:00
case " twc " : return const Color ( 0xAAFFDB31 ) ;
2024-07-27 19:10:45 +00:00
default : return theme . colorScheme . surface ;
}
return theme . colorScheme . surface ;
}
@ override
Widget build ( BuildContext context ) {
return Card (
surfaceTintColor: getCardTint ( distinguishment . type , distinguishment . detail ? ? " null " ) ,
child: Column (
children: [
Row (
children: [
2024-08-03 17:52:20 +00:00
const Spacer ( ) ,
Text ( t . distinguishment , style: const TextStyle ( fontFamily: " Eurostile Round Extended " ) ) ,
const Spacer ( )
2024-07-27 19:10:45 +00:00
] ,
) ,
RichText (
textAlign: TextAlign . center ,
text: TextSpan (
style: DefaultTextStyle . of ( context ) . style ,
children: getDistinguishmentTitle ( distinguishment . header ) ,
) ,
) ,
Text ( getDistinguishmentSubtitle ( distinguishment . footer ) , style: const TextStyle ( fontSize: 18 ) , textAlign: TextAlign . center ) ,
] ,
) ,
) ;
}
}
2024-08-09 22:52:50 +00:00
class FakeDistinguishmentThingy extends StatelessWidget {
final bool banned ;
final bool badStanding ;
final bool bot ;
final String ? botMaintainers ;
FakeDistinguishmentThingy ( { super . key , this . banned = false , this . badStanding = false , this . bot = false , this . botMaintainers } ) ;
Color getCardTint ( ) {
if ( banned ) return Colors . red ;
if ( badStanding ) return Colors . redAccent ;
2024-08-31 23:00:26 +00:00
if ( bot ) return const Color . fromARGB ( 255 , 60 , 93 , 55 ) ;
2024-08-09 22:52:50 +00:00
return theme . colorScheme . surface ;
}
InlineSpan getDistinguishmentTitle ( ) {
String text = " " ;
if ( banned ) text = " banned " ;
if ( badStanding ) text = " bad standing " ;
if ( bot ) text = " bot account " ;
return TextSpan ( text: text . toUpperCase ( ) , style: const TextStyle ( fontSize: 28 , fontWeight: FontWeight . bold , color: Colors . white ) ) ;
}
String getDistinguishmentSubtitle ( ) {
if ( banned ) return " Bans are placed when TETR.IO rules or terms of service are broken " ;
if ( badStanding ) return " One or more recent bans on record " ;
if ( bot ) return " Operated by $ botMaintainers " ;
return " " ;
}
@ override
Widget build ( BuildContext context ) {
return Card (
surfaceTintColor: getCardTint ( ) ,
2024-08-12 23:07:59 +00:00
child: Container (
2024-08-31 23:00:26 +00:00
decoration: banned ? const BoxDecoration (
2024-08-12 23:07:59 +00:00
gradient: LinearGradient (
2024-08-31 23:00:26 +00:00
colors: [ Colors . transparent , Color . fromARGB ( 171 , 244 , 67 , 54 ) , Color . fromARGB ( 171 , 244 , 67 , 54 ) ] ,
2024-08-12 23:07:59 +00:00
stops: [ 0.1 , 0.9 , 0.01 ] ,
tileMode: TileMode . mirror ,
begin: Alignment . topLeft ,
end: AlignmentDirectional ( - 0.95 , - 0.95 )
)
) : null ,
child: Column (
children: [
Center (
child: RichText (
textAlign: TextAlign . center ,
text: TextSpan (
style: DefaultTextStyle . of ( context ) . style ,
children: [ getDistinguishmentTitle ( ) ] ,
) ,
2024-08-09 22:52:50 +00:00
) ,
) ,
2024-08-12 23:07:59 +00:00
Text ( getDistinguishmentSubtitle ( ) , style: const TextStyle ( fontSize: 18 ) , textAlign: TextAlign . center ) ,
] ,
) ,
2024-08-09 22:52:50 +00:00
) ,
) ;
}
}
2024-08-04 22:23:08 +00:00
class BadgesThingy extends StatelessWidget {
final List < Badge > badges ;
const BadgesThingy ( { super . key , required this . badges } ) ;
@ override
Widget build ( BuildContext context ) {
return Card (
child: Column (
children: [
Padding (
padding: const EdgeInsets . fromLTRB ( 20.0 , 0.0 , 20.0 , 0.0 ) ,
child: Row (
children: [
const Text ( " Badges " , style: TextStyle ( fontFamily: " Eurostile Round Extended " ) ) ,
const Spacer ( ) ,
Text ( intf . format ( badges . length ) )
] ,
) ,
) ,
SingleChildScrollView (
scrollDirection: Axis . horizontal ,
child: Row (
children: [
for ( var badge in badges )
IconButton (
onPressed: ( ) = > showDialog < void > (
context: context ,
builder: ( BuildContext context ) {
return AlertDialog (
title: Text ( badge . label , style: const TextStyle ( fontFamily: " Eurostile Round Extended " ) ) ,
content: SingleChildScrollView (
child: ListBody (
children: [
Wrap (
direction: Axis . horizontal ,
alignment: WrapAlignment . center ,
crossAxisAlignment: WrapCrossAlignment . center ,
spacing: 25 ,
children: [
Image . asset ( " res/tetrio_badges/ ${ badge . badgeId } .png " ) ,
Text ( badge . ts ! = null
? t . obtainDate ( date: timestamp ( badge . ts ! ) )
: t . assignedManualy ) ,
] ,
)
] ,
) ,
) ,
actions: < Widget > [
TextButton (
child: Text ( t . popupActions . ok ) ,
onPressed: ( ) {
Navigator . of ( context ) . pop ( ) ;
} ,
) ,
] ,
) ;
} ,
) ,
tooltip: badge . label ,
icon: Image . asset (
" res/tetrio_badges/ ${ badge . badgeId } .png " ,
height: 32 ,
errorBuilder: ( context , error , stackTrace ) {
return Image . network (
kIsWeb ? " https://ts.dan63.by/oskware_bridge.php?endpoint=TetrioBadge&badge= ${ badge . badgeId } " : " https://tetr.io/res/badges/ ${ badge . badgeId } .png " ,
height: 32 ,
errorBuilder: ( context , error , stackTrace ) {
return Image . asset ( " res/icons/kagari.png " , height: 32 , width: 32 ) ;
}
) ;
} ,
)
)
] ,
) ,
)
] ,
) ,
) ;
}
}
2024-07-27 19:10:45 +00:00
class NewUserThingy extends StatelessWidget {
final TetrioPlayer player ;
final bool showStateTimestamp ;
final Function setState ;
const NewUserThingy ( { super . key , required this . player , required this . showStateTimestamp , required this . setState } ) ;
Color roleColor ( String role ) {
switch ( role ) {
case " sysop " :
2024-08-03 17:52:20 +00:00
return const Color . fromARGB ( 255 , 23 , 165 , 133 ) ;
2024-07-27 19:10:45 +00:00
case " admin " :
2024-08-03 17:52:20 +00:00
return const Color . fromARGB ( 255 , 255 , 78 , 138 ) ;
2024-07-27 19:10:45 +00:00
case " mod " :
2024-08-03 17:52:20 +00:00
return const Color . fromARGB ( 255 , 204 , 128 , 242 ) ;
2024-07-27 19:10:45 +00:00
case " halfmod " :
2024-08-03 17:52:20 +00:00
return const Color . fromARGB ( 255 , 95 , 118 , 254 ) ;
2024-07-27 19:10:45 +00:00
case " bot " :
2024-08-03 17:52:20 +00:00
return const Color . fromARGB ( 255 , 60 , 93 , 55 ) ;
2024-07-27 19:10:45 +00:00
case " banned " :
2024-08-03 17:52:20 +00:00
return const Color . fromARGB ( 255 , 248 , 28 , 28 ) ;
2024-07-27 19:10:45 +00:00
default :
return Colors . white10 ;
}
}
2024-08-04 22:23:08 +00:00
String fontStyle ( int length ) {
if ( length < 10 ) return " Eurostile Round Extended " ;
else if ( length < 13 ) return " Eurostile Round " ;
else return " Eurostile Round Condensed " ;
}
2024-07-27 19:10:45 +00:00
@ override
Widget build ( BuildContext context ) {
final t = Translations . of ( context ) ;
return LayoutBuilder ( builder: ( context , constraints ) {
double pfpHeight = 128 ;
int xpTableID = 0 ;
while ( player . xp > xpTableScuffed . values . toList ( ) [ xpTableID ] ) {
xpTableID + + ;
}
return Card (
clipBehavior: Clip . antiAlias ,
2024-08-04 22:23:08 +00:00
child: Column (
children: [
Padding (
padding: const EdgeInsets . only ( bottom: 4.0 ) ,
child: Container (
2024-08-03 17:52:20 +00:00
constraints: const BoxConstraints ( maxWidth: 960 ) ,
2024-07-27 19:10:45 +00:00
height: player . bannerRevision ! = null ? 218.0 : 138.0 ,
child: Stack (
//clipBehavior: Clip.none,
children: [
2024-08-31 23:00:26 +00:00
// TODO: osk banner can cause memory leak
2024-09-11 22:41:02 +00:00
if ( player . bannerRevision ! = null ) FadeInImage . memoryNetwork ( image: kIsWeb ? " https://ts.dan63.by/oskware_bridge.php?endpoint=TetrioBanner&user= ${ player . userId } &rv= ${ player . bannerRevision } " : " https://tetr.io/user-content/banners/ ${ player . userId } .jpg?rv= ${ player . bannerRevision } " ,
placeholder: kTransparentImage ,
2024-07-27 19:10:45 +00:00
fit: BoxFit . cover ,
height: 120 ,
2024-09-11 22:41:02 +00:00
fadeInCurve: Easing . standard , fadeInDuration: Durations . long4
2024-07-27 19:10:45 +00:00
) ,
Positioned (
top: player . bannerRevision ! = null ? 90.0 : 10.0 ,
left: 16.0 ,
child: ClipRRect (
borderRadius: BorderRadius . circular ( 1000 ) ,
child: player . role = = " banned "
? Image . asset ( " res/avatars/tetrio_banned.png " , fit: BoxFit . fitHeight , height: pfpHeight , )
: player . avatarRevision ! = null
2024-09-11 22:41:02 +00:00
? FadeInImage . memoryNetwork ( image: kIsWeb ? " https://ts.dan63.by/oskware_bridge.php?endpoint=TetrioProfilePicture&user= ${ player . userId } &rv= ${ player . avatarRevision } " : " https://tetr.io/user-content/avatars/ ${ player . userId } .jpg?rv= ${ player . avatarRevision } " ,
fit: BoxFit . fitHeight , height: 128 , placeholder: kTransparentImage , fadeInCurve: Easing . emphasizedDecelerate , fadeInDuration: Durations . long4 )
2024-07-27 19:10:45 +00:00
: Image . asset ( " res/avatars/tetrio_anon.png " , fit: BoxFit . fitHeight , height: pfpHeight ) ,
)
) ,
Positioned (
top: player . bannerRevision ! = null ? 120.0 : 40.0 ,
left: 160.0 ,
2024-08-31 23:00:26 +00:00
child: Tooltip (
message: " ${ player . userId } \n (Click to copy user ID) " ,
child: RichText ( text: TextSpan ( text: player . username , style: TextStyle (
fontFamily: fontStyle ( player . username . length ) ,
fontSize: 28 ,
) ,
recognizer: TapGestureRecognizer ( ) . . onTap = ( ) {
copyToClipboard ( player . userId ) ;
ScaffoldMessenger . of ( context ) . showSnackBar ( SnackBar ( content: Text ( t . copiedToClipboard ) ) ) ;
}
)
)
2024-07-27 19:10:45 +00:00
) ,
) ,
Positioned (
top: player . bannerRevision ! = null ? 160.0 : 80.0 ,
left: 160.0 ,
child: Row (
children: [
Padding (
padding: const EdgeInsets . only ( right: 4.0 ) ,
2024-08-03 17:52:20 +00:00
child: Chip ( label: Text ( player . role . toUpperCase ( ) , style: const TextStyle ( shadows: textShadow ) , ) , padding: const EdgeInsets . all ( 0.0 ) , color: WidgetStatePropertyAll ( roleColor ( player . role ) ) ) ,
2024-07-27 19:10:45 +00:00
) ,
RichText (
text: TextSpan (
2024-08-03 17:52:20 +00:00
style: const TextStyle ( fontFamily: " Eurostile Round " ) ,
2024-07-27 19:10:45 +00:00
children:
[
2024-08-03 17:52:20 +00:00
if ( player . friendCount > 0 ) const WidgetSpan ( child: Icon ( Icons . person ) , alignment: PlaceholderAlignment . middle , baseline: TextBaseline . alphabetic ) ,
2024-07-27 19:10:45 +00:00
if ( player . friendCount > 0 ) TextSpan ( text: " ${ intf . format ( player . friendCount ) } " ) ,
if ( player . supporterTier > 0 ) WidgetSpan ( child: Icon ( player . supporterTier > 1 ? Icons . star : Icons . star_border , color: player . supporterTier > 1 ? Colors . yellowAccent : Colors . white ) , alignment: PlaceholderAlignment . middle , baseline: TextBaseline . alphabetic ) ,
if ( player . supporterTier > 0 ) TextSpan ( text: player . supporterTier . toString ( ) , style: TextStyle ( color: player . supporterTier > 1 ? Colors . yellowAccent : Colors . white ) ) ,
]
)
)
] ,
) ,
) ,
Positioned (
top: player . bannerRevision ! = null ? 193.0 : 113.0 ,
left: 160.0 ,
2024-08-31 23:00:26 +00:00
child: SizedBox (
width: 270 ,
child: RichText (
text: TextSpan (
style: const TextStyle ( fontFamily: " Eurostile Round " ) ,
children: [
if ( player . country ! = null ) TextSpan ( text: " ${ t . countries [ player . country ] } • " ) ,
TextSpan ( text: player . registrationTime = = null ? t . wasFromBeginning : timestamp ( player . registrationTime ! ) , style: const TextStyle ( color: Colors . grey ) )
]
)
) ,
2024-07-27 19:10:45 +00:00
)
) ,
Positioned (
top: player . bannerRevision ! = null ? 126.0 : 46.0 ,
right: 16.0 ,
child: RichText (
textAlign: TextAlign . end ,
text: TextSpan (
2024-08-03 17:52:20 +00:00
style: const TextStyle ( fontFamily: " Eurostile Round " ) ,
2024-07-27 19:10:45 +00:00
children: [
2024-08-31 23:00:26 +00:00
TextSpan ( text: " Level ${ ( player . level . isNegative | | player . level . isNaN ) ? " --- " : intf . format ( player . level . floor ( ) ) } " , style: TextStyle ( decoration: ( player . level . isNegative | | player . level . isNaN ) ? null : TextDecoration . underline , decorationColor: Colors . white70 , decorationStyle: TextDecorationStyle . dotted , color: ( player . level . isNegative | | player . level . isNaN ) ? Colors . grey : Colors . white ) , recognizer: ( player . level . isNegative | | player . level . isNaN ) ? null : TapGestureRecognizer ( ) ? . . onTap = ( ) {
2024-07-27 19:10:45 +00:00
showDialog (
context: context ,
builder: ( BuildContext context ) = > AlertDialog (
2024-08-06 22:24:31 +00:00
title: Text ( " Level ${ intf . format ( player . level . floor ( ) ) } " , textAlign: TextAlign . center ) ,
2024-07-27 19:10:45 +00:00
content: SingleChildScrollView (
child: ListBody ( children: [
Text (
" ${ NumberFormat . decimalPatternDigits ( locale: LocaleSettings . currentLocale . languageCode , decimalDigits: 2 ) . format ( player . xp ) } XP " ,
style: const TextStyle ( fontFamily: " Eurostile Round " , fontWeight: FontWeight . bold )
) ,
Padding (
padding: const EdgeInsets . fromLTRB ( 0 , 8 , 0 , 8 ) ,
child: SfLinearGauge (
minimum: 0 ,
maximum: 1 ,
interval: 1 ,
ranges: [
LinearGaugeRange ( startValue: 0 , endValue: player . level - player . level . floor ( ) , color: Colors . cyanAccent ) ,
LinearGaugeRange ( startValue: 0 , endValue: ( player . xp / xpTableScuffed . values . toList ( ) [ xpTableID ] ) , color: Colors . redAccent , position: LinearElementPosition . cross )
] ,
showTicks: true ,
showLabels: false
) ,
) ,
Text ( " ${ t . statCellNum . xpProgress } : ${ ( ( player . level - player . level . floor ( ) ) * 100 ) . toStringAsFixed ( 2 ) } % " ) ,
Text ( " ${ t . statCellNum . xpFrom0ToLevel ( n: xpTableScuffed . keys . toList ( ) [ xpTableID ] ) } : ${ ( ( player . xp / xpTableScuffed . values . toList ( ) [ xpTableID ] ) * 100 ) . toStringAsFixed ( 2 ) } % ( ${ NumberFormat . decimalPatternDigits ( locale: LocaleSettings . currentLocale . languageCode , decimalDigits: 0 ) . format ( xpTableScuffed . values . toList ( ) [ xpTableID ] - player . xp ) } ${ t . statCellNum . xpLeft } ) " )
]
) ,
) ,
actions: < Widget > [
TextButton (
2024-08-03 17:52:20 +00:00
child: const Text ( " OK " ) ,
2024-07-27 19:10:45 +00:00
onPressed: ( ) { Navigator . of ( context ) . pop ( ) ; }
)
]
)
) ;
} ) ,
2024-08-03 17:52:20 +00:00
const TextSpan ( text: " \n " ) ,
2024-08-06 22:24:31 +00:00
TextSpan ( text: player . gameTime . isNegative ? " -h --m " : playtime ( player . gameTime ) , style: TextStyle ( color: player . gameTime . isNegative ? Colors . grey : Colors . white , decoration: player . gameTime . isNegative ? null : TextDecoration . underline , decorationColor: Colors . white70 , decorationStyle: TextDecorationStyle . dotted ) , recognizer: ! player . gameTime . isNegative ? ( TapGestureRecognizer ( ) . . onTap = ( ) {
2024-07-27 19:10:45 +00:00
showDialog (
context: context ,
builder: ( BuildContext context ) = > AlertDialog (
2024-08-06 22:24:31 +00:00
title: Text ( t . exactGametime , textAlign: TextAlign . center ) ,
2024-07-27 19:10:45 +00:00
content: SingleChildScrollView (
child: ListBody ( children: [
Text (
2024-08-04 22:23:08 +00:00
" ${ intf . format ( player . gameTime . inDays ) } d ${ nonsecs . format ( player . gameTime . inHours % 24 ) } h ${ nonsecs . format ( player . gameTime . inMinutes % 60 ) } m ${ nonsecs . format ( player . gameTime . inSeconds % 60 ) } s ${ nonsecs3 . format ( player . gameTime . inMilliseconds % 1000 ) } ms ${ nonsecs3 . format ( player . gameTime . inMicroseconds % 1000 ) } μs " ,
2024-07-27 19:10:45 +00:00
style: const TextStyle ( fontFamily: " Eurostile Round " , fontSize: 24 )
) ,
2024-08-06 22:24:31 +00:00
Padding (
padding: const EdgeInsets . only ( top: 8.0 ) ,
child: Text ( " It's ${ f4 . format ( player . gameTime . inSeconds / 31536000 ) } years, " ) ,
) ,
Text ( " ${ f4 . format ( player . gameTime . inSeconds / 2628000 ) } monts, " ) ,
Text ( " ${ f4 . format ( player . gameTime . inSeconds / 3600 ) } hours, " ) ,
Text ( " ${ f2 . format ( player . gameTime . inMilliseconds / 60000 ) } minutes, " ) ,
Text ( " ${ intf . format ( player . gameTime . inSeconds ) } seconds " ) ,
2024-07-27 19:10:45 +00:00
]
) ,
) ,
actions: < Widget > [
TextButton (
2024-08-03 17:52:20 +00:00
child: const Text ( " OK " ) ,
2024-07-27 19:10:45 +00:00
onPressed: ( ) { Navigator . of ( context ) . pop ( ) ; }
)
]
)
) ;
2024-08-04 22:23:08 +00:00
} ) : null ) ,
2024-08-03 17:52:20 +00:00
const TextSpan ( text: " \n " ) ,
TextSpan ( text: player . gamesWon > - 1 ? intf . format ( player . gamesWon ) : " --- " , style: TextStyle ( color: player . gamesWon > - 1 ? Colors . white : Colors . grey ) ) ,
TextSpan ( text: " / ${ player . gamesPlayed > - 1 ? intf . format ( player . gamesPlayed ) : " --- " } " , style: const TextStyle ( fontFamily: " Eurostile Round Condensed " , color: Colors . grey ) ) ,
2024-07-27 19:10:45 +00:00
]
)
)
)
] ,
2024-08-04 22:23:08 +00:00
) ,
2024-07-27 19:10:45 +00:00
) ,
2024-08-04 22:23:08 +00:00
) ,
Row (
mainAxisAlignment: MainAxisAlignment . center ,
children: [
Expanded ( child: ElevatedButton . icon ( onPressed: ( ) { print ( " ok, and? " ) ; } , icon: const Icon ( Icons . person_add ) , label: Text ( t . track ) , style: const ButtonStyle ( shape: WidgetStatePropertyAll ( RoundedRectangleBorder ( borderRadius: BorderRadius . only ( bottomLeft: Radius . circular ( 12.0 ) ) ) ) ) ) ) ,
2024-10-03 21:00:41 +00:00
Expanded (
child: ElevatedButton . icon (
onPressed: ( ) {
Navigator . push ( context , MaterialPageRoute (
builder: ( context ) = > CompareView ( player ) ,
) ,
) ;
} ,
icon: const Icon ( Icons . balance ) ,
label: Text ( t . compare ) ,
style: const ButtonStyle ( shape: WidgetStatePropertyAll ( RoundedRectangleBorder ( borderRadius: BorderRadius . only ( bottomRight: Radius . circular ( 12.0 ) ) ) ) )
)
)
2024-08-04 22:23:08 +00:00
] ,
)
] ,
2024-07-27 19:10:45 +00:00
) ,
) ;
} ) ;
}
2024-08-03 17:52:20 +00:00
}
class SearchDrawer extends StatefulWidget {
2024-08-04 22:23:08 +00:00
final Function changePlayer ;
final TextEditingController controller ;
const SearchDrawer ( { super . key , required this . changePlayer , required this . controller } ) ;
2024-08-03 17:52:20 +00:00
@ override
State < SearchDrawer > createState ( ) = > _SearchDrawerState ( ) ;
}
class _SearchDrawerState extends State < SearchDrawer > {
@ override
Widget build ( BuildContext context ) {
return Drawer (
child: StreamBuilder (
stream: teto . allPlayers ,
builder: ( context , snapshot ) {
switch ( snapshot . connectionState ) {
case ConnectionState . none:
case ConnectionState . waiting:
case ConnectionState . done:
case ConnectionState . active:
final allPlayers = ( snapshot . data ! = null )
? snapshot . data as Map < String , String >
: < String , String > { } ;
allPlayers . remove ( prefs . getString ( " player " ) ? ? " 6098518e3d5155e6ec429cdc " ) ; // player from the home button will be delisted
List < String > keys = allPlayers . keys . toList ( ) ;
return NestedScrollView (
headerSliverBuilder: ( BuildContext context , bool value ) {
return [
SliverToBoxAdapter (
child: SearchBar (
2024-08-04 22:23:08 +00:00
controller: widget . controller ,
2024-08-03 17:52:20 +00:00
hintText: " Hello " ,
hintStyle: const WidgetStatePropertyAll ( TextStyle ( color: Colors . grey ) ) ,
trailing: [
2024-08-04 22:23:08 +00:00
IconButton ( onPressed: ( ) { setState ( ( ) {
widget . changePlayer ( widget . controller . value . text ) ;
Navigator . of ( context ) . pop ( ) ;
} ) ; } , icon: const Icon ( Icons . search ) )
2024-08-03 17:52:20 +00:00
] ,
2024-08-04 22:23:08 +00:00
onSubmitted: ( value ) {
setState ( ( ) {
widget . changePlayer ( value ) ;
Navigator . of ( context ) . pop ( ) ;
} ) ;
} ,
2024-08-03 17:52:20 +00:00
) ,
2024-08-07 22:42:04 +00:00
) ,
SliverToBoxAdapter (
child: ListTile (
title: Text ( prefs . getString ( " player " ) ? ? " dan63 " ) ,
onTap: ( ) {
widget . changePlayer ( " 6098518e3d5155e6ec429cdc " ) ;
Navigator . of ( context ) . pop ( ) ;
} ,
) ,
2024-08-03 17:52:20 +00:00
)
] ;
} ,
body: ListView . builder ( // Builds list of tracked players.
itemCount: allPlayers . length ,
itemBuilder: ( context , index ) {
var i = allPlayers . length - 1 - index ; // Last players in this map are most recent ones, they are gonna be shown at the top.
return ListTile (
title: Text ( allPlayers [ keys [ i ] ] ? ? keys [ i ] ) , // Takes last known username from list of states
onTap: ( ) {
2024-08-04 22:23:08 +00:00
widget . changePlayer ( keys [ i ] ) ; // changes to chosen player
2024-08-03 17:52:20 +00:00
Navigator . of ( context ) . pop ( ) ; // and closes itself.
} ,
) ;
} )
) ;
}
}
)
) ;
}
2024-08-04 22:23:08 +00:00
}
class TetraLeagueThingy extends StatelessWidget {
2024-08-16 22:40:09 +00:00
final TetraLeague league ;
2024-09-08 22:10:51 +00:00
final TetraLeague ? toCompare ;
2024-09-01 21:44:19 +00:00
final Cutoffs ? cutoffs ;
2024-08-04 22:23:08 +00:00
2024-09-08 22:10:51 +00:00
const TetraLeagueThingy ( { super . key , required this . league , this . toCompare , this . cutoffs } ) ;
2024-08-04 22:23:08 +00:00
@ override
Widget build ( BuildContext context ) {
return Card (
2024-09-05 21:42:21 +00:00
//surfaceTintColor: rankColors[league.rank],
2024-08-04 22:23:08 +00:00
child: Column (
children: [
2024-09-09 22:38:52 +00:00
TLRatingThingy ( userID: " w " , tlData: league , oldTl: toCompare , showPositions: true ) ,
2024-09-01 21:44:19 +00:00
TLProgress (
tlData: league ,
previousRankTRcutoff: cutoffs ! = null ? cutoffs ! . tr [ league . rank ! = " z " ? league . rank : league . percentileRank ] : null ,
nextRankTRcutoff: cutoffs ! = null ? ( league . rank ! = " z " ? league . rank = = " x+ " : league . percentileRank = = " x+ " ) ? 25000 : cutoffs ! . tr [ ranks . elementAtOrNull ( ranks . indexOf ( league . rank ! = " z " ? league . rank : league . percentileRank ) + 1 ) ] : null ,
2024-09-08 22:10:51 +00:00
previousRankTRcutoffTarget: league . rank ! = " z " ? rankTargets [ league . rank ] : null ,
nextRankTRcutoffTarget: ( league . rank ! = " z " & & league . rank ! = " x+ " ) ? rankTargets [ ranks . elementAtOrNull ( ranks . indexOf ( league . rank ) + 1 ) ] : null ,
2024-09-01 21:44:19 +00:00
previousGlickoCutoff: cutoffs ! = null ? cutoffs ! . glicko [ league . rank ! = " z " ? league . rank : league . percentileRank ] : null ,
nextRankGlickoCutoff: cutoffs ! = null ? ( league . rank ! = " z " ? league . rank = = " x+ " : league . percentileRank = = " x+ " ) ? 25000 : cutoffs ! . glicko [ ranks . elementAtOrNull ( ranks . indexOf ( league . rank ! = " z " ? league . rank : league . percentileRank ) + 1 ) ] : null ,
) ,
2024-08-07 22:42:04 +00:00
Row (
// spacing: 25.0,
// alignment: WrapAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment . center ,
2024-08-06 22:24:31 +00:00
children: [
2024-08-07 22:42:04 +00:00
Expanded (
child: Center (
child: Table (
2024-08-09 22:52:50 +00:00
defaultColumnWidth: const IntrinsicColumnWidth ( ) ,
2024-08-07 22:42:04 +00:00
children: [
TableRow ( children: [
2024-08-15 21:55:45 +00:00
Text ( f2 . format ( league . apm ? ? 0.00 ) , textAlign: TextAlign . right , style: const TextStyle ( fontSize: 21 ) ) ,
2024-09-08 22:10:51 +00:00
const Text ( " APM " , style: TextStyle ( fontSize: 21 ) ) ,
if ( toCompare ! = null ) Text ( " ( ${ comparef2 . format ( league . apm ! - toCompare ! . apm ! ) } ) " , textAlign: TextAlign . right , style: TextStyle ( fontSize: 21 , color: getDifferenceColor ( league . apm ! - toCompare ! . apm ! ) ) )
2024-08-07 22:42:04 +00:00
] ) ,
TableRow ( children: [
2024-08-15 21:55:45 +00:00
Text ( f2 . format ( league . pps ? ? 0.00 ) , textAlign: TextAlign . right , style: const TextStyle ( fontSize: 21 ) ) ,
2024-09-08 22:10:51 +00:00
const Text ( " PPS " , style: TextStyle ( fontSize: 21 ) ) ,
if ( toCompare ! = null ) Text ( " ( ${ comparef2 . format ( league . pps ! - toCompare ! . pps ! ) } ) " , textAlign: TextAlign . right , style: TextStyle ( fontSize: 21 , color: getDifferenceColor ( league . pps ! - toCompare ! . pps ! ) ) )
2024-08-07 22:42:04 +00:00
] ) ,
TableRow ( children: [
2024-08-15 21:55:45 +00:00
Text ( f2 . format ( league . vs ? ? 0.00 ) , textAlign: TextAlign . right , style: const TextStyle ( fontSize: 21 ) ) ,
2024-09-08 22:10:51 +00:00
const Text ( " VS " , style: TextStyle ( fontSize: 21 ) ) ,
if ( toCompare ! = null ) Text ( " ( ${ comparef2 . format ( league . vs ! - toCompare ! . vs ! ) } ) " , textAlign: TextAlign . right , style: TextStyle ( fontSize: 21 , color: getDifferenceColor ( league . vs ! - toCompare ! . vs ! ) ) )
2024-08-07 22:42:04 +00:00
] )
] ,
) ,
) ,
2024-08-06 22:24:31 +00:00
) ,
SizedBox (
height: 128.0 ,
width: 128.0 ,
2024-08-12 23:07:59 +00:00
child: ClipRRect (
borderRadius: BorderRadius . circular ( 1000 ) ,
child: SfRadialGauge (
backgroundColor: Colors . black ,
axes: [
RadialAxis (
2024-08-24 14:41:07 +00:00
minimum: 0.0 ,
maximum: 1.0 ,
2024-08-12 23:07:59 +00:00
radiusFactor: 1.01 ,
showTicks: true ,
showLabels: false ,
2024-08-24 14:41:07 +00:00
interval: 0.25 ,
minorTicksPerInterval: 0 ,
2024-08-12 23:07:59 +00:00
ranges: [
GaugeRange ( startValue: 0 , endValue: league . winrate , color: theme . colorScheme . primary )
] ,
annotations: [
GaugeAnnotation ( widget: Container ( child:
Text ( percentage . format ( league . winrate ) , textAlign: TextAlign . center , style: const TextStyle ( fontSize: 25 , fontWeight: FontWeight . bold ) ) ) ,
angle: 90 , positionFactor: 0.1
) ,
GaugeAnnotation ( widget: Container ( child:
Text ( t . statCellNum . winrate , textAlign: TextAlign . center ) ) ,
angle: 270 , positionFactor: 0.4
2024-09-08 22:10:51 +00:00
) ,
if ( toCompare ! = null ) GaugeAnnotation ( widget: Container ( child:
Text ( comparef2 . format ( ( league . winrate - toCompare ! . winrate ) * 100 ) , textAlign: TextAlign . center , style: TextStyle ( color: getDifferenceColor ( league . winrate - toCompare ! . winrate ) ) ) ) ,
angle: 90 , positionFactor: 0.45
2024-08-12 23:07:59 +00:00
)
] ,
)
]
) ,
2024-08-06 22:24:31 +00:00
) ,
) ,
2024-08-07 22:42:04 +00:00
Expanded (
child: Center (
child: Table (
2024-08-09 22:52:50 +00:00
defaultColumnWidth: const IntrinsicColumnWidth ( ) ,
2024-08-07 22:42:04 +00:00
children: [
TableRow ( children: [
//Text("APM: ", style: TextStyle(fontSize: 21)),
2024-08-09 22:52:50 +00:00
Text ( intf . format ( league . gamesPlayed ) , textAlign: TextAlign . right , style: const TextStyle ( fontSize: 21 ) ) ,
2024-09-08 22:10:51 +00:00
const Text ( " Games " , style: TextStyle ( fontSize: 21 ) ) ,
if ( toCompare ! = null ) Text ( " ( ${ comparef2 . format ( league . gamesPlayed - toCompare ! . gamesPlayed ) } ) " , textAlign: TextAlign . right , style: TextStyle ( fontSize: 21 , color: Colors . grey ) )
2024-08-07 22:42:04 +00:00
] ) ,
TableRow ( children: [
//Text("PPS: ", style: TextStyle(fontSize: 21)),
2024-08-09 22:52:50 +00:00
Text ( intf . format ( league . gamesWon ) , textAlign: TextAlign . right , style: const TextStyle ( fontSize: 21 ) ) ,
2024-09-08 22:10:51 +00:00
const Text ( " Won " , style: TextStyle ( fontSize: 21 ) ) ,
if ( toCompare ! = null ) Text ( " ( ${ comparef2 . format ( league . gamesWon - toCompare ! . gamesWon ) } ) " , textAlign: TextAlign . right , style: TextStyle ( fontSize: 21 , color: Colors . grey ) )
2024-09-09 22:38:52 +00:00
] ) ,
TableRow ( children: [
//Text("VS: ", style: TextStyle(fontSize: 21)),
Tooltip ( child: Text ( " ${ league . gxe . isNegative ? " --- " : f3 . format ( league . gxe ) } " , textAlign: TextAlign . right , style: TextStyle ( fontSize: 21 , color: league . standingLocal . isNegative ? Colors . grey : Colors . white ) ) , message: " ${ f2 . format ( league . s1tr ) } " , ) ,
Text ( " GLIXARE " , style: TextStyle ( fontSize: 21 , color: league . standingLocal . isNegative ? Colors . grey : Colors . white ) ) ,
if ( toCompare ! = null ) Text ( " ( ${ comparef . format ( league . gxe - toCompare ! . gxe ) } ) " , textAlign: TextAlign . right , style: TextStyle ( fontSize: 21 , color: getDifferenceColor ( league . standingLocal - toCompare ! . standingLocal ) ) )
] ) ,
2024-08-07 22:42:04 +00:00
] ,
) ,
) ,
) ,
] ,
) ,
] ,
) ,
) ;
}
}
class NerdStatsThingy extends StatelessWidget {
final NerdStats nerdStats ;
2024-09-08 22:10:51 +00:00
final NerdStats ? oldNerdStats ;
2024-08-07 22:42:04 +00:00
2024-09-08 22:10:51 +00:00
const NerdStatsThingy ( { super . key , required this . nerdStats , this . oldNerdStats } ) ;
2024-08-07 22:42:04 +00:00
@ override
Widget build ( BuildContext context ) {
return Card (
child: Column (
children: [
2024-08-15 21:55:45 +00:00
Padding (
2024-09-08 22:10:51 +00:00
padding: const EdgeInsets . fromLTRB ( 12.0 , 8.0 , 12.0 , 8.0 ) ,
2024-08-15 21:55:45 +00:00
child: Row (
crossAxisAlignment: CrossAxisAlignment . center ,
mainAxisSize: MainAxisSize . min ,
children: [
SizedBox (
height: 256.0 ,
width: 256.0 ,
child: ClipRRect (
borderRadius: BorderRadius . circular ( 1000 ) ,
child: SfRadialGauge (
backgroundColor: Colors . black ,
axes: [
RadialAxis (
startAngle: 200 ,
endAngle: 340 ,
minimum: 0.0 ,
maximum: 1.0 ,
radiusFactor: 1.01 ,
showTicks: true ,
showLabels: false ,
interval: 0.1 ,
//labelsPosition: ElementsPosition.outside,
ranges: [
GaugeRange ( startValue: 0 , endValue: nerdStats . app , color: theme . colorScheme . primary )
] ,
annotations: [
GaugeAnnotation ( widget: Container ( child:
RichText (
textAlign: TextAlign . center ,
text: TextSpan (
style: const TextStyle ( fontFamily: " Eurostile Round " ) ,
children: [
const TextSpan ( text: " APP \n " ) ,
TextSpan ( text: f3 . format ( nerdStats . app ) , style: const TextStyle ( fontSize: 25 , fontFamily: " Eurostile Round Extended " , fontWeight: FontWeight . w100 ) ) ,
2024-09-08 22:10:51 +00:00
if ( oldNerdStats ! = null ) TextSpan ( text: " \n ${ comparef . format ( nerdStats . app - oldNerdStats ! . app ) } " , style: TextStyle ( color: getDifferenceColor ( nerdStats . app - oldNerdStats ! . app ) ) ) ,
2024-08-15 21:55:45 +00:00
]
) ) ) ,
angle: 270 , positionFactor: 0.5
) ,
] ,
) ,
RadialAxis (
startAngle: 20 ,
endAngle: 160 ,
isInversed: true ,
minimum: 1.8 ,
maximum: 2.4 ,
radiusFactor: 1.01 ,
showTicks: true ,
showLabels: false ,
interval: 0.1 ,
//labelsPosition: ElementsPosition.outside,
ranges: [
GaugeRange ( startValue: 0 , endValue: nerdStats . vsapm , color: theme . colorScheme . primary )
] ,
annotations: [
GaugeAnnotation ( widget: Container ( child:
RichText (
textAlign: TextAlign . center ,
text: TextSpan (
style: const TextStyle ( fontFamily: " Eurostile Round " ) ,
children: [
const TextSpan ( text: " VS/APM \n " ) ,
TextSpan ( text: f3 . format ( nerdStats . vsapm ) , style: const TextStyle ( fontSize: 25 , fontFamily: " Eurostile Round Extended " , fontWeight: FontWeight . w100 ) ) ,
2024-09-08 22:10:51 +00:00
if ( oldNerdStats ! = null ) TextSpan ( text: " \n ${ comparef . format ( nerdStats . vsapm - oldNerdStats ! . vsapm ) } " , style: TextStyle ( color: getDifferenceColor ( nerdStats . vsapm - oldNerdStats ! . vsapm ) ) ) ,
2024-08-15 21:55:45 +00:00
]
) ) ) ,
angle: 90 , positionFactor: 0.5
)
] ,
)
]
) ,
2024-08-12 23:07:59 +00:00
) ,
2024-08-07 22:42:04 +00:00
) ,
2024-08-15 21:55:45 +00:00
Expanded (
child: Wrap (
alignment: WrapAlignment . center ,
2024-09-08 22:10:51 +00:00
spacing: 10.0 ,
runSpacing: 10.0 ,
runAlignment: WrapAlignment . start ,
2024-08-15 21:55:45 +00:00
children: [
2024-09-08 22:10:51 +00:00
GaugetThingy ( value: nerdStats . dss , oldValue: oldNerdStats ? . dss , min: 0 , max: 1.0 , tickInterval: . 2 , label: " DS/S " , sideSize: 128.0 , fractionDigits: 3 , moreIsBetter: true ) ,
GaugetThingy ( value: nerdStats . dsp , oldValue: oldNerdStats ? . dsp , min: 0 , max: 1.0 , tickInterval: . 2 , label: " DS/P " , sideSize: 128.0 , fractionDigits: 3 , moreIsBetter: true ) ,
GaugetThingy ( value: nerdStats . appdsp , oldValue: oldNerdStats ? . appdsp , min: 0 , max: 1.2 , tickInterval: . 2 , label: " APP+DS/P " , sideSize: 128.0 , fractionDigits: 3 , moreIsBetter: true ) ,
GaugetThingy ( value: nerdStats . cheese , oldValue: oldNerdStats ? . cheese , min: - 80 , max: 80 , tickInterval: 40 , label: " Cheese " , sideSize: 128.0 , fractionDigits: 2 , moreIsBetter: false ) ,
GaugetThingy ( value: nerdStats . gbe , oldValue: oldNerdStats ? . gbe , min: 0 , max: 1.0 , tickInterval: . 2 , label: " GbE " , sideSize: 128.0 , fractionDigits: 3 , moreIsBetter: true ) ,
GaugetThingy ( value: nerdStats . nyaapp , oldValue: oldNerdStats ? . nyaapp , min: 0 , max: 1.2 , tickInterval: . 2 , label: " wAPP " , sideSize: 128.0 , fractionDigits: 3 , moreIsBetter: true ) ,
GaugetThingy ( value: nerdStats . area , oldValue: oldNerdStats ? . area , min: 0 , max: 1000 , tickInterval: 100 , label: " Area " , sideSize: 128.0 , fractionDigits: 1 , moreIsBetter: true ) ,
2024-08-15 21:55:45 +00:00
] ,
) ,
)
]
) ,
2024-08-07 22:42:04 +00:00
) ,
] ,
)
) ;
2024-08-08 23:01:46 +00:00
}
}
class EstTrThingy extends StatelessWidget {
final EstTr estTr ;
const EstTrThingy ( { super . key , required this . estTr } ) ;
2024-08-07 22:42:04 +00:00
2024-08-08 23:01:46 +00:00
@ override
Widget build ( BuildContext context ) {
2024-08-09 22:52:50 +00:00
return const Card (
//child: ,
) ;
}
}
class GraphsThingy extends StatelessWidget {
final double apm ;
final double pps ;
final double vs ;
final NerdStats nerdStats ;
final Playstyle playstyle ;
const GraphsThingy ( { super . key , required this . nerdStats , required this . playstyle , required this . apm , required this . pps , required this . vs } ) ;
@ override
Widget build ( BuildContext context ) {
return Card (
child: Padding (
padding: const EdgeInsets . only ( top: 8.0 ) ,
child: Center ( child: Graphs ( apm , pps , vs , nerdStats , playstyle ) ) ,
) ,
) ;
2024-08-08 23:01:46 +00:00
}
2024-08-09 22:52:50 +00:00
2024-08-07 22:42:04 +00:00
}
class GaugetThingy extends StatelessWidget {
final double value ;
final double min ;
final double max ;
2024-09-08 22:10:51 +00:00
final double ? oldValue ;
final bool moreIsBetter ;
2024-08-08 23:01:46 +00:00
final double tickInterval ;
2024-08-07 22:42:04 +00:00
final String label ;
final double sideSize ;
final int fractionDigits ;
2024-09-08 22:10:51 +00:00
GaugetThingy ( { super . key , required this . value , required this . min , required this . max , this . oldValue , required this . tickInterval , required this . label , required this . sideSize , required this . fractionDigits , required this . moreIsBetter } ) ;
2024-08-07 22:42:04 +00:00
@ override
Widget build ( BuildContext context ) {
2024-08-08 23:01:46 +00:00
NumberFormat f = NumberFormat . decimalPatternDigits ( locale: LocaleSettings . currentLocale . languageCode , decimalDigits: fractionDigits ) ;
2024-08-12 23:07:59 +00:00
return ClipRRect (
borderRadius: BorderRadius . circular ( 1000 ) ,
child: SizedBox (
height: sideSize ,
width: sideSize ,
child: SfRadialGauge (
backgroundColor: Colors . black ,
axes: [
RadialAxis (
radiusFactor: 1.01 ,
minimum: min ,
maximum: max ,
showTicks: true ,
showLabels: false ,
interval: tickInterval ,
//labelsPosition: ElementsPosition.outside,
ranges: [
GaugeRange ( startValue: 0 , endValue: value , color: theme . colorScheme . primary )
] ,
annotations: [
GaugeAnnotation ( widget: Container ( child:
Text ( f . format ( value ) , textAlign: TextAlign . center , style: const TextStyle ( fontSize: 25 , fontWeight: FontWeight . bold ) ) ) ,
angle: 90 , positionFactor: 0.10
) ,
GaugeAnnotation ( widget: Container ( child:
Text ( label , textAlign: TextAlign . center , style: const TextStyle ( height: . 9 ) ) ) ,
angle: 270 , positionFactor: 0.4
2024-09-08 22:10:51 +00:00
) ,
if ( oldValue ! = null ) GaugeAnnotation ( widget: Container ( child:
Text ( comparef2 . format ( value - oldValue ! ) , textAlign: TextAlign . center , style: TextStyle ( color: getDifferenceColor ( moreIsBetter ? value - oldValue ! : oldValue ! - value ) ) ) ) ,
angle: 90 , positionFactor: 0.45
2024-08-12 23:07:59 +00:00
)
] ,
)
]
) ,
2024-08-04 22:23:08 +00:00
) ,
) ;
}
2024-08-09 22:52:50 +00:00
}
class ZenithThingy extends StatelessWidget {
final RecordSingle ? zenith ;
const ZenithThingy ( { super . key , required this . zenith } ) ;
@ override
Widget build ( BuildContext context ) {
return Card (
child: Padding (
padding: const EdgeInsets . fromLTRB ( 8.0 , 0.0 , 8.0 , 0.0 ) ,
child: Column (
children: [
Row (
mainAxisAlignment: MainAxisAlignment . center ,
children: [
Column (
mainAxisSize: MainAxisSize . min ,
children: [
RichText (
text: TextSpan (
text: zenith ! = null ? " ${ f2 . format ( zenith ! . stats . zenith ! . altitude ) } m " : " --- m " ,
style: TextStyle ( fontFamily: " Eurostile Round Extended " , fontSize: 36 , fontWeight: FontWeight . w500 , color: zenith ! = null ? Colors . white : Colors . grey ) ,
) ,
) ,
if ( zenith ! = null ) RichText (
text: TextSpan (
text: " " ,
style: const TextStyle ( fontFamily: " Eurostile Round " , fontSize: 14 , color: Colors . grey ) ,
children: [
2024-08-13 22:45:28 +00:00
if ( zenith ! . rank ! = - 1 ) TextSpan ( text: " № ${ intf . format ( zenith ! . rank ) } " , style: TextStyle ( color: getColorOfRank ( zenith ! . rank ) ) ) ,
2024-08-09 22:52:50 +00:00
if ( zenith ! . rank ! = - 1 ) const TextSpan ( text: " • " ) ,
2024-08-13 22:45:28 +00:00
if ( zenith ! . countryRank ! = - 1 ) TextSpan ( text: " № ${ intf . format ( zenith ! . countryRank ) } local " , style: TextStyle ( color: getColorOfRank ( zenith ! . countryRank ) ) ) ,
2024-08-09 22:52:50 +00:00
if ( zenith ! . countryRank ! = - 1 ) const TextSpan ( text: " • " ) ,
TextSpan ( text: timestamp ( zenith ! . timestamp ) ) ,
]
) ,
) ,
] ,
) ,
if ( zenith ! = null & & ( zenith ! . extras as ZenithExtras ) . mods . isNotEmpty ) Container ( width: 16.0 ) ,
if ( zenith ! = null & & ( zenith ! . extras as ZenithExtras ) . mods . isNotEmpty ) for ( String mod in ( zenith ! . extras as ZenithExtras ) . mods ) Image . asset ( " res/icons/ ${ mod } .png " , height: 64.0 )
] ,
) ,
if ( zenith ! = null ) Row (
children: [
Expanded (
child: Center (
child: Table (
defaultColumnWidth: const IntrinsicColumnWidth ( ) ,
children: [
TableRow ( children: [
const Text ( " APM: " , style: TextStyle ( fontSize: 21 ) ) ,
Text ( f2 . format ( zenith ! . aggregateStats . apm ) , textAlign: TextAlign . right , style: const TextStyle ( fontSize: 21 ) ) ,
] ) ,
TableRow ( children: [
const Text ( " PPS: " , style: TextStyle ( fontSize: 21 ) ) ,
Text ( f2 . format ( zenith ! . aggregateStats . pps ) , textAlign: TextAlign . right , style: const TextStyle ( fontSize: 21 ) ) ,
] ) ,
TableRow ( children: [
const Text ( " VS: " , style: TextStyle ( fontSize: 21 ) ) ,
Text ( f2 . format ( zenith ! . aggregateStats . vs ) , textAlign: TextAlign . right , style: const TextStyle ( fontSize: 21 ) ) ,
] )
] ,
) ,
) ,
) ,
Expanded (
child: Center (
child: Table (
defaultColumnWidth: const IntrinsicColumnWidth ( ) ,
children: [
TableRow ( children: [
2024-08-31 23:00:26 +00:00
Text ( intf . format ( zenith ! . stats . kills ) , textAlign: TextAlign . right , style: const TextStyle ( fontSize: 21 ) ) ,
2024-08-09 22:52:50 +00:00
const Text ( " KO's " , style: TextStyle ( fontSize: 21 ) )
] ) ,
TableRow ( children: [
Text ( f2 . format ( zenith ! . stats . cps ) , textAlign: TextAlign . right , style: const TextStyle ( fontSize: 21 ) ) ,
const Text ( " CPS " , style: TextStyle ( fontSize: 21 ) )
] ) ,
TableRow ( children: [
Text ( f2 . format ( zenith ! . stats . zenith ! . peakrank ) , textAlign: TextAlign . right , style: const TextStyle ( fontSize: 21 ) ) ,
const Text ( " Peak CPS " , style: TextStyle ( fontSize: 21 ) )
] )
] ,
) ,
) ,
) ,
] ,
)
]
) ,
)
) ;
}
2024-08-07 22:42:04 +00:00
2024-08-06 22:24:31 +00:00
}
class _TLRecords extends StatelessWidget {
final String userID ;
final Function changePlayer ;
final List < BetaRecord > data ;
final bool wasActiveInTL ;
final bool oldMathcesHere ;
final bool separateScrollController ;
/// Widget, that displays Tetra League records.
/// Accepts list of TL records ([data]) and [userID] of player from the view
const _TLRecords ( { required this . userID , required this . changePlayer , required this . data , required this . wasActiveInTL , required this . oldMathcesHere , this . separateScrollController = false } ) ;
@ override
Widget build ( BuildContext context ) {
if ( data . isEmpty ) {
return Center ( child: Column (
mainAxisSize: MainAxisSize . min ,
children: [
Text ( t . noRecords , style: const TextStyle ( fontFamily: " Eurostile Round " , fontSize: 28 ) ) ,
if ( wasActiveInTL ) Text ( t . errors . actionSuggestion ) ,
if ( wasActiveInTL ) TextButton ( onPressed: ( ) { changePlayer ( userID , fetchTLmatches: true ) ; } , child: Text ( t . fetchAndSaveOldTLmatches ) )
] ,
) ) ;
}
bool bigScreen = MediaQuery . of ( context ) . size . width > = 768 ;
int length = data . length ;
return ListView . builder (
physics: const AlwaysScrollableScrollPhysics ( ) ,
controller: separateScrollController ? ScrollController ( ) : null ,
itemCount: oldMathcesHere ? length : 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 ) ) ,
if ( wasActiveInTL ) Text ( t . errors . actionSuggestion ) ,
if ( wasActiveInTL ) TextButton ( onPressed: ( ) { changePlayer ( userID , fetchTLmatches: true ) ; } , child: Text ( t . fetchAndSaveOldTLmatches ) )
] ,
) ) ;
}
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 (
stops: const [ 0 , 0.05 ] ,
colors: [ accentColor , Colors . transparent ]
)
) ,
child: ListTile (
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 ] . results . leaderboard . firstWhere ( ( element ) = > element . id ! = userID ) . username } " ) ,
subtitle: Text ( timestamp ( data [ index ] . ts ) , style: const TextStyle ( color: Colors . grey ) ) ,
trailing: TrailingStats (
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 ) ) ) //Navigator.push(context, MaterialPageRoute(builder: (context) => TlMatchResultView(record: data[index], initPlayerId: userID))),
) ,
) ;
} ) ;
}
2024-08-31 23:00:26 +00:00
}
class TLRatingThingy extends StatelessWidget {
final String userID ;
final TetraLeague tlData ;
final TetraLeague ? oldTl ;
final double ? topTR ;
2024-09-01 21:44:19 +00:00
final bool ? showPositions ;
2024-08-31 23:00:26 +00:00
final DateTime ? lastMatchPlayed ;
2024-09-01 21:44:19 +00:00
const TLRatingThingy ( { super . key , required this . userID , required this . tlData , this . oldTl , this . topTR , this . lastMatchPlayed , this . showPositions } ) ;
2024-08-31 23:00:26 +00:00
@ override
Widget build ( BuildContext context ) {
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 . 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());
return Wrap (
direction: Axis . horizontal ,
alignment: WrapAlignment . spaceAround ,
crossAxisAlignment: WrapCrossAlignment . center ,
clipBehavior: Clip . hardEdge ,
children: [
( userID = = " 5e32fc85ab319c2ab1beb07c " & & oskKagariGimmick ) // he love her so much, you can't even imagine
? Image . asset ( " res/icons/kagari.png " , height: 128 ) // Btw why she wearing Kazamatsuri high school uniform?
: Image . asset ( " res/tetrio_tl_alpha_ranks/ ${ tlData . rank } .png " , height: 128 ) ,
Column (
crossAxisAlignment: CrossAxisAlignment . start ,
children: [
RichText (
text: TextSpan (
2024-09-09 22:38:52 +00:00
style: const TextStyle ( fontFamily: " Eurostile Round " , fontSize: 20 , color: Colors . white , height: 0.9 ) ,
2024-08-31 23:00:26 +00:00
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 ] ) ,
TextSpan ( text: " Glicko " , style: TextStyle ( fontFamily: " Eurostile Round Extended " , fontSize: bigScreen ? 42 : 28 ) )
] ,
2 = > [
TextSpan ( text: " ${ t . top } ${ formatedPercentile [ 0 ] } " , style: TextStyle ( fontFamily: " Eurostile Round Extended " , fontSize: bigScreen ? 42 : 28 ) ) ,
if ( formatedPercentile . elementAtOrNull ( 1 ) ! = null ) TextSpan ( text: decimalSeparator + formatedPercentile [ 1 ] ) ,
TextSpan ( text: " % " , style: TextStyle ( fontFamily: " Eurostile Round Extended " , fontSize: bigScreen ? 42 : 28 ) )
] ,
_ = > [
TextSpan ( text: formatedTR [ 0 ] , style: TextStyle ( fontFamily: " Eurostile Round Extended " , fontSize: bigScreen ? 42 : 28 ) ) ,
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 ) ) , ]
)
) ,
2024-09-09 22:38:52 +00:00
if ( oldTl ! = null ) RichText (
2024-08-31 23:00:26 +00:00
textAlign: TextAlign . center ,
2024-09-09 22:38:52 +00:00
softWrap: true ,
text: TextSpan (
style: DefaultTextStyle . of ( context ) . style ,
children: [
TextSpan ( 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 . tr - oldTl ! . tr ) } TR "
} ,
style: TextStyle (
color: getDifferenceColor ( switch ( prefs . getInt ( " ratingMode " ) ) {
1 = > tlData . glicko ! - oldTl ! . glicko ! ,
2 = > tlData . percentile - oldTl ! . percentile ,
_ = > tlData . tr - oldTl ! . tr
} )
) ,
) ,
const TextSpan ( text: " • " , style: TextStyle ( color: Colors . grey ) ) ,
TextSpan ( text: switch ( prefs . getInt ( " ratingMode " ) ) {
1 = > " ${ fDiff . format ( tlData . tr - oldTl ! . tr ) } TR " ,
_ = > " ${ fDiff . format ( tlData . glicko ! - oldTl ! . glicko ! ) } Glicko "
} ,
style: TextStyle (
color: getDifferenceColor ( switch ( prefs . getInt ( " ratingMode " ) ) {
1 = > tlData . tr - oldTl ! . tr ,
_ = > tlData . glicko ! - oldTl ! . glicko !
} )
) ,
) ,
const TextSpan ( text: " • " , style: TextStyle ( color: Colors . grey ) ) ,
TextSpan (
text: " ${ fDiff . format ( tlData . rd ! - oldTl ! . rd ! ) } RD " ,
style: TextStyle ( color: getDifferenceColor ( oldTl ! . rd ! - tlData . rd ! ) )
)
] ,
2024-08-31 23:00:26 +00:00
) ,
) ,
if ( tlData . gamesPlayed > 9 ) Column (
children: [
RichText (
textAlign: TextAlign . center ,
softWrap: true ,
text: TextSpan (
style: DefaultTextStyle . of ( context ) . style ,
children: [
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.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))
] ,
) ,
) ,
] ,
) ,
2024-09-01 21:44:19 +00:00
if ( showPositions = = true ) RichText (
2024-08-31 23:00:26 +00:00
textAlign: TextAlign . start ,
text: TextSpan (
text: " " ,
style: const TextStyle ( fontFamily: " Eurostile Round " , fontSize: 14 , color: Colors . grey ) ,
children: [
if ( tlData . standing ! = - 1 ) TextSpan ( text: " № ${ intf . format ( tlData . standing ) } " , style: TextStyle ( color: getColorOfRank ( tlData . standing ) ) ) ,
if ( tlData . standing ! = - 1 | | tlData . standingLocal ! = - 1 ) const TextSpan ( text: " • " ) ,
if ( tlData . standingLocal ! = - 1 ) TextSpan ( text: " № ${ intf . format ( tlData . standingLocal ) } local " , style: TextStyle ( color: getColorOfRank ( tlData . standingLocal ) ) ) ,
if ( tlData . standing ! = - 1 & & tlData . standingLocal ! = - 1 ) const TextSpan ( text: " • " ) ,
TextSpan ( text: timestamp ( tlData . timestamp ) ) ,
]
) ,
) ,
] ,
) ,
] ,
) ;
}
2024-09-14 22:05:50 +00:00
}
class FutureError extends StatelessWidget {
final AsyncSnapshot snapshot ;
FutureError ( this . snapshot ) ;
@ override
Widget build ( BuildContext context ) {
2024-09-29 22:02:19 +00:00
return TweenAnimationBuilder (
duration: Durations . medium3 ,
tween: Tween < double > ( begin: 0 , end: 1 ) ,
curve: Easing . standard ,
builder: ( context , value , child ) {
return Container (
transform: Matrix4 . translationValues ( 0 , 50 - value * 50 , 0 ) ,
child: Opacity ( opacity: value , child: child ) ,
) ;
} ,
child: Column (
2024-09-14 22:05:50 +00:00
mainAxisSize: MainAxisSize . min ,
children: [
2024-09-29 22:02:19 +00:00
Spacer ( ) ,
Icon ( Icons . error_outline , size: 128.0 , color: Colors . red , shadows: [
Shadow ( offset: Offset ( 0.0 , 0.0 ) , blurRadius: 30.0 , color: Colors . red ) ,
Shadow ( offset: Offset ( 0.0 , 0.0 ) , blurRadius: 80.0 , color: Colors . red ) ,
] ) ,
2024-09-14 22:05:50 +00:00
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 ) ,
2024-10-01 21:46:43 +00:00
child: Text ( snapshot . stackTrace . toString ( ) , textAlign: TextAlign . left , style: TextStyle ( fontFamily: " Monospace " ) ) ,
2024-09-14 22:05:50 +00:00
) ,
2024-09-29 22:02:19 +00:00
Spacer ( )
2024-09-14 22:05:50 +00:00
] ,
2024-09-29 22:02:19 +00:00
) ,
) ;
}
}
class FetchResultError extends StatelessWidget {
final FetchResults data ;
FetchResultError ( this . data ) ;
@ override
Widget build ( BuildContext context ) {
IconData icon = Icons . error_outline ;
String errText = " " ;
String ? subText ;
switch ( data . exception . runtimeType ) {
case TetrioPlayerNotExist:
icon = Icons . search_off ;
errText = t . errors . noSuchUser ;
subText = t . errors . noSuchUserSub ;
break ;
case TetrioDiscordNotExist:
icon = Icons . search_off ;
errText = t . errors . discordNotAssigned ;
subText = t . errors . discordNotAssignedSub ;
case ConnectionIssue:
var err = data . exception as ConnectionIssue ;
errText = t . errors . connection ( code: err . code , message: err . message ) ;
break ;
case TetrioForbidden:
icon = Icons . remove_circle ;
errText = t . errors . forbidden ;
subText = t . errors . forbiddenSub ( nickname: ' osk ' ) ;
break ;
case TetrioTooManyRequests:
errText = t . errors . tooManyRequests ;
subText = t . errors . tooManyRequestsSub ;
break ;
case TetrioOskwareBridgeProblem:
errText = t . errors . oskwareBridge ;
subText = t . errors . oskwareBridgeSub ;
break ;
case TetrioInternalProblem:
errText = kIsWeb ? t . errors . internalWebVersion : t . errors . internal ;
subText = kIsWeb ? t . errors . internalWebVersionSub : t . errors . internalSub ;
break ;
case ClientException:
errText = t . errors . clientException ;
break ;
default :
errText = data . exception . toString ( ) ;
}
return TweenAnimationBuilder (
duration: Durations . medium3 ,
tween: Tween < double > ( begin: 0 , end: 1 ) ,
curve: Easing . standard ,
builder: ( context , value , child ) {
return Container (
transform: Matrix4 . translationValues ( 0 , 50 - value * 50 , 0 ) ,
child: Opacity ( opacity: value , child: child ) ,
) ;
} ,
child: Column (
mainAxisSize: MainAxisSize . min ,
children: [
Spacer ( ) ,
Icon ( icon , size: 128.0 , color: Colors . red , shadows: [
Shadow ( offset: Offset ( 0.0 , 0.0 ) , blurRadius: 30.0 , color: Colors . red ) ,
Shadow ( offset: Offset ( 0.0 , 0.0 ) , blurRadius: 80.0 , color: Colors . red ) ,
] ) ,
Text ( errText , style: const TextStyle ( fontFamily: " Eurostile Round " , fontSize: 42 , fontWeight: FontWeight . bold ) , textAlign: TextAlign . center ) ,
if ( subText ! = null ) Padding (
padding: const EdgeInsets . only ( top: 8.0 ) ,
child: Text ( subText , textAlign: TextAlign . center ) ,
) ,
Spacer ( )
] ,
) ,
2024-09-14 22:05:50 +00:00
) ;
}
2024-07-27 19:10:45 +00:00
}