From bf72c4cee3f32c1de325ad6a4ac9ad360416b83a Mon Sep 17 00:00:00 2001 From: dan63047 Date: Sat, 23 Nov 2024 01:35:16 +0300 Subject: [PATCH] I'm not sure about my decisions --- lib/gen/strings.g.dart | 6 +- lib/main.dart | 1 + lib/utils/numers_formats.dart | 1 + lib/views/destination_calculator.dart | 507 +++++++++++++----------- lib/views/destination_cutoffs.dart | 263 ++++++------ lib/views/destination_graphs.dart | 122 +++--- lib/views/destination_home.dart | 430 ++++++++++---------- lib/views/destination_info.dart | 98 +++-- lib/views/destination_leaderboards.dart | 441 +++++++++++---------- lib/views/destination_saved_data.dart | 146 ++++--- lib/views/destination_settings.dart | 46 ++- lib/views/main_view.dart | 160 +++++--- res/i18n/strings.i18n.json | 2 +- 13 files changed, 1213 insertions(+), 1010 deletions(-) diff --git a/lib/gen/strings.g.dart b/lib/gen/strings.g.dart index 273325a..1c66178 100644 --- a/lib/gen/strings.g.dart +++ b/lib/gen/strings.g.dart @@ -6,7 +6,7 @@ /// Locales: 3 /// Strings: 1818 (606 per locale) /// -/// Built on 2024-11-16 at 13:39 UTC +/// Built on 2024-11-21 at 21:18 UTC // coverage:ignore-file // ignore_for_file: type=lint @@ -220,7 +220,7 @@ class Translations implements BaseTranslations { String comparingWith({required Object newDate, required Object oldDate}) => 'Data from ${newDate} comparing with ${oldDate}'; String get top => 'Top'; String get topRank => 'Top rank'; - String verdictGeneral({required Object n, required Object verdict, required Object rank}) => '${n} ${verdict} than ${rank} rank average'; + String verdictGeneral({required Object n, required Object verdict, required Object rank}) => '${n} ${verdict} ${rank} rank avg'; String get verdictBetter => 'better'; String get verdictWorse => 'worse'; String get smooth => 'Smooth'; @@ -2348,7 +2348,7 @@ extension on Translations { case 'comparingWith': return ({required Object newDate, required Object oldDate}) => 'Data from ${newDate} comparing with ${oldDate}'; case 'top': return 'Top'; case 'topRank': return 'Top rank'; - case 'verdictGeneral': return ({required Object n, required Object verdict, required Object rank}) => '${n} ${verdict} than ${rank} rank average'; + case 'verdictGeneral': return ({required Object n, required Object verdict, required Object rank}) => '${n} ${verdict} ${rank} rank avg'; case 'verdictBetter': return 'better'; case 'verdictWorse': return 'worse'; case 'smooth': return 'Smooth'; diff --git a/lib/main.dart b/lib/main.dart index 32d410e..d3c1f26 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -46,6 +46,7 @@ ThemeData theme = ThemeData( ), segmentedButtonTheme: SegmentedButtonThemeData( style: ButtonStyle( + visualDensity: VisualDensity(horizontal: -4.0, vertical: -4.0), side: const WidgetStatePropertyAll(BorderSide(color: Colors.transparent)), surfaceTintColor: const WidgetStatePropertyAll(Colors.cyanAccent), iconColor: const WidgetStatePropertyAll(Colors.cyanAccent), diff --git a/lib/utils/numers_formats.dart b/lib/utils/numers_formats.dart index 3e72b3d..005f44a 100644 --- a/lib/utils/numers_formats.dart +++ b/lib/utils/numers_formats.dart @@ -13,6 +13,7 @@ final NumberFormat f2l = NumberFormat.decimalPatternDigits(locale: LocaleSetting final NumberFormat f1 = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 1); final NumberFormat f0 = NumberFormat.decimalPattern(LocaleSettings.currentLocale.languageCode); final NumberFormat percentage = NumberFormat.percentPattern(LocaleSettings.currentLocale.languageCode)..maximumFractionDigits = 2; +final NumberFormat percentagef4 = NumberFormat.percentPattern(LocaleSettings.currentLocale.languageCode)..maximumFractionDigits = 4; /// Readable [a] - [b], without sign String readableIntDifference(int a, int b){ diff --git a/lib/views/destination_calculator.dart b/lib/views/destination_calculator.dart index 02de265..5918f6d 100644 --- a/lib/views/destination_calculator.dart +++ b/lib/views/destination_calculator.dart @@ -27,6 +27,8 @@ enum CalcCards{ damage } +CalcCards calcCard = CalcCards.calc; + class ClearData{ final String title; final Lineclears lineclear; @@ -131,6 +133,7 @@ class Rules{ const TextStyle mainToggleInRules = TextStyle(fontSize: 18, fontWeight: ui.FontWeight.w800); class _DestinationCalculatorState extends State { + // Stats calculator variables double? apm; double? pps; double? vs; @@ -141,6 +144,25 @@ class _DestinationCalculatorState extends State { TextEditingController apmController = TextEditingController(); TextEditingController vsController = TextEditingController(); + // Damage Calculator variables + List rSideWidgets = []; + List lSideWidgets = []; + int combo = -1; + int b2b = -1; + int previousB2B = -1; + int totalDamage = 0; + int normalDamage = 0; + int comboDamage = 0; + int b2bDamage = 0; + int surgeDamage = 0; + int pcDamage = 0; + + // values for "the bar" + late double sec2end; + late double sec3end; + late double sec4end; + late double sec5end; + List clears = []; Map customClearsChoice = { "No Spin Clears": 5, @@ -149,8 +171,6 @@ class _DestinationCalculatorState extends State { int idCounter = 0; Rules rules = Rules(); - CalcCards card = CalcCards.calc; - @override void initState() { super.initState(); @@ -175,17 +195,11 @@ class _DestinationCalculatorState extends State { } } - // void calcDamage(){ - // for (ClearData lineclear in clears){ - - // } - // } - Widget getCalculator(){ return SingleChildScrollView( child: Column( children: [ - Card( + if (widget.constraints.maxWidth > 768.0) Card( child: Center(child: Padding( padding: const EdgeInsets.only(bottom: 8.0), child: Column( @@ -200,14 +214,16 @@ class _DestinationCalculatorState extends State { padding: const EdgeInsets.fromLTRB(16.0, 8.0, 16.0, 8.0), child: Row( children: [ + //TODO: animate those TextFields Expanded( child: Padding( padding: const EdgeInsets.fromLTRB(12.0, 0.0, 12.0, 0.0), child: TextField( onSubmitted: (value) => calc(), + onChanged: (value) {setState(() {});}, controller: apmController, keyboardType: TextInputType.number, - decoration: const InputDecoration(suffix: Text("APM"), alignLabelWithHint: true, hintText: "Enter your APM"), + decoration: InputDecoration(suffix: apmController.value.text.isNotEmpty ? Text("APM") : null, alignLabelWithHint: true, hintText: widget.constraints.maxWidth > 768.0 ? "Enter your APM" : "APM"), ), ) ), @@ -216,9 +232,10 @@ class _DestinationCalculatorState extends State { padding: const EdgeInsets.fromLTRB(12.0, 0.0, 12.0, 0.0), child: TextField( onSubmitted: (value) => calc(), + onChanged: (value) {setState(() {});}, controller: ppsController, keyboardType: TextInputType.number, - decoration: const InputDecoration(suffix: Text("PPS"), alignLabelWithHint: true, hintText: "Enter your PPS"), + decoration: InputDecoration(suffix: ppsController.value.text.isNotEmpty ? Text("PPS") : null, alignLabelWithHint: true, hintText: widget.constraints.maxWidth > 768.0 ? "Enter your PPS" : "PPS"), ), ) ), @@ -227,9 +244,10 @@ class _DestinationCalculatorState extends State { padding: const EdgeInsets.fromLTRB(12.0, 0.0, 12.0, 0.0), child: TextField( onSubmitted: (value) => calc(), + onChanged: (value) {setState(() {});}, controller: vsController, keyboardType: TextInputType.number, - decoration: const InputDecoration(suffix: Text("VS"), alignLabelWithHint: true, hintText: "Enter your VS"), + decoration: InputDecoration(suffix: vsController.value.text.isNotEmpty ? Text("VS") : null, alignLabelWithHint: true, hintText: widget.constraints.maxWidth > 768.0 ? "Enter your VS" : "VS"), ), ) ), @@ -242,7 +260,7 @@ class _DestinationCalculatorState extends State { ), ), if (nerdStats != null) Card( - child: NerdStatsThingy(nerdStats: nerdStats!) + child: NerdStatsThingy(nerdStats: nerdStats!, width: widget.constraints.minWidth) ), if (playstyle != null) Card( child: Graphs(apm!, pps!, vs!, nerdStats!, playstyle!) @@ -253,10 +271,102 @@ class _DestinationCalculatorState extends State { ); } - Widget getDamageCalculator(){ - List rSideWidgets = []; - List lSideWidgets = []; + Widget rSideDamageCalculator(double width, bool hasSidebar){ + return SizedBox( + width: width - (hasSidebar ? 80 : 0), + height: widget.constraints.maxHeight - (hasSidebar ? 108 : 178), + child: clears.isEmpty ? InfoThingy("Click on the actions on the left to add them here") : + 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); + }); + }, + children: lSideWidgets, + ), + ), + Divider(), + Column( + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(16.0, 0.0, 34.0, 0.0), + child: Row( + children: [ + Text("Total damage:", style: TextStyle(fontSize: 36, fontWeight: ui.FontWeight.w100)), + Spacer(), + Text(intf.format(totalDamage), style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 36, fontWeight: ui.FontWeight.w100)) + ], + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Text("Lineclears: ${intf.format(normalDamage)}"), + Text("Combo: ${intf.format(comboDamage)}"), + Text("B2B: ${intf.format(b2bDamage)}"), + Text("Surge: ${intf.format(surgeDamage)}"), + Text("PCs: ${intf.format(pcDamage)}") + ], + ), + if (totalDamage > 0) SfLinearGauge( + minimum: 0, + maximum: totalDamage.toDouble(), + showLabels: false, + showTicks: false, + ranges: [ + LinearGaugeRange( + color: Colors.green, + startValue: 0, + endValue: normalDamage.toDouble(), + position: LinearElementPosition.cross, + ), + LinearGaugeRange( + color: Colors.yellow, + startValue: normalDamage.toDouble(), + endValue: sec2end, + position: LinearElementPosition.cross, + ), + LinearGaugeRange( + color: Colors.blue, + startValue: sec2end, + endValue: sec3end, + position: LinearElementPosition.cross, + ), + LinearGaugeRange( + color: Colors.red, + startValue: sec3end, + endValue: sec4end, + position: LinearElementPosition.cross, + ), + LinearGaugeRange( + color: Colors.orange, + startValue: sec4end, + endValue: sec5end, + position: LinearElementPosition.cross, + ), + ], + ), + 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)))))) + ], + ) + ], + ), + ), + ); + } + Widget getDamageCalculator(){ + rSideWidgets = []; + lSideWidgets = []; for (var key in clearsExisting.keys){ rSideWidgets.add(Text(key)); for (ClearData data in clearsExisting[key]!) rSideWidgets.add(Card( @@ -299,15 +409,15 @@ class _DestinationCalculatorState extends State { rSideWidgets.add(const Divider()); } - int combo = -1; - int b2b = -1; - int previousB2B = -1; - int totalDamage = 0; - int normalDamage = 0; - int comboDamage = 0; - int b2bDamage = 0; - int surgeDamage = 0; - int pcDamage = 0; + combo = -1; + b2b = -1; + previousB2B = -1; + totalDamage = 0; + normalDamage = 0; + comboDamage = 0; + b2bDamage = 0; + surgeDamage = 0; + pcDamage = 0; for (ClearData lineclear in clears){ previousB2B = b2b; @@ -345,13 +455,13 @@ class _DestinationCalculatorState extends State { pcDamage += pcDmg; } // values for "the bar" - double sec2end = normalDamage.toDouble()+comboDamage.toDouble(); - double sec3end = normalDamage.toDouble()+comboDamage.toDouble()+b2bDamage.toDouble(); - double sec4end = normalDamage.toDouble()+comboDamage.toDouble()+b2bDamage.toDouble()+surgeDamage.toDouble(); - double sec5end = normalDamage.toDouble()+comboDamage.toDouble()+b2bDamage.toDouble()+surgeDamage.toDouble()+pcDamage.toDouble(); + sec2end = normalDamage.toDouble()+comboDamage.toDouble(); + sec3end = normalDamage.toDouble()+comboDamage.toDouble()+b2bDamage.toDouble(); + sec4end = normalDamage.toDouble()+comboDamage.toDouble()+b2bDamage.toDouble()+surgeDamage.toDouble(); + sec5end = normalDamage.toDouble()+comboDamage.toDouble()+b2bDamage.toDouble()+surgeDamage.toDouble()+pcDamage.toDouble(); return Column( children: [ - Card( + if (widget.constraints.maxWidth > 768.0) Card( child: Center(child: Padding( padding: const EdgeInsets.only(bottom: 8.0), child: Column( @@ -361,216 +471,133 @@ class _DestinationCalculatorState extends State { ), )), ), - Row( - crossAxisAlignment: CrossAxisAlignment.start, - 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( - child: Column( - children: [ - Card( - child: Column( - children: [ - 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);}), - )), - ), - ListTile( - title: Text("Perfect Clear Damage"), - trailing: SizedBox(width: 90.0, child: TextField( - keyboardType: TextInputType.number, - inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r'[0-9]'))], - decoration: InputDecoration(hintText: rules.pcDamage.toString()), - onChanged: (value) => setState((){rules.pcDamage = int.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"), - trailing: DropdownButton( - items: [for (var v in ComboTables.values) if (v != ComboTables.none) DropdownMenuItem(value: v.index, child: Text(comboTablesNames[v]!))], - value: rules.comboTable.index, - onChanged: (v) => setState((){rules.comboTable = ComboTables.values[v!];}), - ), - ) - ], - ), - ), - 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);}), - )), - ), - ], - ), - ) - ], - ), - ) - ]), - ) - ], - ) - ), - ), - SizedBox( - width: widget.constraints.maxWidth - 350 - 80, - height: widget.constraints.maxHeight - 108, - child: clears.isEmpty ? InfoThingy("Click on the actions on the left to add them here") : - 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); - }); - }, - children: lSideWidgets, + Expanded( + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: widget.constraints.maxWidth > 768.0 ? 350.0 : widget.constraints.maxWidth, + child: DefaultTabController(length: widget.constraints.maxWidth > 768.0 ? 2 : 3, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Card( + child: TabBar(tabs: [ + Tab(text: "Actions"), + if (widget.constraints.maxWidth <= 768.0) Tab(text: "Results"), + Tab(text: "Rules"), + ]), ), - ), - Divider(), - Column( - mainAxisSize: MainAxisSize.min, - children: [ - Padding( - padding: const EdgeInsets.fromLTRB(16.0, 0.0, 34.0, 0.0), - child: Row( - children: [ - Text("Total damage:", style: TextStyle(fontSize: 36, fontWeight: ui.FontWeight.w100)), - Spacer(), - Text(intf.format(totalDamage), style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 36, fontWeight: ui.FontWeight.w100)) - ], + SizedBox( + height: widget.constraints.maxHeight - 164, + child: TabBarView(children: [ + SingleChildScrollView( + child: Column( + children: rSideWidgets, + ), ), - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - Text("Lineclears: ${intf.format(normalDamage)}"), - Text("Combo: ${intf.format(comboDamage)}"), - Text("B2B: ${intf.format(b2bDamage)}"), - Text("Surge: ${intf.format(surgeDamage)}"), - Text("PCs: ${intf.format(pcDamage)}") - ], - ), - if (totalDamage > 0) SfLinearGauge( - minimum: 0, - maximum: totalDamage.toDouble(), - showLabels: false, - showTicks: false, - ranges: [ - LinearGaugeRange( - color: Colors.green, - startValue: 0, - endValue: normalDamage.toDouble(), - position: LinearElementPosition.cross, + if (widget.constraints.maxWidth <= 768.0) SingleChildScrollView( + child: rSideDamageCalculator(widget.constraints.minWidth, false), + ), + SingleChildScrollView( + child: Column( + children: [ + Card( + child: Column( + children: [ + 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);}), + )), + ), + ListTile( + title: Text("Perfect Clear Damage"), + trailing: SizedBox(width: 90.0, child: TextField( + keyboardType: TextInputType.number, + inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r'[0-9]'))], + decoration: InputDecoration(hintText: rules.pcDamage.toString()), + onChanged: (value) => setState((){rules.pcDamage = int.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"), + trailing: DropdownButton( + items: [for (var v in ComboTables.values) if (v != ComboTables.none) DropdownMenuItem(value: v.index, child: Text(comboTablesNames[v]!))], + value: rules.comboTable.index, + onChanged: (v) => setState((){rules.comboTable = ComboTables.values[v!];}), + ), + ) + ], + ), + ), + 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);}), + )), + ), + ], + ), + ) + ], ), - LinearGaugeRange( - color: Colors.yellow, - startValue: normalDamage.toDouble(), - endValue: sec2end, - position: LinearElementPosition.cross, - ), - LinearGaugeRange( - color: Colors.blue, - startValue: sec2end, - endValue: sec3end, - position: LinearElementPosition.cross, - ), - LinearGaugeRange( - color: Colors.red, - startValue: sec3end, - endValue: sec4end, - position: LinearElementPosition.cross, - ), - LinearGaugeRange( - color: Colors.orange, - startValue: sec4end, - endValue: sec5end, - position: LinearElementPosition.cross, - ), - ], - ), - 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)))))) - ], - ) - ], + ), + ]), + ) + ], + ) ), ), - ) - ], + if (widget.constraints.maxWidth > 768.0) rSideDamageCalculator(widget.constraints.maxWidth - 350, true) + ], + ), ) ], ); @@ -581,13 +608,13 @@ class _DestinationCalculatorState extends State { return Column( children: [ SizedBox( - height: widget.constraints.maxHeight -32, - child: switch (card){ + height: widget.constraints.maxHeight - (widget.constraints.maxWidth > 768.0 ? 32 : 133), + child: switch (calcCard){ CalcCards.calc => getCalculator(), CalcCards.damage => getDamageCalculator() } ), - SegmentedButton( + if (widget.constraints.maxWidth > 768.0) SegmentedButton( showSelectedIcon: false, segments: >[ const ButtonSegment( @@ -599,10 +626,10 @@ class _DestinationCalculatorState extends State { label: Text('Damage Calculator'), ), ], - selected: {card}, + selected: {calcCard}, onSelectionChanged: (Set newSelection) { setState(() { - card = newSelection.first; + calcCard = newSelection.first; });}) ], ); diff --git a/lib/views/destination_cutoffs.dart b/lib/views/destination_cutoffs.dart index 7feb500..32f1c45 100644 --- a/lib/views/destination_cutoffs.dart +++ b/lib/views/destination_cutoffs.dart @@ -157,136 +157,139 @@ class _DestinationCutoffsState extends State { ), ), ), - 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: TextButton(child: Text("More Info", textAlign: TextAlign.center, style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, fontWeight: FontWeight.w500)), onPressed: () { - Navigator.push(context, MaterialPageRoute( - builder: (context) => RankView(rank: "", nextRankTR: snapshot.data!.data["top1"]!.tr, nextRankPercentile: 0.00, nextRankTargetTR: 25000.00, totalPlayers: snapshot.data!.total, cutoffTetrio: CutoffTetrio(apm: 0, pps: 0, vs: 0, pos: 0, percentile: 1, count: snapshot.data!.total, countPercentile: 1, tr: snapshot.data!.data["d"]!.tr, targetTr: snapshot.data!.data['d']!.targetTr)), - ), - ); - },), - ), - ] - ), - 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), - children: [ - 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 on ${f2.format(snapshot.data!.data[ranks[ranks.indexOf(rank)+1]]!.tr - snapshot.data!.data[ranks[ranks.indexOf(rank)+1]]!.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 on ${f2.format(snapshot.data!.data[rank]!.targetTr - snapshot.data!.data[rank]!.tr)} TR" : "Not deflated", style: TextStyle(color: snapshot.data!.data[rank]!.tr < snapshot.data!.data[rank]!.targetTr ? Colors.white : Colors.white60, shadows: null)) - ] - )), - ), - 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), - children: [ - TextSpan(text: intf.format(snapshot.data!.data[rank]!.count)), - TextSpan(text: " (${f2.format(snapshot.data!.data[rank]!.countPercentile * 100)}%)", style: const TextStyle(color: Colors.white60, shadows: null)), - TextSpan(text: "\n(from № ${intf.format(snapshot.data!.data[rank]!.pos)})", style: const TextStyle(color: Colors.white60, shadows: null)) - ] - )) - ), - 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)), onPressed: () { - Navigator.push(context, MaterialPageRoute( - builder: (context) => RankView(rank: rank, nextRankTR: rank == "x+" ? snapshot.data!.data["top1"]!.tr : snapshot.data!.data[ranks[ranks.indexOf(rank)+1]]!.tr, nextRankPercentile: rank == "x+" ? 0.00 : snapshot.data!.data[ranks[ranks.indexOf(rank)+1]]!.percentile, nextRankTargetTR: rank == "x+" ? 25000.00 : snapshot.data!.data[ranks[ranks.indexOf(rank)+1]]!.targetTr, totalPlayers: snapshot.data!.total, cutoffTetrio: snapshot.data!.data[rank]!), - ), - ); - },), - ), - ] - ) - ], + SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: 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: TextButton(child: Text("More Info", textAlign: TextAlign.center, style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, fontWeight: FontWeight.w500)), onPressed: () { + Navigator.push(context, MaterialPageRoute( + builder: (context) => RankView(rank: "", nextRankTR: snapshot.data!.data["top1"]!.tr, nextRankPercentile: 0.00, nextRankTargetTR: 25000.00, totalPlayers: snapshot.data!.total, cutoffTetrio: CutoffTetrio(apm: 0, pps: 0, vs: 0, pos: 0, percentile: 1, count: snapshot.data!.total, countPercentile: 1, tr: snapshot.data!.data["d"]!.tr, targetTr: snapshot.data!.data['d']!.targetTr)), + ), + ); + },), + ), + ] + ), + 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), + children: [ + 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 on ${f2.format(snapshot.data!.data[ranks[ranks.indexOf(rank)+1]]!.tr - snapshot.data!.data[ranks[ranks.indexOf(rank)+1]]!.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 on ${f2.format(snapshot.data!.data[rank]!.targetTr - snapshot.data!.data[rank]!.tr)} TR" : "Not deflated", style: TextStyle(color: snapshot.data!.data[rank]!.tr < snapshot.data!.data[rank]!.targetTr ? Colors.white : Colors.white60, shadows: null)) + ] + )), + ), + 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), + children: [ + TextSpan(text: intf.format(snapshot.data!.data[rank]!.count)), + TextSpan(text: " (${f2.format(snapshot.data!.data[rank]!.countPercentile * 100)}%)", style: const TextStyle(color: Colors.white60, shadows: null)), + TextSpan(text: "\n(from № ${intf.format(snapshot.data!.data[rank]!.pos)})", style: const TextStyle(color: Colors.white60, shadows: null)) + ] + )) + ), + 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)), onPressed: () { + Navigator.push(context, MaterialPageRoute( + builder: (context) => RankView(rank: rank, nextRankTR: rank == "x+" ? snapshot.data!.data["top1"]!.tr : snapshot.data!.data[ranks[ranks.indexOf(rank)+1]]!.tr, nextRankPercentile: rank == "x+" ? 0.00 : snapshot.data!.data[ranks[ranks.indexOf(rank)+1]]!.percentile, nextRankTargetTR: rank == "x+" ? 25000.00 : snapshot.data!.data[ranks[ranks.indexOf(rank)+1]]!.targetTr, totalPlayers: snapshot.data!.total, cutoffTetrio: snapshot.data!.data[rank]!), + ), + ); + },), + ), + ] + ) + ], + ), ) ] ), diff --git a/lib/views/destination_graphs.dart b/lib/views/destination_graphs.dart index 7bb5ad7..4e0218f 100644 --- a/lib/views/destination_graphs.dart +++ b/lib/views/destination_graphs.dart @@ -19,13 +19,17 @@ class DestinationGraphs extends StatefulWidget{ final String searchFor; //final Function setState; final BoxConstraints constraints; + final bool noSidebar; - const DestinationGraphs({super.key, required this.searchFor, required this.constraints}); + const DestinationGraphs({super.key, required this.searchFor, required this.constraints, required this.noSidebar}); @override State createState() => _DestinationGraphsState(); } +Graph graph = Graph.history; +Stats Ychart = Stats.tr; + enum Graph{ history, leagueState, @@ -42,14 +46,12 @@ class _DestinationGraphsState extends State { String yAxisTitle = ""; bool _smooth = false; final List> _yAxis = [for (MapEntry e in chartsShortTitles.entries) DropdownMenuItem(value: e.key, child: Text(e.value))]; - Graph _graph = Graph.history; - Stats _Ychart = Stats.tr; Stats _Xchart = Stats.tr; int _season = currentSeason-1; ValueNotifier historyPlayerUsername = ValueNotifier(""); ValueNotifier historyPlayerAvatarRevizion = ValueNotifier(""); List excludeRanks = []; - late Future> futureLeague = getTetraLeagueData(_Xchart, _Ychart); + late Future> futureLeague = getTetraLeagueData(_Xchart, Ychart); String searchLeague = ""; //Duration postSeasonLeft = seasonStart.difference(DateTime.now()); @@ -99,7 +101,7 @@ class _DestinationGraphsState extends State { style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 20), ), ), - Text('${f4.format(data.x)} ${chartsShortTitles[_Xchart]}\n${f4.format(data.y)} ${chartsShortTitles[_Ychart]}') + Text('${f4.format(data.x)} ${chartsShortTitles[_Xchart]}\n${f4.format(data.y)} ${chartsShortTitles[Ychart]}') ], ), ); @@ -207,8 +209,8 @@ class _DestinationGraphsState extends State { case ConnectionState.done: if (snapshot.hasData){ if (snapshot.data!.isEmpty || !snapshot.data!.containsKey(_season)) return ErrorThingy(eText: "Not enough data"); - List<_HistoryChartSpot> selectedGraph = snapshot.data![_season]![_Ychart]!; - yAxisTitle = chartsShortTitles[_Ychart]!; + List<_HistoryChartSpot> selectedGraph = snapshot.data![_season]![Ychart]!; + yAxisTitle = chartsShortTitles[Ychart]!; return SfCartesianChart( tooltipBehavior: _historyTooltipBehavior, zoomPanBehavior: _zoomPanBehavior, @@ -306,14 +308,14 @@ class _DestinationGraphsState extends State { return const Center(child: CircularProgressIndicator()); case ConnectionState.done: if (snapshot.hasData){ - yAxisTitle = chartsShortTitles[_Ychart]!; + yAxisTitle = chartsShortTitles[Ychart]!; return SfCartesianChart( tooltipBehavior: _leagueTooltipBehavior, zoomPanBehavior: _zoomPanBehavior, primaryXAxis: const DateTimeAxis(), primaryYAxis: NumericAxis( // isInversed: true, - maximum: switch (_Ychart){ + maximum: switch (Ychart){ Stats.tr => 25000.0, Stats.gxe => 100.00, _ => null @@ -327,7 +329,7 @@ class _DestinationGraphsState extends State { animationDuration: 0, //opacity: 0.5, xValueMapper: (Cutoffs data, _) => data.ts, - yValueMapper: (Cutoffs data, _) => switch (_Ychart){ + yValueMapper: (Cutoffs data, _) => switch (Ychart){ Stats.glicko => data.glicko[rank], Stats.gxe => data.gxe[rank], _ => data.tr[rank] @@ -344,20 +346,20 @@ class _DestinationGraphsState extends State { @override Widget build(BuildContext context) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - SingleChildScrollView( - scrollDirection: Axis.vertical, - child: Column( + return SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Column( mainAxisSize: MainAxisSize.min, children: [ Card( child: Wrap( spacing: 20, crossAxisAlignment: WrapCrossAlignment.center, + alignment: WrapAlignment.center, children: [ - if (_graph == Graph.history) Row( + if (graph == Graph.history) Row( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.person), @@ -372,7 +374,7 @@ class _DestinationGraphsState extends State { ), ], ), - if (_graph == Graph.leagueState) SizedBox( + if (graph == Graph.leagueState) SizedBox( width: 300, child: TextField( style: TextStyle(fontSize: 18.0000), @@ -385,11 +387,11 @@ class _DestinationGraphsState extends State { }, onSubmitted: (v){ searchLeague = v; - setState((){futureLeague = getTetraLeagueData(_Xchart, _Ychart);}); + setState((){futureLeague = getTetraLeagueData(_Xchart, Ychart);}); }, ) ), - if (_graph == Graph.history) Row( + if (graph == Graph.history) Row( mainAxisSize: MainAxisSize.min, children: [ const Padding(padding: EdgeInsets.all(8.0), child: Text("Season:", style: TextStyle(fontSize: 22))), @@ -404,20 +406,20 @@ class _DestinationGraphsState extends State { ), ], ), - if (_graph != Graph.leagueCutoffs) Row( + 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){ + 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, + value: graph == Graph.history ? _gamesPlayedInsteadOfDateAndTime : _Xchart, onChanged: (value) { setState(() { - if (_graph == Graph.history) + if (graph == Graph.history) _gamesPlayedInsteadOfDateAndTime = value! as bool; else _Xchart = value! as Stats; }); @@ -430,17 +432,17 @@ class _DestinationGraphsState extends State { children: [ const Padding(padding: EdgeInsets.all(8.0), child: Text("Y:", style: TextStyle(fontSize: 22))), DropdownButton( - 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, - value: _Ychart, + 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, + value: Ychart, onChanged: (value) { setState(() { - _Ychart = value!; + Ychart = value!; }); } ), ], ), - if (_graph == Graph.history) Row( + if (graph == Graph.history) Row( mainAxisSize: MainAxisSize.min, children: [ Checkbox(value: _smooth, @@ -453,7 +455,7 @@ class _DestinationGraphsState extends State { Text(t.smooth, style: const TextStyle(color: Colors.white, fontSize: 22)) ], ), - if (_graph == Graph.leagueState) IconButton( + if (graph == Graph.leagueState) IconButton( color: excludeRanks.isNotEmpty ? Theme.of(context).colorScheme.primary : null, onPressed: (){ showDialog(context: context, builder: (BuildContext context) { @@ -492,7 +494,7 @@ class _DestinationGraphsState extends State { actions: [ TextButton( child: const Text("Apply"), - onPressed: () {Navigator.of(context).pop(); setState((){futureLeague = getTetraLeagueData(_Xchart, _Ychart);});} + onPressed: () {Navigator.of(context).pop(); setState((){futureLeague = getTetraLeagueData(_Xchart, Ychart);});} ) ] ); @@ -506,10 +508,10 @@ class _DestinationGraphsState extends State { ), Card( child: SizedBox( - width: MediaQuery.of(context).size.width - 88, + width: MediaQuery.of(context).size.width - (widget.noSidebar ? 0 : 88), height: MediaQuery.of(context).size.height - 96, child: Padding( padding: const EdgeInsets.fromLTRB(40, 30, 40, 30), - child: switch (_graph){ + child: switch (graph){ Graph.history => getHistoryGraph(), Graph.leagueState => getLeagueState(), Graph.leagueCutoffs => getCutoffsHistory() @@ -519,34 +521,34 @@ class _DestinationGraphsState extends State { ) ], ), - ), - SegmentedButton( - showSelectedIcon: false, - segments: >[ - const ButtonSegment( - value: Graph.history, - label: Text('Player History')), - ButtonSegment( - value: Graph.leagueState, - label: Text('League State')), - ButtonSegment( - value: Graph.leagueCutoffs, - label: Text('League Cutoffs'), - ), - ], - selected: {_graph}, - onSelectionChanged: (Set newSelection) { - setState(() { - _graph = newSelection.first; - switch (newSelection.first){ - case Graph.leagueCutoffs: - case Graph.history: - _Ychart = Stats.tr; - case Graph.leagueState: - _Ychart = Stats.apm; - } - });}) - ], + if (!widget.noSidebar) SegmentedButton( + showSelectedIcon: false, + segments: >[ + const ButtonSegment( + value: Graph.history, + label: Text('Player History')), + ButtonSegment( + value: Graph.leagueState, + label: Text('League State')), + ButtonSegment( + value: Graph.leagueCutoffs, + label: Text('League Cutoffs'), + ), + ], + selected: {graph}, + onSelectionChanged: (Set newSelection) { + setState(() { + graph = newSelection.first; + switch (newSelection.first){ + case Graph.leagueCutoffs: + case Graph.history: + Ychart = Stats.tr; + case Graph.leagueState: + Ychart = Stats.apm; + } + });}) + ], + ), ); } } diff --git a/lib/views/destination_home.dart b/lib/views/destination_home.dart index 9693644..8157533 100644 --- a/lib/views/destination_home.dart +++ b/lib/views/destination_home.dart @@ -125,58 +125,60 @@ class ZenithCard extends StatelessWidget { Widget splitsCard(){ return Card( - child: SizedBox( - width: 300, - height: 318, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Stack( - alignment: AlignmentDirectional.bottomStart, - children: [ - const Text("T", style: TextStyle( - fontStyle: FontStyle.italic, - fontSize: 65, - height: 1.2, - )), - const Positioned(left: 25, top: 20, child: Text("otal time", style: TextStyle(fontFamily: "Eurostile Round Extended"))), - Padding( - padding: const EdgeInsets.only(left: 10.0), - child: Text(getMoreNormalTime(record!.stats.finalTime), style: const TextStyle( - shadows: textShadow, - fontFamily: "Eurostile Round Extended", - fontSize: 36, - fontWeight: FontWeight.w500, - color: Colors.white - )), - ) - ], - ), - SizedBox( - width: 300.0, - child: Table( - columnWidths: const { - 0: FixedColumnWidth(36) - }, + child: Center( + child: SizedBox( + width: 300, + height: 318, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Stack( + alignment: AlignmentDirectional.bottomStart, children: [ - const TableRow( - children: [ - Text("Floor"), - Text("Split", textAlign: TextAlign.right), - Text("Total", textAlign: TextAlign.right), - ] - ), - for (int i = 0; i < record!.stats.zenith!.splits.length; i++) TableRow( - children: [ - Text((i+1).toString()), - Text(record!.stats.zenith!.splits[i] != Duration.zero ? getMoreNormalTime(record!.stats.zenith!.splits[i]-(i-1 != -1 ? record!.stats.zenith!.splits[i-1] : Duration.zero)) : "--:--.---", textAlign: TextAlign.right), - Text(record!.stats.zenith!.splits[i] != Duration.zero ? getMoreNormalTime(record!.stats.zenith!.splits[i]) : "--:--.---", textAlign: TextAlign.right), - ] + const Text("T", style: TextStyle( + fontStyle: FontStyle.italic, + fontSize: 65, + height: 1.2, + )), + const Positioned(left: 25, top: 20, child: Text("otal time", style: TextStyle(fontFamily: "Eurostile Round Extended"))), + Padding( + padding: const EdgeInsets.only(left: 10.0), + child: Text(getMoreNormalTime(record!.stats.finalTime), style: const TextStyle( + shadows: textShadow, + fontFamily: "Eurostile Round Extended", + fontSize: 36, + fontWeight: FontWeight.w500, + color: Colors.white + )), ) ], ), - ), - ], + 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), + ] + ), + for (int i = 0; i < record!.stats.zenith!.splits.length; i++) TableRow( + children: [ + Text((i+1).toString()), + Text(record!.stats.zenith!.splits[i] != Duration.zero ? getMoreNormalTime(record!.stats.zenith!.splits[i]-(i-1 != -1 ? record!.stats.zenith!.splits[i-1] : Duration.zero)) : "--:--.---", textAlign: TextAlign.right), + Text(record!.stats.zenith!.splits[i] != Duration.zero ? getMoreNormalTime(record!.stats.zenith!.splits[i]) : "--:--.---", textAlign: TextAlign.right), + ] + ) + ], + ), + ), + ], + ), ), ), ); @@ -257,9 +259,156 @@ class RecordCard extends StatelessWidget { final MapEntry? closestAverage; final bool? betterThanClosestAverage; final String? rank; + final double width; - const RecordCard(this.record, this.achievements, this.betterThanRankAverage, this.closestAverage, this.betterThanClosestAverage, this.rank); + const RecordCard(this.record, this.achievements, this.betterThanRankAverage, this.closestAverage, this.betterThanClosestAverage, this.rank, {this.width = double.infinity}); + Widget result(){ + return Card( + 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, + children: [ + 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: [ + if (rank != null && rank != "z") TextSpan(text: "${t.verdictGeneral(n: switch(record!.gamemode){ + "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" || rank == "x+") && 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) 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)), + ] + ), + ), + ], + ), + ], + ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Expanded( + child: Table( + 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 " + }, textAlign: TextAlign.right, style: const TextStyle(fontSize: 21)), + Text(switch(record!.gamemode){ + "40l" => " Pieces", + "blitz" => " Level", + "5mblast" => " SPP", + _ => " i wanted to" + }, textAlign: TextAlign.left, style: const TextStyle(fontSize: 21)), + ]), + TableRow(children: [ + Text(f2.format(record!.stats.pps), textAlign: TextAlign.right, style: const TextStyle(fontSize: 21)), + const Text(" PPS", textAlign: TextAlign.left, style: TextStyle(fontSize: 21)), + ]), + 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" + }, textAlign: TextAlign.right, style: const TextStyle(fontSize: 21)), + Text(switch(record!.gamemode){ + "40l" => " KPP", + "blitz" => " SPP", + "5mblast" => " Pieces", + _ => " no" + }, textAlign: TextAlign.left, style: const TextStyle(fontSize: 21)), + ]) + ], + ), + ), + Expanded( + child: Table( + defaultColumnWidth:const IntrinsicColumnWidth(), + children: [ + TableRow(children: [ + 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)), + ]), + TableRow(children: [ + Text(f2.format(record!.stats.kps), textAlign: TextAlign.right, style: const TextStyle(fontSize: 21)), + const Text(" KPS", textAlign: TextAlign.left, style: TextStyle(fontSize: 21)), + ]), + TableRow(children: [ + Text(switch(record!.gamemode){ + "40l" => " ", + "blitz" => record!.stats.piecesPlaced.toString(), + "5mblast" => record!.stats.piecesPlaced.toString(), + _ => "but god said" + }, textAlign: TextAlign.right, style: const TextStyle(fontSize: 21)), + Text(switch(record!.gamemode){ + "40l" => " ", + "blitz" => " Pieces", + "5mblast" => " Pieces", + _ => " no" + }, textAlign: TextAlign.left, style: const TextStyle(fontSize: 21)), + ]) + ], + ), + ), + ], + ) + ], + ), + ); + } + + Widget hjsdj(){ + return 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") + ], + ), + ), + ); + } + @override Widget build(BuildContext context) { if (record == null) { @@ -288,149 +437,15 @@ class RecordCard extends StatelessWidget { ), ), ), - Card( - 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, - children: [ - 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: [ - if (rank != null && rank != "z") TextSpan(text: "${t.verdictGeneral(n: switch(record!.gamemode){ - "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" || rank == "x+") && 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) 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)), - ] - ), - ), - ], - ), - ], - ), - Row( - children: [ - Expanded( - child: Table( - 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 " - }, textAlign: TextAlign.right, style: const TextStyle(fontSize: 21)), - Text(switch(record!.gamemode){ - "40l" => " Pieces", - "blitz" => " Level", - "5mblast" => " SPP", - _ => " i wanted to" - }, textAlign: TextAlign.left, style: const TextStyle(fontSize: 21)), - ]), - TableRow(children: [ - Text(f2.format(record!.stats.pps), textAlign: TextAlign.right, style: const TextStyle(fontSize: 21)), - const Text(" PPS", textAlign: TextAlign.left, style: TextStyle(fontSize: 21)), - ]), - 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" - }, textAlign: TextAlign.right, style: const TextStyle(fontSize: 21)), - Text(switch(record!.gamemode){ - "40l" => " KPP", - "blitz" => " SPP", - "5mblast" => " Pieces", - _ => " no" - }, textAlign: TextAlign.left, style: const TextStyle(fontSize: 21)), - ]) - ], - ), - ), - Expanded( - child: Table( - defaultColumnWidth:const IntrinsicColumnWidth(), - children: [ - TableRow(children: [ - 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)), - ]), - TableRow(children: [ - Text(f2.format(record!.stats.kps), textAlign: TextAlign.right, style: const TextStyle(fontSize: 21)), - const Text(" KPS", textAlign: TextAlign.left, style: TextStyle(fontSize: 21)), - ]), - TableRow(children: [ - Text(switch(record!.gamemode){ - "40l" => " ", - "blitz" => record!.stats.piecesPlaced.toString(), - "5mblast" => record!.stats.piecesPlaced.toString(), - _ => "but god said" - }, textAlign: TextAlign.right, style: const TextStyle(fontSize: 21)), - 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") - ], - ), - ), + Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [result(), hjsdj()], ), Wrap( direction: Axis.horizontal, children: [ - for (Achievement achievement in achievements) FractionallySizedBox(widthFactor: 0.5, child: AchievementSummary(achievement: achievement)), + for (Achievement achievement in achievements) FractionallySizedBox(widthFactor: 1/((width/600).ceil()), child: AchievementSummary(achievement: achievement)), ], ), ] @@ -461,8 +476,9 @@ class RecordSummary extends StatelessWidget{ final MapEntry? closestAverage; final bool? betterThanClosestAverage; final String? rank; + final double width; - const RecordSummary({super.key, required this.record, this.betterThanRankAverage, this.closestAverage, this.old = false, this.betterThanClosestAverage, this.rank, this.hideRank = false}); + const RecordSummary({super.key, required this.record, this.betterThanRankAverage, this.closestAverage, this.old = false, this.betterThanClosestAverage, this.rank, this.hideRank = false, this.width = double.infinity}); @override Widget build(BuildContext context) { @@ -597,7 +613,7 @@ class AchievementSummary extends StatelessWidget{ if (achievement!.vt == 4) TextSpan(text: " • "), if (achievement!.vt != 5) TextSpan(text: (achievement?.pos != null && !achievement!.pos!.isNegative) ? "№ ${intf.format(achievement!.pos!+1)}" : "№ ---", style: TextStyle(color: achievement?.pos != null ? getColorOfRank(achievement!.pos!+1) : Colors.grey)), if (achievement!.vt != 5) TextSpan(text: " • ", style: TextStyle(color: achievement?.pos != null ? getColorOfRank(achievement!.pos!+1) : Colors.grey)), - TextSpan(text: "Top ${achievement?.pos != null ? percentage.format(achievement!.pos! / achievement!.total!) : "---%"}", style: TextStyle(color: achievement?.pos != null ? getColorOfRank(achievement!.pos!+1) : Colors.grey)), + TextSpan(text: "Top ${achievement?.pos != null ? percentagef4.format(achievement!.pos! / achievement!.total!) : "---%"}", style: TextStyle(color: achievement?.pos != null ? getColorOfRank(achievement!.pos!+1) : Colors.grey)), ] ), ), @@ -1134,12 +1150,12 @@ class _DestinationHomeState extends State with SingleTickerProv CardMod.exRecords => getListOfRecords("zenithex/recent", "zenithex/top", widget.constraints), }, Cards.sprint => switch (cardMod){ - CardMod.info => RecordCard(snapshot.data?.summaries!.sprint, sprintAchievements, sprintBetterThanRankAverage, closestAverageSprint, sprintBetterThanClosestAverage, snapshot.data!.summaries!.league.rank), + CardMod.info => RecordCard(snapshot.data?.summaries!.sprint, sprintAchievements, sprintBetterThanRankAverage, closestAverageSprint, sprintBetterThanClosestAverage, snapshot.data!.summaries!.league.rank, width: width), CardMod.records => getListOfRecords("40l/recent", "40l/top", widget.constraints), _ => const Center(child: Text("huh?")) }, Cards.blitz => switch (cardMod){ - CardMod.info => RecordCard(snapshot.data?.summaries!.blitz, blitzAchievements, blitzBetterThanRankAverage, closestAverageBlitz, blitzBetterThanClosestAverage, snapshot.data!.summaries!.league.rank), + CardMod.info => RecordCard(snapshot.data?.summaries!.blitz, blitzAchievements, blitzBetterThanRankAverage, closestAverageBlitz, blitzBetterThanClosestAverage, snapshot.data!.summaries!.league.rank, width: width), CardMod.records => getListOfRecords("blitz/recent", "blitz/top", widget.constraints), _ => const Center(child: Text("huh?")) }, @@ -1307,22 +1323,22 @@ class _DestinationHomeState extends State with SingleTickerProv else if (snapshot.data!.player!.badstanding == true) FakeDistinguishmentThingy(badStanding: true), rigthCard(snapshot, sprintAchievements, blitzAchievements, width), if (rightCard == Cards.overview) 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)), - ) - ], - ), + 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)), + ) + ], + ), + ), if (rightCard == Cards.overview) NewsThingy(snapshot.data!.news!) ], ) diff --git a/lib/views/destination_info.dart b/lib/views/destination_info.dart index 895fc96..d107d4c 100644 --- a/lib/views/destination_info.dart +++ b/lib/views/destination_info.dart @@ -17,19 +17,20 @@ class DestinationInfo extends StatefulWidget{ class InfoCard extends StatelessWidget { final double height; + final double viewportWidth; final String assetLink; final String? assetLinkOnFocus; final String title; final String description; final void Function() onPressed; - const InfoCard({required this.height, required this.assetLink, required this.title, required this.description, this.assetLinkOnFocus, required this.onPressed}); + const InfoCard({required this.height, this.viewportWidth = double.infinity, required this.assetLink, required this.title, required this.description, this.assetLinkOnFocus, required this.onPressed}); @override Widget build(BuildContext context) { return Card( clipBehavior: Clip.hardEdge, - child: SizedBox( + child: viewportWidth > 768.0 ? SizedBox( width: 450, height: height, child: Column( @@ -43,6 +44,26 @@ class InfoCard extends StatelessWidget { Spacer() ], ), + ) : SizedBox( + width: viewportWidth, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Image.asset(assetLink, fit: BoxFit.cover, width: 200.0), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TextButton(child: Text(title, style: Theme.of(context).textTheme.titleLarge!.copyWith(decoration: TextDecoration.underline, decorationColor: Colors.white70, decorationStyle: TextDecorationStyle.dotted, fontSize: 28), textAlign: TextAlign.center), onPressed: onPressed), + Padding( + padding: const EdgeInsets.all(12.0), + child: Text(description), + ), + ], + ), + ), + ], + ), ), ); } @@ -52,6 +73,43 @@ class InfoCard extends StatelessWidget { class _DestinationInfo extends State { @override Widget build(BuildContext context) { + List cards = [ + InfoCard( + height: widget.constraints.maxHeight - 77, + viewportWidth: widget.constraints.maxWidth, + assetLink: "res/images/info card 1 focus.png", + title: "40 Lines & Blitz Averages", + description: "Since calculating 40 Lines & Blitz averages is tedious process, it gets updated only once in a while. Click on the title of this card to see the full 40 Lines & Blitz averages table\n\n${t.sprintAndBlitsRelevance(date: DateFormat.yMMMd(LocaleSettings.currentLocale.languageCode).format(sprintAndBlitzRelevance))}", + onPressed: (){ + Navigator.push(context, MaterialPageRoute( + builder: (context) => SprintAndBlitzView(), + )); + } + ), + InfoCard( + height: widget.constraints.maxHeight - 77, + viewportWidth: widget.constraints.maxWidth, + assetLink: "res/images/Снимок экрана_2023-11-06_01-00-50.png", + title: "Tetra Stats Wiki", + description: "Find more information about Tetra Stats functions and statictic, that it provides", + onPressed: (){ + launchInBrowser(Uri.https("github.com", "dan63047/TetraStats/wiki")); + } + ), + InfoCard( + height: widget.constraints.maxHeight - 77, + viewportWidth: widget.constraints.maxWidth, + assetLink: "res/images/Снимок экрана_2023-11-06_01-00-50.png", + title: "About Tetra Stats", + description: "Developed by dan63\n", + onPressed: (){ + Navigator.push(context, MaterialPageRoute( + builder: (context) => AboutView(), + )); + }, + ), + ]; + return Column( mainAxisSize: MainAxisSize.min, children: [ @@ -60,41 +118,7 @@ class _DestinationInfo extends State { ), SingleChildScrollView( scrollDirection: Axis.horizontal, - child: Row( - children: [ - InfoCard( - height: widget.constraints.maxHeight - 77, - assetLink: "res/images/info card 1 focus.png", - title: "40 Lines & Blitz Averages", - description: "Since calculating 40 Lines & Blitz averages is tedious process, it gets updated only once in a while. Click on the title of this card to see the full 40 Lines & Blitz averages table\n\n${t.sprintAndBlitsRelevance(date: DateFormat.yMMMd(LocaleSettings.currentLocale.languageCode).format(sprintAndBlitzRelevance))}", - onPressed: (){ - Navigator.push(context, MaterialPageRoute( - builder: (context) => SprintAndBlitzView(), - )); - } - ), - InfoCard( - height: widget.constraints.maxHeight - 77, - assetLink: "res/images/Снимок экрана_2023-11-06_01-00-50.png", - title: "Tetra Stats Wiki", - description: "Find more information about Tetra Stats functions and statictic, that it provides", - onPressed: (){ - launchInBrowser(Uri.https("github.com", "dan63047/TetraStats/wiki")); - } - ), - InfoCard( - height: widget.constraints.maxHeight - 77, - assetLink: "res/images/Снимок экрана_2023-11-06_01-00-50.png", - title: "About Tetra Stats", - description: "Developed by dan63\n", - onPressed: (){ - Navigator.push(context, MaterialPageRoute( - builder: (context) => AboutView(), - )); - }, - ), - ], - ), + child: widget.constraints.maxWidth > 768.0 ? Row(children: cards) : Column(children: cards), ) ], ); diff --git a/lib/views/destination_leaderboards.dart b/lib/views/destination_leaderboards.dart index d354ba8..58662cd 100644 --- a/lib/views/destination_leaderboards.dart +++ b/lib/views/destination_leaderboards.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:ffi'; import 'dart:math'; import 'package:flutter/material.dart'; import 'package:tetra_stats/data_objects/tetrio_constants.dart'; @@ -12,13 +13,16 @@ import 'package:tetra_stats/widgets/future_error.dart'; class DestinationLeaderboards extends StatefulWidget{ final BoxConstraints constraints; + final bool noSidebar; - const DestinationLeaderboards({super.key, required this.constraints}); + const DestinationLeaderboards({super.key, required this.constraints, required this.noSidebar}); @override State createState() => _DestinationLeaderboardsState(); } +const double transformThreshold = 768.0; + enum Leaderboards{ tl, fullTL, @@ -116,14 +120,220 @@ class _DestinationLeaderboardsState extends State { }); } - static TextStyle trailingStyle = TextStyle(fontSize: 28); + Widget rightSide(double width){ + print(width); + const double eukjsakjas = 450; + TextStyle trailingStyle = TextStyle(fontSize: 28, fontFamily: width < eukjsakjas ? "Eurostile Round Condensed" : null); + return SizedBox( + width: width, + child: Card( + child: StreamBuilder>( + 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: Theme.of(context).textTheme.titleSmall, textAlign: TextAlign.center), + Wrap( + alignment: WrapAlignment.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; + _reachedTheEnd = 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; + _reachedTheEnd = false; + setState((){_fetchData();}); + }) + ), + if (_currentLb == Leaderboards.fullTL) IconButton( + color: _excludeRanks.isNotEmpty ? Theme.of(context).colorScheme.primary : null, + onPressed: (){ + showDialog(context: context, builder: (BuildContext context) { + return StatefulBuilder( + builder: (context, StateSetter setAlertState) { + return AlertDialog( + title: Text("Filter", textAlign: TextAlign.center), + content: SingleChildScrollView( + child: Column( + children: [ + CheckboxListTile(value: getTotalFilterValue(), tristate: true, title: Text("All", style: TextStyle(fontFamily: "Eurostile Round Extended")), onChanged: (value){ + setAlertState( + (){ + if (_excludeRanks.length*2 > ranks.length){ + _excludeRanks.clear(); + }else{ + _excludeRanks = List.of(ranks); + } + } + ); + }), + for(String rank in ranks.reversed) CheckboxListTile(value: _excludeRanks.indexOf(rank) == -1, onChanged: (value){ + setAlertState( + (){ + if (_excludeRanks.indexOf(rank) == -1){ + _excludeRanks.add(rank); + }else{ + _excludeRanks.remove(rank); + } + } + ); + }, title: Text(rank.toUpperCase()),) + ], + ), + ), + actions: [ + TextButton( + child: const Text("Apply"), + onPressed: () {Navigator.of(context).pop(); setState((){ + _currentLb = Leaderboards.fullTL; + list.clear(); + prisecter = null; + _fetchData();}); + } + ) + ] + ); + } + ); + }); + }, icon: Icon(Icons.filter_alt)), + if (_currentLb == Leaderboards.fullTL) IconButton( + color: _reverse ? Theme.of(context).colorScheme.primary : null, + icon: Transform.rotate(angle: _reverse ? pi : 0.0, child: Icon(Icons.filter_list)), + onPressed: (){ + setState((){ + _reverse = !_reverse; + _currentLb = Leaderboards.fullTL; + list.clear(); + prisecter = null; + _fetchData(); + }); + }, + ) + ], + ), + const Divider(), + Expanded( + child: ListView.builder( + controller: _scrollController, + itemCount: list.length, + 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)), + ), + itemBuilder: (BuildContext context, int index){ + return ListTile( + leading: Text(intf.format(index+1)), + title: Text(snapshot.data![index].username, style: TextStyle(fontSize: 22)), + 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 => switch (stat) { + Stats.tr => 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) + ], + ), + Stats.gp => Text("${intf.format(snapshot.data![index].getStatByEnum(stat))} ${chartsShortTitles[stat]}", style: trailingStyle), + Stats.gw => Text("${intf.format(snapshot.data![index].getStatByEnum(stat))} ${chartsShortTitles[stat]}", style: trailingStyle), + Stats.apm => Text("${f2.format(snapshot.data![index].getStatByEnum(stat))} ${chartsShortTitles[stat]}", style: trailingStyle), + Stats.pps => Text("${f2.format(snapshot.data![index].getStatByEnum(stat))} ${chartsShortTitles[stat]}", style: trailingStyle), + Stats.vs => Text("${f2.format(snapshot.data![index].getStatByEnum(stat))} ${chartsShortTitles[stat]}", style: trailingStyle), + _ => Text("${f4.format(snapshot.data![index].getStatByEnum(stat))} ${chartsShortTitles[stat]}", style: trailingStyle) + }, + 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) + }, + subtitle: width >= eukjsakjas ? 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", + Leaderboards.fullTL => switch (stat) { + Stats.tr => "${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", + _ => "${f2.format(snapshot.data![index].tr)} TR, ${snapshot.data![index].rank.toUpperCase()} rank" + }, + 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}", + 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", + 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)) : null, + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => UserView(searchFor: snapshot.data![index].userId), + maintainState: false, + ), + ); + }, + ); + } + ), + ), + ], + ); + } + if (snapshot.hasError){ return FutureError(snapshot); } + } + return Text("huh?"); + }, + ), + ), + ); + } @override Widget build(BuildContext context) { return Row( children: [ SizedBox( - width: 350.0, + width: widget.constraints.maxWidth > transformThreshold ? 300.0 : widget.constraints.maxWidth, height: widget.constraints.maxHeight, child: Column( children: [ @@ -132,7 +342,7 @@ class _DestinationLeaderboardsState extends State { mainAxisSize: MainAxisSize.min, children: [ Spacer(), - Text("Leaderboards", style: Theme.of(context).textTheme.headlineMedium), + Text("Leaderboards", style: Theme.of(context).textTheme.headlineMedium!.copyWith(fontSize: 32)), Spacer() ], ), @@ -147,6 +357,26 @@ class _DestinationLeaderboardsState extends State { trailing: Icon(Icons.arrow_right, color: _currentLb.index == index ? Colors.white : Colors.grey), subtitle: index == 1 ? Text("Heavy, but allows you to sort players by their stats and filter them by ranks", style: TextStyle(color: Colors.grey, fontSize: 12)) : null, onTap: () { + if (widget.constraints.maxWidth <= transformThreshold) Navigator.push( + context, + MaterialPageRoute( + builder: (context) => Scaffold( + floatingActionButtonLocation: FloatingActionButtonLocation.endFloat, + floatingActionButton: Padding( + padding: const EdgeInsets.fromLTRB(0.0, 12.0, 0.0, 0.0), + child: FloatingActionButton( + onPressed: () => Navigator.pop(context), + tooltip: 'Fuck go back', + child: const Icon(Icons.arrow_back), + ), + ), + body: SafeArea( + child: rightSide(widget.constraints.maxWidth) + ) + ), + maintainState: false, + ), + ); _currentLb = leaderboards.keys.elementAt(index); list.clear(); prisecter = null; @@ -161,208 +391,7 @@ class _DestinationLeaderboardsState extends State { ], ), ), - SizedBox( - width: widget.constraints.maxWidth - 350 - 88, - child: Card( - child: StreamBuilder>( - 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: Theme.of(context).textTheme.titleSmall), - 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; - _reachedTheEnd = 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; - _reachedTheEnd = false; - setState((){_fetchData();}); - }) - ), - if (_currentLb == Leaderboards.fullTL) IconButton( - color: _excludeRanks.isNotEmpty ? Theme.of(context).colorScheme.primary : null, - onPressed: (){ - showDialog(context: context, builder: (BuildContext context) { - return StatefulBuilder( - builder: (context, StateSetter setAlertState) { - return AlertDialog( - title: Text("Filter", textAlign: TextAlign.center), - content: SingleChildScrollView( - child: Column( - children: [ - CheckboxListTile(value: getTotalFilterValue(), tristate: true, title: Text("All", style: TextStyle(fontFamily: "Eurostile Round Extended")), onChanged: (value){ - setAlertState( - (){ - if (_excludeRanks.length*2 > ranks.length){ - _excludeRanks.clear(); - }else{ - _excludeRanks = List.of(ranks); - } - } - ); - }), - for(String rank in ranks.reversed) CheckboxListTile(value: _excludeRanks.indexOf(rank) == -1, onChanged: (value){ - setAlertState( - (){ - if (_excludeRanks.indexOf(rank) == -1){ - _excludeRanks.add(rank); - }else{ - _excludeRanks.remove(rank); - } - } - ); - }, title: Text(rank.toUpperCase()),) - ], - ), - ), - actions: [ - TextButton( - child: const Text("Apply"), - onPressed: () {Navigator.of(context).pop(); setState((){ - _currentLb = Leaderboards.fullTL; - list.clear(); - prisecter = null; - _fetchData();}); - } - ) - ] - ); - } - ); - }); - }, icon: Icon(Icons.filter_alt)), - if (_currentLb == Leaderboards.fullTL) IconButton( - color: _reverse ? Theme.of(context).colorScheme.primary : null, - icon: Transform.rotate(angle: _reverse ? pi : 0.0, child: Icon(Icons.filter_list)), - onPressed: (){ - setState((){ - _reverse = !_reverse; - _currentLb = Leaderboards.fullTL; - list.clear(); - prisecter = null; - _fetchData(); - }); - }, - ) - ], - ), - const Divider(), - Expanded( - child: ListView.builder( - controller: _scrollController, - itemCount: list.length, - 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)), - ), - itemBuilder: (BuildContext context, int index){ - return ListTile( - leading: Text(intf.format(index+1)), - title: Text(snapshot.data![index].username, style: TextStyle(fontSize: 22)), - 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 => switch (stat) { - Stats.tr => 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) - ], - ), - Stats.gp => Text("${intf.format(snapshot.data![index].getStatByEnum(stat))} ${chartsShortTitles[stat]}", style: trailingStyle), - Stats.gw => Text("${intf.format(snapshot.data![index].getStatByEnum(stat))} ${chartsShortTitles[stat]}", style: trailingStyle), - Stats.apm => Text("${f2.format(snapshot.data![index].getStatByEnum(stat))} ${chartsShortTitles[stat]}", style: trailingStyle), - Stats.pps => Text("${f2.format(snapshot.data![index].getStatByEnum(stat))} ${chartsShortTitles[stat]}", style: trailingStyle), - Stats.vs => Text("${f2.format(snapshot.data![index].getStatByEnum(stat))} ${chartsShortTitles[stat]}", style: trailingStyle), - _ => Text("${f4.format(snapshot.data![index].getStatByEnum(stat))} ${chartsShortTitles[stat]}", style: trailingStyle) - }, - 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) - }, - 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", - Leaderboards.fullTL => switch (stat) { - Stats.tr => "${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", - _ => "${f2.format(snapshot.data![index].tr)} TR, ${snapshot.data![index].rank.toUpperCase()} rank" - }, - 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}", - 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", - 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)), - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => UserView(searchFor: snapshot.data![index].userId), - maintainState: false, - ), - ); - }, - ); - } - ), - ), - ], - ); - } - if (snapshot.hasError){ return FutureError(snapshot); } - } - return Text("huh?"); - }, - ), - ), - ), + if (widget.constraints.maxWidth > transformThreshold) rightSide(widget.constraints.maxWidth - 300 - (widget.noSidebar ? 0 : 88)), ], ); } diff --git a/lib/views/destination_saved_data.dart b/lib/views/destination_saved_data.dart index eed34c1..ed4759d 100644 --- a/lib/views/destination_saved_data.dart +++ b/lib/views/destination_saved_data.dart @@ -58,6 +58,69 @@ class _DestinationSavedData extends State { ); } + Widget rightSide(double width, bool hasSidebar){ + return SizedBox( + width: width - (hasSidebar ? 80.0 : 0.00), + child: selectedID != null ? FutureBuilder<(List, List, List)>( + future: getDataAbout(selectedID!), + 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.hasError){ return FutureError(snapshot); } + if (snapshot.hasData){ + return DefaultTabController( + length: 3, + child: Card( + child: Column( + children: [ + Card( + child: TabBar( + labelStyle: Theme.of(context).textTheme.titleMedium!.copyWith(fontSize: 28), + labelColor: Theme.of(context).colorScheme.primary, + tabs: [ + Tab(text: "S${currentSeason} TL States"), + Tab(text: "S1 TL States"), + Tab(text: "TL Records") + ]), + ), + SizedBox( + height: widget.constraints.maxHeight - 64, + child: TabBarView(children: [ + ListView.builder( + itemCount: snapshot.data!.$1.length, + itemBuilder: (context, index) { + return getTetraLeagueListTile(snapshot.data!.$1[index]); + },), + ListView.builder( + itemCount: snapshot.data!.$2.length, + itemBuilder: (context, index) { + return getTetraLeagueListTile(snapshot.data!.$2[index]); + },), + ListView.builder( + itemCount: snapshot.data!.$3.length, + itemBuilder: (context, index) { + return AlphaLeagueEntryThingy(snapshot.data!.$3[index], selectedID!); + },), + ] + ), + ) + ], + ), + ), + ); + } + return Text("what?"); + } + } + ) : + InfoThingy("Select nickname on the left to see data assosiated with it") + ); + } + @override Widget build(BuildContext context) { return FutureBuilder>( @@ -74,7 +137,7 @@ class _DestinationSavedData extends State { return Row( children: [ SizedBox( - width: 450, + width: widget.constraints.maxWidth > 900.0 ? 350 : widget.constraints.maxWidth - (widget.constraints.maxWidth <= 768.0 ? 0 : 80), child: Column( children: [ Card( @@ -93,72 +156,33 @@ class _DestinationSavedData extends State { //subtitle: Text("NaN states, NaN TL records", style: TextStyle(color: Colors.grey)), onTap: () => setState(() { selectedID = id; + if (widget.constraints.maxWidth <= 900.0) Navigator.push( + context, + MaterialPageRoute( + builder: (context) => Scaffold( + floatingActionButtonLocation: FloatingActionButtonLocation.endFloat, + floatingActionButton: Padding( + padding: const EdgeInsets.fromLTRB(0.0, 12.0, 0.0, 0.0), + child: FloatingActionButton( + onPressed: () => Navigator.pop(context), + tooltip: 'Fuck go back', + child: const Icon(Icons.arrow_back), + ), + ), + body: SafeArea( + child: rightSide(widget.constraints.maxWidth, false) + ) + ), + maintainState: false, + ), + ); }), ), ) ], ), ), - SizedBox( - width: widget.constraints.maxWidth - 450 - 80, - child: selectedID != null ? FutureBuilder<(List, List, List)>( - future: getDataAbout(selectedID!), - 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.hasError){ return FutureError(snapshot); } - if (snapshot.hasData){ - return DefaultTabController( - length: 3, - child: Card( - child: Column( - children: [ - Card( - child: TabBar( - labelStyle: Theme.of(context).textTheme.titleMedium!.copyWith(fontSize: 28), - labelColor: Theme.of(context).colorScheme.primary, - tabs: [ - Tab(text: "S${currentSeason} TL States"), - Tab(text: "S1 TL States"), - Tab(text: "TL Records") - ]), - ), - SizedBox( - height: widget.constraints.maxHeight - 64, - child: TabBarView(children: [ - ListView.builder( - itemCount: snapshot.data!.$1.length, - itemBuilder: (context, index) { - return getTetraLeagueListTile(snapshot.data!.$1[index]); - },), - ListView.builder( - itemCount: snapshot.data!.$2.length, - itemBuilder: (context, index) { - return getTetraLeagueListTile(snapshot.data!.$2[index]); - },), - ListView.builder( - itemCount: snapshot.data!.$3.length, - itemBuilder: (context, index) { - return AlphaLeagueEntryThingy(snapshot.data!.$3[index], selectedID!); - },), - ] - ), - ) - ], - ), - ), - ); - } - return Text("what?"); - } - } - ) : - InfoThingy("Select nickname on the left to see data assosiated with it") - ) + if (widget.constraints.maxWidth > 900.0) rightSide(widget.constraints.maxWidth - 350, true) ], ); } diff --git a/lib/views/destination_settings.dart b/lib/views/destination_settings.dart index 935bfac..e64602e 100644 --- a/lib/views/destination_settings.dart +++ b/lib/views/destination_settings.dart @@ -607,6 +607,19 @@ class _DestinationSettings extends State with SingleTickerP ); } + Widget rightSide(double width, bool hasSidebar){ + return SizedBox( + width: width - (hasSidebar ? 80 : 0), + child: SingleChildScrollView( + child: switch (mod){ + SettingsCardMod.general => getGeneralSettings(), + SettingsCardMod.customization => getCustomizationSettings(), + SettingsCardMod.database => getDatabaseSettings(), + }, + ) + ); + } + @override Widget build(BuildContext context) { final t = Translations.of(context); @@ -618,7 +631,7 @@ class _DestinationSettings extends State with SingleTickerP crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( - width: 450, + width: widget.constraints.maxWidth > 900.0 ? 350 : widget.constraints.maxWidth - (widget.constraints.maxWidth <= 768.0 ? 0 : 80), child: Column( children: [ Card( @@ -639,22 +652,33 @@ class _DestinationSettings extends State with SingleTickerP setState(() { mod = m; }); + if (widget.constraints.maxWidth <= 900.0) Navigator.push( + context, + MaterialPageRoute( + builder: (context) => Scaffold( + floatingActionButtonLocation: FloatingActionButtonLocation.endFloat, + floatingActionButton: Padding( + padding: const EdgeInsets.fromLTRB(0.0, 12.0, 0.0, 0.0), + child: FloatingActionButton( + onPressed: () => Navigator.pop(context), + tooltip: 'Fuck go back', + child: const Icon(Icons.arrow_back), + ), + ), + body: SafeArea( + child: rightSide(widget.constraints.maxWidth, false) + ) + ), + maintainState: false, + ), + ); }, ), ) ], ), ), - SizedBox( - width: widget.constraints.maxWidth - 450 - 80, - child: SingleChildScrollView( - child: switch (mod){ - SettingsCardMod.general => getGeneralSettings(), - SettingsCardMod.customization => getCustomizationSettings(), - SettingsCardMod.database => getDatabaseSettings(), - }, - ) - ) + if (widget.constraints.maxWidth > 900.0) rightSide(widget.constraints.maxWidth - 350, true) ], ); } diff --git a/lib/views/main_view.dart b/lib/views/main_view.dart index 6efd434..216f00f 100644 --- a/lib/views/main_view.dart +++ b/lib/views/main_view.dart @@ -27,6 +27,10 @@ late Future _data; TetrioPlayersLeaderboard? _everyone; int destination = 0; +// TODO: Redesign some widgets, so they could look nice on mobile view +// - stats below TL progress bar & similar parts in other widgets +// - APP and VS/APM gadget + Future getData(String searchFor) async { TetrioPlayer player; try{ @@ -160,6 +164,102 @@ class _MainState extends State with TickerProviderStateMixin { ); } + Widget pickers(int destination){ + return switch (destination) { + 0 => Column( + children: [ + SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: SegmentedButton( + showSelectedIcon: false, + selected: {cardMod}, + segments: modeButtons[rightCard]!, + onSelectionChanged: (p0) { + setState(() { + cardMod = p0.first; + }); + }, + ), + ), + SegmentedButton( + showSelectedIcon: false, + segments: >[ + const ButtonSegment( + value: Cards.overview, + tooltip: 'Overview', + icon: Icon(Icons.calendar_view_day)), + ButtonSegment( + value: Cards.tetraLeague, + tooltip: 'Tetra League', + icon: SvgPicture.asset("res/icons/league.svg", height: 16, colorFilter: ColorFilter.mode(theme.colorScheme.primary, BlendMode.modulate))), + ButtonSegment( + value: Cards.quickPlay, + tooltip: 'Quick Play', + icon: SvgPicture.asset("res/icons/qp.svg", height: 16, colorFilter: ColorFilter.mode(theme.colorScheme.primary, BlendMode.modulate))), + ButtonSegment( + value: Cards.sprint, + tooltip: '40 Lines', + icon: SvgPicture.asset("res/icons/40l.svg", height: 16, colorFilter: ColorFilter.mode(theme.colorScheme.primary, BlendMode.modulate))), + ButtonSegment( + value: Cards.blitz, + tooltip: 'Blitz', + icon: SvgPicture.asset("res/icons/blitz.svg", height: 16, colorFilter: ColorFilter.mode(theme.colorScheme.primary, BlendMode.modulate))), + ], + selected: {rightCard}, + onSelectionChanged: (Set newSelection) { + setState(() { + cardMod = CardMod.info; + rightCard = newSelection.first; + });}) + ], + ), + 1 => SegmentedButton( + showSelectedIcon: false, + segments: >[ + const ButtonSegment( + value: Graph.history, + label: Text('Player History')), + ButtonSegment( + value: Graph.leagueState, + label: Text('League State')), + ButtonSegment( + value: Graph.leagueCutoffs, + label: Text('League Cutoffs'), + ), + ], + selected: {graph}, + onSelectionChanged: (Set newSelection) { + setState(() { + graph = newSelection.first; + switch (newSelection.first){ + case Graph.leagueCutoffs: + case Graph.history: + Ychart = Stats.tr; + case Graph.leagueState: + Ychart = Stats.apm; + } + });}), + 4 => SegmentedButton( + showSelectedIcon: false, + segments: >[ + const ButtonSegment( + value: CalcCards.calc, + label: Text('Stats Calculator'), + ), + ButtonSegment( + value: CalcCards.damage, + label: Text('Damage Calculator'), + ), + ], + selected: {calcCard}, + onSelectionChanged: (Set newSelection) { + setState(() { + calcCard = newSelection.first; + });}), + _ => Container() + }; + } + @override Widget build(BuildContext context) { return LayoutBuilder( @@ -185,58 +285,10 @@ class _MainState extends State with TickerProviderStateMixin { }, ), Expanded( - child: Column( - children: [ - SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: SegmentedButton( - showSelectedIcon: false, - selected: {cardMod}, - segments: modeButtons[rightCard]!, - onSelectionChanged: (p0) { - setState(() { - cardMod = p0.first; - }); - }, - ), - ), - SegmentedButton( - showSelectedIcon: false, - segments: >[ - const ButtonSegment( - value: Cards.overview, - tooltip: 'Overview', - icon: Icon(Icons.calendar_view_day)), - ButtonSegment( - value: Cards.tetraLeague, - tooltip: 'Tetra League', - icon: SvgPicture.asset("res/icons/league.svg", height: 16, colorFilter: ColorFilter.mode(theme.colorScheme.primary, BlendMode.modulate))), - ButtonSegment( - value: Cards.quickPlay, - tooltip: 'Quick Play', - icon: SvgPicture.asset("res/icons/qp.svg", height: 16, colorFilter: ColorFilter.mode(theme.colorScheme.primary, BlendMode.modulate))), - ButtonSegment( - value: Cards.sprint, - tooltip: '40 Lines', - icon: SvgPicture.asset("res/icons/40l.svg", height: 16, colorFilter: ColorFilter.mode(theme.colorScheme.primary, BlendMode.modulate))), - ButtonSegment( - value: Cards.blitz, - tooltip: 'Blitz', - icon: SvgPicture.asset("res/icons/blitz.svg", height: 16, colorFilter: ColorFilter.mode(theme.colorScheme.primary, BlendMode.modulate))), - ], - selected: {rightCard}, - onSelectionChanged: (Set newSelection) { - setState(() { - cardMod = CardMod.info; - rightCard = newSelection.first; - });}) - ], - ), + child: pickers(destination), ), - IconButton( - tooltip: 'Fake "Open navigation menu" button\nHere only for symmetry', - icon: const Icon(Icons.menu, color: Colors.transparent), - onPressed: () {}, + SizedBox( + width: 40.0, ), ], ), @@ -259,7 +311,7 @@ class _MainState extends State with TickerProviderStateMixin { leading: FloatingActionButton( elevation: 0, onPressed: () { - Scaffold.of(context).openDrawer(); + _scaffoldKey.currentState!.openDrawer(); }, child: const Icon(Icons.search), ), @@ -300,8 +352,8 @@ class _MainState extends State with TickerProviderStateMixin { Expanded( child: switch (destination){ 0 => DestinationHome(searchFor: _searchFor, constraints: constraints, dataFuture: _data, noSidebar: !screenIsBig), - 1 => DestinationGraphs(searchFor: _searchFor, constraints: constraints), - 2 => DestinationLeaderboards(constraints: constraints), + 1 => DestinationGraphs(searchFor: _searchFor, constraints: constraints, noSidebar: !screenIsBig), + 2 => DestinationLeaderboards(constraints: constraints, noSidebar: !screenIsBig), 3 => DestinationCutoffs(constraints: constraints), 4 => DestinationCalculator(constraints: constraints), 5 => DestinationInfo(constraints: constraints), diff --git a/res/i18n/strings.i18n.json b/res/i18n/strings.i18n.json index 6c75715..65505ed 100644 --- a/res/i18n/strings.i18n.json +++ b/res/i18n/strings.i18n.json @@ -84,7 +84,7 @@ "comparingWith": "Data from ${newDate} comparing with ${oldDate}", "top": "Top", "topRank": "Top rank", - "verdictGeneral": "$n $verdict than $rank rank average", + "verdictGeneral": "$n $verdict $rank rank avg", "verdictBetter": "better", "verdictWorse": "worse", "smooth": "Smooth",