First time experience thougths + some stuff
This commit is contained in:
parent
6c8e7b9147
commit
1b9249f36b
|
@ -46,6 +46,11 @@ class Achievement {
|
|||
this.total,
|
||||
this.rank});
|
||||
|
||||
@override
|
||||
String toString(){
|
||||
return "${name}: ${v}";
|
||||
}
|
||||
|
||||
Achievement.fromJson(Map<String, dynamic> json) {
|
||||
k = json['k'];
|
||||
o = json['o'];
|
||||
|
|
|
@ -262,6 +262,13 @@ enum ComboTables{
|
|||
multiplier
|
||||
}
|
||||
|
||||
Map<ComboTables, String> comboTablesNames = {
|
||||
ComboTables.none: "None",
|
||||
ComboTables.classic: "Classic",
|
||||
ComboTables.modern: "Modern",
|
||||
ComboTables.multiplier: "Multiplier"
|
||||
};
|
||||
|
||||
const int BACKTOBACK_BONUS = 1;
|
||||
const double BACKTOBACK_BONUS_LOG = .8;
|
||||
const int COMBO_MINIFIER = 1;
|
||||
|
|
|
@ -7,6 +7,7 @@ import 'dart:developer' as developer;
|
|||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:tetra_stats/services/tetrio_crud.dart';
|
||||
import 'package:tetra_stats/views/first_time_view.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
||||
import 'package:sqflite_common_ffi_web/sqflite_ffi_web.dart';
|
||||
|
@ -18,6 +19,8 @@ import 'package:go_router/go_router.dart';
|
|||
late final PackageInfo packageInfo;
|
||||
late SharedPreferences prefs;
|
||||
late TetrioService teto;
|
||||
late GoRouter router;
|
||||
|
||||
ThemeData theme = ThemeData(
|
||||
fontFamily: 'Eurostile Round',
|
||||
colorScheme: const ColorScheme.dark(
|
||||
|
@ -59,20 +62,6 @@ ThemeData theme = ThemeData(
|
|||
scaffoldBackgroundColor: Colors.black
|
||||
);
|
||||
|
||||
final router = GoRouter(
|
||||
initialLocation: "/",
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: "/",
|
||||
builder: (_, __) => const MainView(),
|
||||
),
|
||||
GoRoute( // that one intended for Android users, that can open https://ch.tetr.io/u/ links
|
||||
path: "/u/:userId",
|
||||
builder: (_, __) => MainView(player: __.pathParameters['userId'])
|
||||
)
|
||||
],
|
||||
);
|
||||
|
||||
void main() async {
|
||||
// Initializing sqflite
|
||||
if (kIsWeb) {
|
||||
|
@ -96,6 +85,25 @@ void main() async {
|
|||
prefs = await SharedPreferences.getInstance();
|
||||
teto = TetrioService();
|
||||
|
||||
router = GoRouter(
|
||||
//initialLocation: prefs.getBool("notFirstTime") == true ? "/" : "/hihello",
|
||||
initialLocation: "/",
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: "/",
|
||||
builder: (_, __) => const MainView(),
|
||||
),
|
||||
GoRoute( // that one intended for Android users, that can open https://ch.tetr.io/u/ links
|
||||
path: "/u/:userId",
|
||||
builder: (_, __) => MainView(player: __.pathParameters['userId'])
|
||||
),
|
||||
GoRoute(
|
||||
path: "/hihello",
|
||||
builder: (_, __) => const FirstTimeView(),
|
||||
)
|
||||
],
|
||||
);
|
||||
|
||||
// Choosing the locale
|
||||
String? locale = prefs.getString("locale");
|
||||
if (locale == null){
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
Color getColorOfRank(int rank){
|
||||
if (rank < 1) return Colors.grey;
|
||||
if (rank == 1) return Colors.yellowAccent;
|
||||
if (rank == 2) return Colors.blueGrey;
|
||||
if (rank == 3) return Colors.brown[400]!;
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
import 'dart:io';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:tetra_stats/gen/strings.g.dart';
|
||||
import 'package:tetra_stats/main.dart';
|
||||
import 'package:tetra_stats/utils/open_in_browser.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
|
||||
late String oldWindowTitle;
|
||||
final DateFormat dateFormat = DateFormat.yMMMd(LocaleSettings.currentLocale.languageCode);
|
||||
|
||||
class AboutView extends StatefulWidget {
|
||||
const AboutView({super.key});
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => AboutState();
|
||||
}
|
||||
|
||||
class AboutCard extends StatelessWidget{
|
||||
final String title;
|
||||
final String value;
|
||||
final String? undervalue; //what?
|
||||
final List<InlineSpan> endvalue; // ...
|
||||
|
||||
const AboutCard(this.title, this.value, this.undervalue, this.endvalue);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Card(child: Column(
|
||||
children: [
|
||||
Text(title, style: Theme.of(context).textTheme.titleSmall),
|
||||
Divider(),
|
||||
Text(value, style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 36, fontWeight: FontWeight.w500, color: Colors.white)),
|
||||
if (undervalue != null) Text(undervalue!),
|
||||
Divider(),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8.0),
|
||||
child: RichText(text: TextSpan(
|
||||
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, color: Colors.grey, height: 0.6),
|
||||
children: endvalue
|
||||
)),
|
||||
)
|
||||
],
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
class AboutState extends State<AboutView> {
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
if (!kIsWeb && !Platform.isAndroid && !Platform.isIOS){
|
||||
windowManager.getTitle().then((value) => oldWindowTitle = value);
|
||||
windowManager.setTitle("Tetra Stats: ${t.settings}");
|
||||
}
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose(){
|
||||
if (!kIsWeb && !Platform.isAndroid && !Platform.isIOS) windowManager.setTitle(oldWindowTitle);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final t = Translations.of(context);
|
||||
bool bigScreen = MediaQuery.of(context).size.width >= 368;
|
||||
return Scaffold(
|
||||
floatingActionButtonLocation: FloatingActionButtonLocation.startTop,
|
||||
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),
|
||||
),
|
||||
),
|
||||
backgroundColor: Colors.black,
|
||||
body: SafeArea(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Card(child: Center(child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(0.0, 6.0, 0.0, 18.0),
|
||||
child: Text("About Tetra Stats", style: Theme.of(context).textTheme.titleLarge, textAlign: TextAlign.center),
|
||||
))),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Card(child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
textAlign: TextAlign.center,
|
||||
"Tetra Stats is a service, that works with TETR.IO Tetra Channel API, providing data from it and calculating some addtitional metrics, based on this data. Service allows user to track their progress in Tetra League with \"Track\" function, which records every Tetra League change into local database (not automatically, you have to visit service from time to time), so these changes could be looked through graphs.\n\nBeanserver blaster is a part of a Tetra Stats, that decoupled into a serverside script. It provides full Tetra League leaderboard, allowing Tetra Stats to sort leaderboard by any metric and build scatter chart, that allows user to analyse Tetra League trends. It also provides history of Tetra League ranks cutoffs, which can be viewed by user via graph as well.\n\nThere is a plans to add replay analysis and tournaments history, so stay tuned!\n\nService is not associated with TETR.IO or osk in any capacity."
|
||||
),
|
||||
)
|
||||
],
|
||||
)),
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
children: [
|
||||
AboutCard("App Version", packageInfo.version, "Build ${packageInfo.buildNumber}", [TextSpan(text: "${packageInfo.appName} (${packageInfo.packageName}) • "), TextSpan(text: "GitHub Repo", style: TextStyle(decoration: TextDecoration.underline, decorationColor: Colors.white70, decorationStyle: TextDecorationStyle.dotted, color: Theme.of(context).colorScheme.primary), recognizer: TapGestureRecognizer()..onTap = (){
|
||||
launchInBrowser(Uri.https("github.com", "dan63047/TetraStats"));
|
||||
})]),
|
||||
AboutCard("Developed By", "dan63", null, [TextSpan(text: "Support him!", style: TextStyle(decoration: TextDecoration.underline, decorationColor: Colors.white70, decorationStyle: TextDecorationStyle.dotted, color: Theme.of(context).colorScheme.primary), recognizer: TapGestureRecognizer()..onTap = (){launchInBrowser(Uri.https("dan63.by", "donate"));})]),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
)),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -665,7 +665,7 @@ class CompareState extends State<CompareView> {
|
|||
|
||||
void getSummariesForInit() async {
|
||||
summaries.add(await teto.fetchSummaries(widget.initPlayer.userId));
|
||||
if (summaries[0].league.nerdStats != null) nicknames.add(players[0].username);
|
||||
nicknames.add(players[0].username);
|
||||
addvaluesEntrys(players.first, summaries.first);
|
||||
best = recalculateBestEntries();
|
||||
setState(() {
|
||||
|
@ -678,7 +678,7 @@ class CompareState extends State<CompareView> {
|
|||
summaries.add(await teto.fetchSummaries(players.last.userId));
|
||||
addvaluesEntrys(players.last, summaries.last);
|
||||
best = recalculateBestEntries();
|
||||
if (summaries.last.league.nerdStats != null) nicknames.add(players.last.username);
|
||||
nicknames.add(players.last.username);
|
||||
setState(() {
|
||||
|
||||
});
|
||||
|
@ -688,6 +688,7 @@ class CompareState extends State<CompareView> {
|
|||
int id = players.indexWhere((e) => e.username == nickname);
|
||||
players.removeAt(id);
|
||||
summaries.removeAt(id);
|
||||
nicknames.remove(nickname);
|
||||
for (int i = 0; i < 7; i++){
|
||||
rawValues[i].removeAt(id);
|
||||
formattedValues[i].removeAt(id);
|
||||
|
@ -786,7 +787,9 @@ class CompareState extends State<CompareView> {
|
|||
),
|
||||
]
|
||||
),
|
||||
//VsGraphs(stats: [for (var s in summaries) if (s.league.nerdStats != null) AggregateStats.precalculated(s.league.apm!, s.league.pps!, s.league.vs!, s.league.nerdStats!, s.league.playstyle!)], nicknames: nicknames)
|
||||
if (i == 1) VsGraphs(stats: [for (var s in summaries) if (s.league.nerdStats != null) AggregateStats.precalculated(s.league.apm!, s.league.pps!, s.league.vs!, s.league.nerdStats!, s.league.playstyle!)], nicknames: [for (int i = 0; i < summaries.length; i++) if (summaries[i].league.nerdStats != null) nicknames[i]]),
|
||||
if (i == 2) VsGraphs(stats: [for (var s in summaries) if ((s.zenith != null || s.zenithCareerBest != null) && (s.zenith?.aggregateStats??s.zenithCareerBest!.aggregateStats).apm > 0.00) s.zenith?.aggregateStats??s.zenithCareerBest!.aggregateStats], nicknames: [for (int i = 0; i < summaries.length; i++) if ((summaries[i].zenith != null || summaries[i].zenithCareerBest != null) && (summaries[i].zenith?.aggregateStats??summaries[i].zenithCareerBest!.aggregateStats).apm > 0.00) nicknames[i]]),
|
||||
if (i == 3) VsGraphs(stats: [for (var s in summaries) if ((s.zenithEx != null || s.zenithExCareerBest != null) && (s.zenithEx?.aggregateStats??s.zenithExCareerBest!.aggregateStats).apm > 0.00) s.zenithEx?.aggregateStats??s.zenithExCareerBest!.aggregateStats], nicknames: [for (int i = 0; i < summaries.length; i++) if ((summaries[i].zenithEx != null || summaries[i].zenithExCareerBest != null) && (summaries[i].zenithEx?.aggregateStats??summaries[i].zenithExCareerBest!.aggregateStats).apm > 0.00) nicknames[i]]),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
@ -388,7 +388,9 @@ class _DestinationCalculatorState extends State<DestinationCalculator> {
|
|||
child: Column(
|
||||
children: [
|
||||
Card(
|
||||
child: ListTile(
|
||||
child: Column(
|
||||
children: [
|
||||
ListTile(
|
||||
title: Text("Multiplier", style: mainToggleInRules),
|
||||
trailing: SizedBox(width: 90.0, child: TextField(
|
||||
keyboardType: TextInputType.number,
|
||||
|
@ -397,6 +399,17 @@ class _DestinationCalculatorState extends State<DestinationCalculator> {
|
|||
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(
|
||||
|
@ -408,7 +421,7 @@ class _DestinationCalculatorState extends State<DestinationCalculator> {
|
|||
if (rules.combo) ListTile(
|
||||
title: Text("Combo Table"),
|
||||
trailing: DropdownButton(
|
||||
items: [for (var v in ComboTables.values) DropdownMenuItem(value: v.index, child: Text(v.name))],
|
||||
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!];}),
|
||||
),
|
||||
|
@ -509,7 +522,7 @@ class _DestinationCalculatorState extends State<DestinationCalculator> {
|
|||
Text("Combo: ${intf.format(comboDamage)}"),
|
||||
Text("B2B: ${intf.format(b2bDamage)}"),
|
||||
Text("Surge: ${intf.format(surgeDamage)}"),
|
||||
Text("PC's: ${intf.format(pcDamage)}")
|
||||
Text("PCs: ${intf.format(pcDamage)}")
|
||||
],
|
||||
),
|
||||
if (totalDamage > 0) SfLinearGauge(
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:tetra_stats/data_objects/achievement.dart';
|
||||
import 'package:tetra_stats/data_objects/cutoff_tetrio.dart';
|
||||
import 'package:tetra_stats/data_objects/news.dart';
|
||||
import 'package:tetra_stats/data_objects/p1nkl0bst3r.dart';
|
||||
|
@ -42,11 +43,10 @@ import 'package:tetra_stats/widgets/zenith_thingy.dart';
|
|||
class DestinationHome extends StatefulWidget{
|
||||
final String searchFor;
|
||||
final Future<FetchResults> dataFuture;
|
||||
final Future<News>? newsFuture;
|
||||
final BoxConstraints constraints;
|
||||
final bool noSidebar;
|
||||
|
||||
const DestinationHome({super.key, required this.searchFor, required this.dataFuture, this.newsFuture, required this.constraints, this.noSidebar = false});
|
||||
const DestinationHome({super.key, required this.searchFor, required this.dataFuture, required this.constraints, this.noSidebar = false});
|
||||
|
||||
@override
|
||||
State<DestinationHome> createState() => _DestinationHomeState();
|
||||
|
@ -57,13 +57,14 @@ class FetchResults{
|
|||
TetrioPlayer? player;
|
||||
List<TetraLeague> states;
|
||||
Summaries? summaries;
|
||||
News? news;
|
||||
Cutoffs? cutoffs;
|
||||
CutoffsTetrio? averages;
|
||||
PlayerLeaderboardPosition? playerPos;
|
||||
bool isTracked;
|
||||
Exception? exception;
|
||||
|
||||
FetchResults(this.success, this.player, this.states, this.summaries, this.cutoffs, this.averages, this.playerPos, this.isTracked, this.exception);
|
||||
FetchResults(this.success, this.player, this.states, this.summaries, this.news, this.cutoffs, this.averages, this.playerPos, this.isTracked, this.exception);
|
||||
}
|
||||
|
||||
class RecordSummary extends StatelessWidget{
|
||||
|
@ -139,6 +140,93 @@ class RecordSummary extends StatelessWidget{
|
|||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AchievementSummary extends StatelessWidget{
|
||||
final Achievement? achievement;
|
||||
|
||||
const AchievementSummary({this.achievement});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Card(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(20.0, 8.0, 20.0, 12.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(achievement?.name??"---", style: Theme.of(context).textTheme.titleSmall, textAlign: TextAlign.center),
|
||||
const Divider(),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 8.0),
|
||||
child: Container(
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: 512.0,
|
||||
maxHeight: 512.0,
|
||||
//minWidth: 256,
|
||||
minHeight: 64.0,
|
||||
),
|
||||
child: ClipRect(
|
||||
child: Align(
|
||||
alignment: Alignment.topLeft.add(Alignment(0.285 * (((achievement?.k??1) - 1) % 8), 0.285 * (((achievement?.k??0) - 1) / 8).floor())),
|
||||
//alignment: Alignment.topLeft.add(Alignment(0.285 * 1, 0)),
|
||||
heightFactor: 0.125,
|
||||
widthFactor: 0.125,
|
||||
child: Image.asset("res/icons/achievements.png", width: 2048, height: 2048, scale: 1),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
//ClipRect(clipper: Rect.fromLTRB(0, 0, 64, 64), child: ),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
RichText(
|
||||
textAlign: TextAlign.start,
|
||||
text: TextSpan(
|
||||
text: achievement?.v == null ? "---" : switch(achievement!.vt){
|
||||
1 => intf.format(achievement!.v),
|
||||
2 => get40lTime((achievement!.v! * 1000).floor()),
|
||||
3 => get40lTime((achievement!.v!.abs() * 1000).floor()),
|
||||
4 => "${f2.format(achievement!.v!)} m",
|
||||
5 => "№ ${intf.format(achievement!.pos!+1)}",
|
||||
6 => intf.format(achievement!.v!.abs()),
|
||||
_ => "lol"
|
||||
},
|
||||
style: TextStyle(fontFamily: "Eurostile Round", fontSize: 36, fontWeight: FontWeight.w500, color: Colors.white, height: 0.9),
|
||||
),
|
||||
),
|
||||
if (achievement != null) RichText(
|
||||
textAlign: TextAlign.start,
|
||||
text: TextSpan(
|
||||
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, color: Colors.grey),
|
||||
children: [
|
||||
TextSpan(text: "${achievement!.object}\n"),
|
||||
if (achievement!.vt == 4) TextSpan(text: "Floor ${achievement?.a != null ? achievement!.a! : "-"}"),
|
||||
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)),
|
||||
]
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const Divider(),
|
||||
Text(achievement?.t != null ? timestamp(achievement!.t!) : "---", style: const TextStyle(color: Colors.grey))
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -730,7 +818,7 @@ class _DestinationHomeState extends State<DestinationHome> with SingleTickerProv
|
|||
);
|
||||
}
|
||||
|
||||
Widget getRecordCard(RecordSingle? record, bool? betterThanRankAverage, MapEntry? closestAverage, bool? betterThanClosestAverage, String? rank){
|
||||
Widget getRecordCard(RecordSingle? record, List<Achievement> achievements, bool? betterThanRankAverage, MapEntry? closestAverage, bool? betterThanClosestAverage, String? rank){
|
||||
if (record == null) {
|
||||
return const Card(
|
||||
child: Center(child: Text("No record", style: TextStyle(fontSize: 42))),
|
||||
|
@ -895,7 +983,13 @@ class _DestinationHomeState extends State<DestinationHome> with SingleTickerProv
|
|||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
),
|
||||
Wrap(
|
||||
direction: Axis.horizontal,
|
||||
children: [
|
||||
for (Achievement achievement in achievements) FractionallySizedBox(widthFactor: 0.5, child: AchievementSummary(achievement: achievement)),
|
||||
],
|
||||
),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
@ -965,12 +1059,6 @@ class _DestinationHomeState extends State<DestinationHome> with SingleTickerProv
|
|||
|
||||
_transition = AnimationController(vsync: this, duration: Durations.long4);
|
||||
|
||||
// _transition.addListener((){
|
||||
// setState(() {
|
||||
|
||||
// });
|
||||
// });
|
||||
|
||||
_offsetAnimation = Tween<Offset>(
|
||||
begin: Offset.zero,
|
||||
end: const Offset(1.5, 0.0),
|
||||
|
@ -1012,6 +1100,21 @@ class _DestinationHomeState extends State<DestinationHome> with SingleTickerProv
|
|||
closestAverageBlitz = blitzAverages.entries.last;
|
||||
blitzBetterThanClosestAverage = false;
|
||||
}
|
||||
List<Achievement> sprintAchievements = <Achievement>[
|
||||
snapshot.data!.summaries!.achievements.firstWhere((e) => e.k == 5),
|
||||
snapshot.data!.summaries!.achievements.firstWhere((e) => e.k == 7),
|
||||
snapshot.data!.summaries!.achievements.firstWhere((e) => e.k == 8),
|
||||
snapshot.data!.summaries!.achievements.firstWhere((e) => e.k == 9),
|
||||
snapshot.data!.summaries!.achievements.firstWhere((e) => e.k == 36),
|
||||
snapshot.data!.summaries!.achievements.firstWhere((e) => e.k == 37),
|
||||
snapshot.data!.summaries!.achievements.firstWhere((e) => e.k == 38),
|
||||
snapshot.data!.summaries!.achievements.firstWhere((e) => e.k == 48),
|
||||
];
|
||||
List<Achievement> blitzAchievements = <Achievement>[
|
||||
snapshot.data!.summaries!.achievements.firstWhere((e) => e.k == 6),
|
||||
snapshot.data!.summaries!.achievements.firstWhere((e) => e.k == 39),
|
||||
snapshot.data!.summaries!.achievements.firstWhere((e) => e.k == 52),
|
||||
];
|
||||
return TweenAnimationBuilder(
|
||||
duration: Durations.long4,
|
||||
tween: Tween<double>(begin: 0, end: 1),
|
||||
|
@ -1051,24 +1154,8 @@ class _DestinationHomeState extends State<DestinationHome> with SingleTickerProv
|
|||
],
|
||||
),
|
||||
),
|
||||
//if (testNews != null && testNews!.news.isNotEmpty)
|
||||
Expanded(
|
||||
child: FutureBuilder<News>(
|
||||
future: widget.newsFuture,
|
||||
builder: (context, snapshot) {
|
||||
switch (snapshot.connectionState){
|
||||
case ConnectionState.none:
|
||||
case ConnectionState.waiting:
|
||||
case ConnectionState.active:
|
||||
return const Card(child: Center(child: CircularProgressIndicator()));
|
||||
case ConnectionState.done:
|
||||
if (snapshot.hasData){
|
||||
return NewsThingy(snapshot.data!);
|
||||
}else if (snapshot.hasError){ return FutureError(snapshot); }
|
||||
}
|
||||
return const Text("what?");
|
||||
}
|
||||
),
|
||||
child: NewsThingy(snapshot.data!.news!)
|
||||
)
|
||||
],
|
||||
),
|
||||
|
@ -1097,12 +1184,12 @@ class _DestinationHomeState extends State<DestinationHome> with SingleTickerProv
|
|||
CardMod.exRecords => getListOfRecords("zenithex/recent", "zenithex/top", widget.constraints),
|
||||
},
|
||||
Cards.sprint => switch (cardMod){
|
||||
CardMod.info => getRecordCard(snapshot.data?.summaries!.sprint, sprintBetterThanRankAverage, closestAverageSprint, sprintBetterThanClosestAverage, snapshot.data!.summaries!.league.rank),
|
||||
CardMod.info => getRecordCard(snapshot.data?.summaries!.sprint, sprintAchievements, sprintBetterThanRankAverage, closestAverageSprint, sprintBetterThanClosestAverage, snapshot.data!.summaries!.league.rank),
|
||||
CardMod.records => getListOfRecords("40l/recent", "40l/top", widget.constraints),
|
||||
_ => const Center(child: Text("huh?"))
|
||||
},
|
||||
Cards.blitz => switch (cardMod){
|
||||
CardMod.info => getRecordCard(snapshot.data?.summaries!.blitz, blitzBetterThanRankAverage, closestAverageBlitz, blitzBetterThanClosestAverage, snapshot.data!.summaries!.league.rank),
|
||||
CardMod.info => getRecordCard(snapshot.data?.summaries!.blitz, blitzAchievements, blitzBetterThanRankAverage, closestAverageBlitz, blitzBetterThanClosestAverage, snapshot.data!.summaries!.league.rank),
|
||||
CardMod.records => getListOfRecords("blitz/recent", "blitz/top", widget.constraints),
|
||||
_ => const Center(child: Text("huh?"))
|
||||
},
|
||||
|
|
|
@ -2,6 +2,8 @@ import 'package:flutter/material.dart';
|
|||
import 'package:intl/intl.dart';
|
||||
import 'package:tetra_stats/data_objects/tetrio_constants.dart';
|
||||
import 'package:tetra_stats/gen/strings.g.dart';
|
||||
import 'package:tetra_stats/utils/open_in_browser.dart';
|
||||
import 'package:tetra_stats/views/about_view.dart';
|
||||
import 'package:tetra_stats/views/sprint_and_blitz_averages.dart';
|
||||
|
||||
class DestinationInfo extends StatefulWidget{
|
||||
|
@ -74,18 +76,23 @@ class _DestinationInfo extends State<DestinationInfo> {
|
|||
InfoCard(
|
||||
height: widget.constraints.maxHeight - 77,
|
||||
assetLink: "res/images/Снимок экрана_2023-11-06_01-00-50.png",
|
||||
title: "Shizuru!",
|
||||
description: "Shizuru shizuru shizuru shizuru shizuru shizuru shizuru shizuru shizuru shizuru shizuru shizuru shizuru shizuru shizuru shizuru shizuru shizuru shizuru shizuru shizuru shizuru shizuru shizuru shizuru shizuru shizuru shizuru shizuru shizuru shizuru shizuru shizuru shizuru shizuru shizuru shizuru shizuru shizuru shizuru shizuru shizuru shizuru shizuru shizuru shizuru shizuru shizuru\nNakatsu Shizuru Nakatsu Shizuru Nakatsu Shizuru Nakatsu Shizuru Nakatsu Shizuru Nakatsu Shizuru Nakatsu Shizuru ",
|
||||
onPressed: (){}
|
||||
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: (){},
|
||||
onPressed: (){
|
||||
Navigator.push(context, MaterialPageRoute(
|
||||
builder: (context) => AboutView(),
|
||||
));
|
||||
},
|
||||
),
|
||||
Card()
|
||||
],
|
||||
),
|
||||
)
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
class FirstTimeView extends StatefulWidget {
|
||||
/// The very first view, that user see when he launch this programm.
|
||||
const FirstTimeView({super.key});
|
||||
|
||||
@override
|
||||
State<FirstTimeView> createState() => _FirstTimeState();
|
||||
}
|
||||
|
||||
class _FirstTimeState extends State<FirstTimeView> with SingleTickerProviderStateMixin {
|
||||
late AnimationController _transition;
|
||||
late final Animation<Offset> _offsetAnimation;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_transition = AnimationController(vsync: this, duration: Durations.long4);
|
||||
|
||||
_offsetAnimation = Tween<Offset>(
|
||||
begin: Offset.zero,
|
||||
end: const Offset(1.5, 0.0),
|
||||
).animate(CurvedAnimation(
|
||||
parent: _transition,
|
||||
curve: Curves.elasticIn,
|
||||
));
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: SafeArea(
|
||||
child: TweenAnimationBuilder(
|
||||
duration: Durations.long4,
|
||||
tween: Tween<double>(begin: 0, end: 1),
|
||||
curve: Easing.standard,
|
||||
builder: (context, value, child) {
|
||||
return Container(
|
||||
transform: Matrix4.translationValues(0, 600-value*600, 0),
|
||||
child: Opacity(opacity: value, child: child),
|
||||
);
|
||||
},
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text("Welcome to Tetra Stats", style: Theme.of(context).textTheme.titleLarge),
|
||||
Text("Service, that allows you to keep track of various statistics for TETR.IO"),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 24.0),
|
||||
child: Card(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text("What's your nickname?", style: Theme.of(context).textTheme.titleSmall),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8.0),
|
||||
child: SizedBox(width: 400.0, child: TextField(
|
||||
maxLength: 16,
|
||||
decoration: InputDecoration(
|
||||
hintText: "Type it here... (3-16 symbols)",
|
||||
counter: const Offstage()
|
||||
),
|
||||
)),
|
||||
),
|
||||
ElevatedButton.icon(onPressed: (){}, icon: Icon(Icons.subdirectory_arrow_left), label: Text("Submit"))
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
)
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -23,7 +23,6 @@ import 'package:tetra_stats/views/destination_settings.dart';
|
|||
import 'package:tetra_stats/main.dart';
|
||||
|
||||
late Future<FetchResults> _data;
|
||||
late Future<News> _newsData;
|
||||
TetrioPlayersLeaderboard? _everyone;
|
||||
|
||||
Future<FetchResults> getData(String searchFor) async {
|
||||
|
@ -36,23 +35,26 @@ Future<FetchResults> getData(String searchFor) async {
|
|||
}
|
||||
|
||||
}on TetrioPlayerNotExist{
|
||||
return FetchResults(false, null, [], null, null, null, null, false, TetrioPlayerNotExist());
|
||||
return FetchResults(false, null, [], null, null, null, null, null, false, TetrioPlayerNotExist());
|
||||
}
|
||||
late Summaries summaries;
|
||||
late News? news;
|
||||
late Cutoffs? cutoffs;
|
||||
late CutoffsTetrio? averages;
|
||||
try {
|
||||
List<dynamic> requests = await Future.wait([
|
||||
teto.fetchSummaries(player.userId),
|
||||
teto.fetchNews(player.userId),
|
||||
teto.fetchCutoffsBeanserver(),
|
||||
if (prefs.getBool("showAverages") == true) teto.fetchCutoffsTetrio()
|
||||
]);
|
||||
|
||||
summaries = requests[0];
|
||||
cutoffs = requests.elementAtOrNull(1);
|
||||
averages = requests.elementAtOrNull(2);
|
||||
news = requests[1];
|
||||
cutoffs = requests.elementAtOrNull(2);
|
||||
averages = requests.elementAtOrNull(3);
|
||||
} on Exception catch (e) {
|
||||
return FetchResults(false, null, [], null, null, null, null, false, e);
|
||||
return FetchResults(false, null, [], null, null, null, null, null, false, e);
|
||||
}
|
||||
PlayerLeaderboardPosition? _meAmongEveryone;
|
||||
if (prefs.getBool("showPositions") == true){
|
||||
|
@ -71,7 +73,7 @@ Future<FetchResults> getData(String searchFor) async {
|
|||
await teto.storeState(summaries.league);
|
||||
}
|
||||
|
||||
return FetchResults(true, player, states, summaries, cutoffs, averages, _meAmongEveryone, isTracking, null);
|
||||
return FetchResults(true, player, states, summaries, news, cutoffs, averages, _meAmongEveryone, isTracking, null);
|
||||
}
|
||||
|
||||
class MainView extends StatefulWidget {
|
||||
|
@ -116,7 +118,6 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
|
|||
setState(() {
|
||||
_searchFor = player;
|
||||
_data = getData(_searchFor);
|
||||
_newsData = teto.fetchNews(_searchFor);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -160,7 +161,7 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
|
|||
onPressed: () {
|
||||
// Add your onPressed code here!
|
||||
},
|
||||
icon: const Icon(Icons.more_horiz_rounded),
|
||||
icon: const Icon(Icons.refresh),
|
||||
),
|
||||
destinations: [
|
||||
getDestinationButton(Icons.home, "Home"),
|
||||
|
@ -191,7 +192,7 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
|
|||
),
|
||||
Expanded(
|
||||
child: switch (destination){
|
||||
0 => DestinationHome(searchFor: _searchFor, constraints: constraints, dataFuture: _data, newsFuture: _newsData),
|
||||
0 => DestinationHome(searchFor: _searchFor, constraints: constraints, dataFuture: _data),
|
||||
1 => DestinationGraphs(searchFor: _searchFor, constraints: constraints),
|
||||
2 => DestinationLeaderboards(constraints: constraints),
|
||||
3 => DestinationCutoffs(constraints: constraints),
|
||||
|
|
|
@ -56,7 +56,7 @@ class UserState extends State<UserView> {
|
|||
body: SafeArea(
|
||||
child: LayoutBuilder(
|
||||
builder: (BuildContext context, BoxConstraints constraints) {
|
||||
return DestinationHome(searchFor: widget.searchFor, dataFuture: getData(widget.searchFor), newsFuture: teto.fetchNews(widget.searchFor), constraints: constraints, noSidebar: true);
|
||||
return DestinationHome(searchFor: widget.searchFor, dataFuture: getData(widget.searchFor), constraints: constraints, noSidebar: true);
|
||||
}
|
||||
)
|
||||
)
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 324 KiB |
Loading…
Reference in New Issue