Settings menu layout + animaion for a button, that doesn't work yet
This commit is contained in:
parent
7eb55f9638
commit
b1e49ee70d
|
@ -4,6 +4,7 @@ import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:developer' as developer;
|
import 'dart:developer' as developer;
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'package:path/path.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
||||||
import 'package:tetra_stats/data_objects/cutoff_tetrio.dart';
|
import 'package:tetra_stats/data_objects/cutoff_tetrio.dart';
|
||||||
|
@ -352,6 +353,25 @@ class TetrioService extends DB {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Returns three integers, representing size of the database in bytes, amount of TL records in it and amount of TL states in it
|
||||||
|
Future<(int, int, int)> getDatabaseData() async {
|
||||||
|
await ensureDbIsOpen();
|
||||||
|
final db = getDatabaseOrThrow();
|
||||||
|
String dbPath;
|
||||||
|
if (kIsWeb) {
|
||||||
|
dbPath = dbName;
|
||||||
|
} else {
|
||||||
|
final docsPath = await getApplicationDocumentsDirectory();
|
||||||
|
dbPath = join(docsPath.path, dbName);
|
||||||
|
}
|
||||||
|
var dbFile = File(dbPath);
|
||||||
|
var dbSize = (await dbFile.stat()).size;
|
||||||
|
var dbTLRecordsQuery = (await db.rawQuery('SELECT COUNT(*) FROM `${tetraLeagueMatchesTable}`')).first['COUNT(*)']! as int;
|
||||||
|
var dbTLStatesQuery = (await db.rawQuery('SELECT COUNT(*) FROM `${tetrioLeagueTable}`')).first['COUNT(*)']! as int;
|
||||||
|
return (dbSize, dbTLRecordsQuery, dbTLStatesQuery);
|
||||||
|
}
|
||||||
|
|
||||||
/// Retrieves avaliable Tetra League matches from Tetra Channel api. Returns stream object (fake stream).
|
/// Retrieves avaliable Tetra League matches from Tetra Channel api. Returns stream object (fake stream).
|
||||||
/// Throws an exception if fails to retrieve.
|
/// Throws an exception if fails to retrieve.
|
||||||
Future<SingleplayerStream> fetchStream(String userID, String stream) async {
|
Future<SingleplayerStream> fetchStream(String userID, String stream) async {
|
||||||
|
|
|
@ -748,7 +748,6 @@ class _AddNewColumnCardState extends State<AddNewColumnCard> with SingleTickerPr
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
|
|
|
@ -234,7 +234,7 @@ class _DestinationCutoffsState extends State<DestinationCutoffs> {
|
||||||
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, fontWeight: FontWeight.w100, color: Colors.white, shadows: textShadow),
|
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, fontWeight: FontWeight.w100, color: Colors.white, shadows: textShadow),
|
||||||
children: [
|
children: [
|
||||||
if (rank == "x+") TextSpan(text: "№ 1 is ${f2.format(snapshot.data!.data["top1"]!.tr)} TR", style: const TextStyle(color: Colors.white60, shadows: null))
|
if (rank == "x+") TextSpan(text: "№ 1 is ${f2.format(snapshot.data!.data["top1"]!.tr)} TR", style: const TextStyle(color: Colors.white60, shadows: null))
|
||||||
else TextSpan(text: snapshot.data!.data[ranks[ranks.indexOf(rank)+1]]!.tr > snapshot.data!.data[ranks[ranks.indexOf(rank)+1]]!.targetTr ? "Inflated from ${NumberFormat.compact().format(snapshot.data!.data[rank]!.targetTr)} TR" : "Not inflated", style: TextStyle(color: snapshot.data!.data[ranks[ranks.indexOf(rank)+1]]!.tr > snapshot.data!.data[ranks[ranks.indexOf(rank)+1]]!.targetTr ? Colors.white :Colors.white60, shadows: null)),
|
else TextSpan(text: snapshot.data!.data[ranks[ranks.indexOf(rank)+1]]!.tr > snapshot.data!.data[ranks[ranks.indexOf(rank)+1]]!.targetTr ? "Inflated from ${NumberFormat.compact().format(snapshot.data!.data[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)),
|
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))
|
if (rank == "d") TextSpan(text: "Well...", style: const TextStyle(color: Colors.white60, shadows: null))
|
||||||
else TextSpan(text: snapshot.data!.data[rank]!.tr < snapshot.data!.data[rank]!.targetTr ? "Deflated untill ${NumberFormat.compact().format(snapshot.data!.data[rank]!.targetTr)} TR" : "Not deflated", style: TextStyle(color: snapshot.data!.data[rank]!.tr < snapshot.data!.data[rank]!.targetTr ? Colors.white : Colors.white60, shadows: null))
|
else TextSpan(text: snapshot.data!.data[rank]!.tr < snapshot.data!.data[rank]!.targetTr ? "Deflated untill ${NumberFormat.compact().format(snapshot.data!.data[rank]!.targetTr)} TR" : "Not deflated", style: TextStyle(color: snapshot.data!.data[rank]!.tr < snapshot.data!.data[rank]!.targetTr ? Colors.white : Colors.white60, shadows: null))
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.dart' hide Badge;
|
import 'package:flutter/material.dart' hide Badge;
|
||||||
|
import 'package:flutter_colorpicker/flutter_colorpicker.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:http/http.dart';
|
import 'package:http/http.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
|
@ -24,6 +26,7 @@ import 'package:tetra_stats/data_objects/tetrio_player.dart';
|
||||||
import 'package:tetra_stats/gen/strings.g.dart';
|
import 'package:tetra_stats/gen/strings.g.dart';
|
||||||
import 'package:tetra_stats/services/crud_exceptions.dart';
|
import 'package:tetra_stats/services/crud_exceptions.dart';
|
||||||
import 'package:tetra_stats/utils/colors_functions.dart';
|
import 'package:tetra_stats/utils/colors_functions.dart';
|
||||||
|
import 'package:tetra_stats/utils/filesizes_converter.dart';
|
||||||
import 'package:tetra_stats/utils/numers_formats.dart';
|
import 'package:tetra_stats/utils/numers_formats.dart';
|
||||||
import 'package:tetra_stats/utils/relative_timestamps.dart';
|
import 'package:tetra_stats/utils/relative_timestamps.dart';
|
||||||
import 'package:tetra_stats/utils/text_shadow.dart';
|
import 'package:tetra_stats/utils/text_shadow.dart';
|
||||||
|
@ -202,6 +205,7 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
|
||||||
3 => DestinationCutoffs(constraints: constraints),
|
3 => DestinationCutoffs(constraints: constraints),
|
||||||
4 => DestinationCalculator(constraints: constraints),
|
4 => DestinationCalculator(constraints: constraints),
|
||||||
6 => DestinationSavedData(constraints: constraints),
|
6 => DestinationSavedData(constraints: constraints),
|
||||||
|
7 => DestinationSettings(constraints: constraints),
|
||||||
_ => Text("Unknown destination $destination")
|
_ => Text("Unknown destination $destination")
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -211,6 +215,456 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class DestinationSettings extends StatefulWidget{
|
||||||
|
final BoxConstraints constraints;
|
||||||
|
|
||||||
|
const DestinationSettings({super.key, required this.constraints});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<DestinationSettings> createState() => _DestinationSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
enum SettingsCardMod{
|
||||||
|
general("General"),
|
||||||
|
customization("Custonization"),
|
||||||
|
database("Local database");
|
||||||
|
|
||||||
|
const SettingsCardMod(this.title);
|
||||||
|
|
||||||
|
final String title;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TextStyle settingsTitlesStyle = TextStyle(fontSize: 18);
|
||||||
|
const EdgeInsets descriptionPadding = EdgeInsets.fromLTRB(12.0, 0.0, 12.0, 8.0);
|
||||||
|
|
||||||
|
class _DestinationSettings extends State<DestinationSettings> {
|
||||||
|
SettingsCardMod mod = SettingsCardMod.general;
|
||||||
|
List<DropdownMenuItem<AppLocale>> locales = <DropdownMenuItem<AppLocale>>[];
|
||||||
|
String defaultNickname = "Checking...";
|
||||||
|
late bool oskKagariGimmick;
|
||||||
|
late bool sheetbotRadarGraphs;
|
||||||
|
late int ratingMode;
|
||||||
|
late int timestampMode;
|
||||||
|
late bool showPositions;
|
||||||
|
late bool showAverages;
|
||||||
|
late bool updateInBG;
|
||||||
|
final TextEditingController _playertext = TextEditingController();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
// if (!kIsWeb && !Platform.isAndroid && !Platform.isIOS){
|
||||||
|
// windowManager.getTitle().then((value) => oldWindowTitle = value);
|
||||||
|
// windowManager.setTitle("Tetra Stats: ${t.settings}");
|
||||||
|
// }
|
||||||
|
_getPreferences();
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose(){
|
||||||
|
// if (!kIsWeb && !Platform.isAndroid && !Platform.isIOS) windowManager.setTitle(oldWindowTitle);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _getPreferences() {
|
||||||
|
showPositions = prefs.getBool("showPositions") ?? false;
|
||||||
|
showAverages = prefs.getBool("showAverages") ?? true;
|
||||||
|
updateInBG = prefs.getBool("updateInBG") ?? false;
|
||||||
|
oskKagariGimmick = prefs.getBool("oskKagariGimmick") ?? true;
|
||||||
|
sheetbotRadarGraphs = prefs.getBool("sheetbotRadarGraphs")?? false;
|
||||||
|
ratingMode = prefs.getInt("ratingMode") ?? 0;
|
||||||
|
timestampMode = prefs.getInt("timestampMode") ?? 0;
|
||||||
|
_setDefaultNickname(prefs.getString("player"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _setDefaultNickname(String? n) async {
|
||||||
|
if (n != null) {
|
||||||
|
try {
|
||||||
|
defaultNickname = await teto.getNicknameByID(n);
|
||||||
|
} on TetrioPlayerNotExist {
|
||||||
|
defaultNickname = n;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
defaultNickname = "dan63047";
|
||||||
|
}
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _setPlayer(String player) async {
|
||||||
|
await prefs.setString('player', player);
|
||||||
|
await _setDefaultNickname(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _removePlayer() async {
|
||||||
|
await prefs.remove('player');
|
||||||
|
await _setDefaultNickname("6098518e3d5155e6ec429cdc");
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget getGeneralSettings(){
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
Card(
|
||||||
|
child: Center(child: Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 8.0),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Text(SettingsCardMod.general.title, style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 42)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
Card(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
ListTile(
|
||||||
|
title: Text("Your account in TETR.IO", style: settingsTitlesStyle),
|
||||||
|
trailing: SizedBox(width: 150.0, child: TextField(
|
||||||
|
keyboardType: TextInputType.text,
|
||||||
|
decoration: InputDecoration(hintText: defaultNickname),
|
||||||
|
//onChanged: (value) => setState((){rules.surgeInitAtB2b = int.parse(value);}),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
Divider(),
|
||||||
|
Padding(
|
||||||
|
padding: descriptionPadding,
|
||||||
|
child: Text("Stats of that player will be loaded initially right after launching this app. By default it loads my (dan63) stats. To change that, enter your nickname here."),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Card(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
ListTile(
|
||||||
|
title: Text("Language", style: settingsTitlesStyle),
|
||||||
|
trailing: DropdownButton(
|
||||||
|
items: locales,
|
||||||
|
value: LocaleSettings.currentLocale,
|
||||||
|
onChanged: (value){
|
||||||
|
LocaleSettings.setLocale(value!);
|
||||||
|
if(value.languageCode == Platform.localeName.substring(0, 2)){
|
||||||
|
prefs.remove('locale');
|
||||||
|
}else{
|
||||||
|
prefs.setString('locale', value.languageCode);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Divider(),
|
||||||
|
Padding(
|
||||||
|
padding: descriptionPadding,
|
||||||
|
child: Text("Tetra Stats was translated on ${locales.length} languages. By default, app will pick your system one or English, if locale of your system isn't avaliable."),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Card(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
ListTile(
|
||||||
|
title: Text("Update data in the background", style: settingsTitlesStyle),
|
||||||
|
trailing: Switch(value: updateInBG, onChanged: (bool value){
|
||||||
|
prefs.setBool("updateInBG", value);
|
||||||
|
setState(() {
|
||||||
|
updateInBG = value;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
),
|
||||||
|
Divider(),
|
||||||
|
Padding(
|
||||||
|
padding: descriptionPadding,
|
||||||
|
child: Text("If on, Tetra Stats will attempt to retrieve new info once cache expires. Usually that happen every 5 minutes"),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Card(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
ListTile(
|
||||||
|
title: Text("Show leaderboard based stats", style: settingsTitlesStyle),
|
||||||
|
trailing: Switch(value: showAverages, onChanged: (bool value){
|
||||||
|
prefs.setBool("showAverages", value);
|
||||||
|
setState(() {
|
||||||
|
showAverages = value;
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
Divider(),
|
||||||
|
Padding(
|
||||||
|
padding: descriptionPadding,
|
||||||
|
child: Text("If on, Tetra Stats gonnna provide additional metrics, which will allow you to compare yourself with average player on your rank. The way you'll see it — stats will be highlited with corresponding color, hover over them with cursor for more info."),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Card(
|
||||||
|
surfaceTintColor: Colors.redAccent,
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
ListTile(
|
||||||
|
title: Text("Show position on leaderboard by stats", style: settingsTitlesStyle),
|
||||||
|
trailing: Switch(value: showPositions, onChanged: (bool value){
|
||||||
|
prefs.setBool("showPositions", value);
|
||||||
|
setState(() {
|
||||||
|
showPositions = value;
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
Divider(),
|
||||||
|
Padding(
|
||||||
|
padding: descriptionPadding,
|
||||||
|
child: Text("This can take some time (and traffic) to load, but will allow you to see your position on the leaderboard, sorted by a stat"),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget getCustomizationSettings(){
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
Card(
|
||||||
|
child: Center(child: Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 8.0),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Text(SettingsCardMod.customization.title, style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 42)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
Card(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
ListTile(
|
||||||
|
title: Text("Accent color", style: settingsTitlesStyle),
|
||||||
|
trailing: ColorIndicator(HSVColor.fromColor(Theme.of(context).colorScheme.primary), width: 25, height: 25),
|
||||||
|
),
|
||||||
|
Divider(),
|
||||||
|
Padding(
|
||||||
|
padding: descriptionPadding,
|
||||||
|
child: Text("That color is seen across this app and usually highlites interactive UI elements."),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Card(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
ListTile(
|
||||||
|
title: Text("Timestamps format", style: settingsTitlesStyle),
|
||||||
|
trailing: DropdownButton(
|
||||||
|
value: timestampMode,
|
||||||
|
items: <DropdownMenuItem>[
|
||||||
|
DropdownMenuItem(value: 0, child: Text(t.timestampsAbsoluteGMT)),
|
||||||
|
DropdownMenuItem(value: 1, child: Text(t.timestampsAbsoluteLocalTime)),
|
||||||
|
DropdownMenuItem(value: 2, child: Text(t.timestampsRelative))
|
||||||
|
],
|
||||||
|
onChanged: (dynamic value){
|
||||||
|
prefs.setInt("timestampMode", value);
|
||||||
|
setState(() {
|
||||||
|
timestampMode = value;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Divider(),
|
||||||
|
Padding(
|
||||||
|
padding: descriptionPadding,
|
||||||
|
child: Text("You can choose, in which way timestamps shows time. By default, they show time in GMT timezone, formatted according to chosen locale, example: ${DateFormat.yMMMd(LocaleSettings.currentLocale.languageCode).add_Hms().format(DateTime.utc(2023, DateTime.july, 20, 21, 03, 19))}."),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: descriptionPadding,
|
||||||
|
child: Text("There is also:\n• Locale formatted in your timezone: ${DateFormat.yMMMd(LocaleSettings.currentLocale.languageCode).add_Hms().format(DateTime.utc(2023, DateTime.july, 20, 21, 03, 19).toLocal())}\n• Relative timestamp: ${relativeDateTime(DateTime.utc(2023, DateTime.july, 20, 21, 03, 19))}"),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Card(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
ListTile(
|
||||||
|
title: Text("Sheetbot-like behavior for radar graphs", style: settingsTitlesStyle),
|
||||||
|
trailing: Switch(value: sheetbotRadarGraphs, onChanged: (bool value){
|
||||||
|
prefs.setBool("sheetbotRadarGraphs", value);
|
||||||
|
setState(() {
|
||||||
|
sheetbotRadarGraphs = value;
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
Divider(),
|
||||||
|
Padding(
|
||||||
|
padding: descriptionPadding,
|
||||||
|
child: Text("Altough it was considered by me, that the way graphs work in SheetBot is not very correct, some people were confused to see, that -0.5 stride dosen't look the way it looks on SheetBot graph. Hence, he we are: if this toggle is on, points on the graphs can appear on the opposite half of the graph if value is negative."),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Card(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
ListTile(
|
||||||
|
title: Text("Osk-Kagari gimmick", style: settingsTitlesStyle),
|
||||||
|
trailing: Switch(value: oskKagariGimmick, onChanged: (bool value){
|
||||||
|
prefs.setBool("oskKagariGimmick", value);
|
||||||
|
setState(() {
|
||||||
|
oskKagariGimmick = value;
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
Divider(),
|
||||||
|
Padding(
|
||||||
|
padding: descriptionPadding,
|
||||||
|
child: Text("If on, instead of osk's rank, :kagari: will be rendered."),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget getDatabaseSettings(){
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
Card(
|
||||||
|
child: Center(child: Column(
|
||||||
|
children: [
|
||||||
|
Text(SettingsCardMod.database.title, style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 42)),
|
||||||
|
Divider(),
|
||||||
|
FutureBuilder<(int, int, int)>(future: teto.getDatabaseData(),
|
||||||
|
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 RichText(
|
||||||
|
text: TextSpan(
|
||||||
|
style: TextStyle(fontFamily: "Eurostile Round", color: Colors.white),
|
||||||
|
children: [
|
||||||
|
TextSpan(text: "${bytesToSize(snapshot.data!.$1)} ", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28)),
|
||||||
|
TextSpan(text: "of data stored\n"),
|
||||||
|
TextSpan(text: "${intf.format(snapshot.data!.$2)} ", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28)),
|
||||||
|
TextSpan(text: "Tetra League records saved\n"),
|
||||||
|
TextSpan(text: "${intf.format(snapshot.data!.$3)} ", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28)),
|
||||||
|
TextSpan(text: "Tetra League playerstates saved"),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (snapshot.hasError){ return FutureError(snapshot); }
|
||||||
|
}
|
||||||
|
return Text("huh?");
|
||||||
|
}
|
||||||
|
),
|
||||||
|
Divider(),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: ElevatedButton.icon(
|
||||||
|
onPressed: (){teto.removeDuplicatesFromTLMatches().then((_) => setState((){}));},
|
||||||
|
icon: const Icon(Icons.build),
|
||||||
|
label: Text("Fix"),
|
||||||
|
style: const ButtonStyle(shape: WidgetStatePropertyAll(RoundedRectangleBorder(borderRadius: BorderRadius.only(bottomLeft: Radius.circular(12.0)))))
|
||||||
|
)
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: ElevatedButton.icon(
|
||||||
|
onPressed: (){teto.compressDB().then((_) => setState((){}));},
|
||||||
|
icon: const Icon(Icons.compress),
|
||||||
|
label: Text("Compress"),
|
||||||
|
style: const ButtonStyle(shape: WidgetStatePropertyAll(RoundedRectangleBorder(borderRadius: BorderRadius.only(bottomRight: Radius.circular(12.0)))))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
Card(
|
||||||
|
child: ListTile(
|
||||||
|
title: Text("Export Database", style: settingsTitlesStyle),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Card(
|
||||||
|
child: ListTile(
|
||||||
|
title: Text("Import Database", style: settingsTitlesStyle),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final t = Translations.of(context);
|
||||||
|
if (locales.isEmpty) for (var v in AppLocale.values){
|
||||||
|
locales.add(DropdownMenuItem<AppLocale>(
|
||||||
|
value: v, child: Text(t.locales[v.languageTag]!)));
|
||||||
|
}
|
||||||
|
return Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: 450,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
const Card(
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Spacer(),
|
||||||
|
Text("Settings", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 36)),
|
||||||
|
Spacer()
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
for (SettingsCardMod m in SettingsCardMod.values) Card(
|
||||||
|
child: ListTile(
|
||||||
|
title: Text(m.title),
|
||||||
|
trailing: Icon(Icons.arrow_right, color: mod == m ? Colors.white : Colors.grey),
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
mod = m;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: widget.constraints.maxWidth - 450 - 80,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: switch (mod){
|
||||||
|
SettingsCardMod.general => getGeneralSettings(),
|
||||||
|
SettingsCardMod.customization => getCustomizationSettings(),
|
||||||
|
SettingsCardMod.database => getDatabaseSettings(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class NewsThingy extends StatelessWidget{
|
class NewsThingy extends StatelessWidget{
|
||||||
final News news;
|
final News news;
|
||||||
|
|
||||||
|
@ -638,13 +1092,44 @@ class BadgesThingy extends StatelessWidget{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class NewUserThingy extends StatelessWidget {
|
class NewUserThingy extends StatefulWidget {
|
||||||
final TetrioPlayer player;
|
final TetrioPlayer player;
|
||||||
final bool showStateTimestamp;
|
final bool showStateTimestamp;
|
||||||
final Function setState;
|
final Function setState;
|
||||||
|
|
||||||
const NewUserThingy({super.key, required this.player, required this.showStateTimestamp, required this.setState});
|
const NewUserThingy({super.key, required this.player, required this.showStateTimestamp, required this.setState});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<NewUserThingy> createState() => _NewUserThingyState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _NewUserThingyState extends State<NewUserThingy> with SingleTickerProviderStateMixin {
|
||||||
|
late AnimationController _addToTrackAnimController;
|
||||||
|
late Animation _addToTrackAnim;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState(){
|
||||||
|
_addToTrackAnimController = AnimationController(
|
||||||
|
duration: Durations.medium3,
|
||||||
|
vsync: this,
|
||||||
|
);
|
||||||
|
_addToTrackAnim = new Tween(
|
||||||
|
begin: 0.0,
|
||||||
|
end: 1.0,
|
||||||
|
).animate(new CurvedAnimation(
|
||||||
|
parent: _addToTrackAnimController,
|
||||||
|
curve: Easing.standardDecelerate,
|
||||||
|
reverseCurve: Easing.standardAccelerate
|
||||||
|
));
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_addToTrackAnimController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
Color roleColor(String role){
|
Color roleColor(String role){
|
||||||
switch (role){
|
switch (role){
|
||||||
case "sysop":
|
case "sysop":
|
||||||
|
@ -677,7 +1162,7 @@ class NewUserThingy extends StatelessWidget {
|
||||||
double pfpHeight = 128;
|
double pfpHeight = 128;
|
||||||
int xpTableID = 0;
|
int xpTableID = 0;
|
||||||
|
|
||||||
while (player.xp > xpTableScuffed.values.toList()[xpTableID]) {
|
while (widget.player.xp > xpTableScuffed.values.toList()[xpTableID]) {
|
||||||
xpTableID++;
|
xpTableID++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -689,41 +1174,41 @@ class NewUserThingy extends StatelessWidget {
|
||||||
padding: const EdgeInsets.only(bottom: 4.0),
|
padding: const EdgeInsets.only(bottom: 4.0),
|
||||||
child: Container(
|
child: Container(
|
||||||
constraints: const BoxConstraints(maxWidth: 960),
|
constraints: const BoxConstraints(maxWidth: 960),
|
||||||
height: player.bannerRevision != null ? 218.0 : 138.0,
|
height: widget.player.bannerRevision != null ? 218.0 : 138.0,
|
||||||
child: Stack(
|
child: Stack(
|
||||||
//clipBehavior: Clip.none,
|
//clipBehavior: Clip.none,
|
||||||
children: [
|
children: [
|
||||||
// TODO: osk banner can cause memory leak
|
// TODO: osk banner can cause memory leak
|
||||||
if (player.bannerRevision != null) FadeInImage.memoryNetwork(image: kIsWeb ? "https://ts.dan63.by/oskware_bridge.php?endpoint=TetrioBanner&user=${player.userId}&rv=${player.bannerRevision}" : "https://tetr.io/user-content/banners/${player.userId}.jpg?rv=${player.bannerRevision}",
|
if (widget.player.bannerRevision != null) FadeInImage.memoryNetwork(image: kIsWeb ? "https://ts.dan63.by/oskware_bridge.php?endpoint=TetrioBanner&user=${widget.player.userId}&rv=${widget.player.bannerRevision}" : "https://tetr.io/user-content/banners/${widget.player.userId}.jpg?rv=${widget.player.bannerRevision}",
|
||||||
placeholder: kTransparentImage,
|
placeholder: kTransparentImage,
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
height: 120,
|
height: 120,
|
||||||
fadeInCurve: Easing.standard, fadeInDuration: Durations.long4
|
fadeInCurve: Easing.standard, fadeInDuration: Durations.long4
|
||||||
),
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
top: player.bannerRevision != null ? 90.0 : 10.0,
|
top: widget.player.bannerRevision != null ? 90.0 : 10.0,
|
||||||
left: 16.0,
|
left: 16.0,
|
||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(1000),
|
borderRadius: BorderRadius.circular(1000),
|
||||||
child: player.role == "banned"
|
child: widget.player.role == "banned"
|
||||||
? Image.asset("res/avatars/tetrio_banned.png", fit: BoxFit.fitHeight, height: pfpHeight,)
|
? Image.asset("res/avatars/tetrio_banned.png", fit: BoxFit.fitHeight, height: pfpHeight,)
|
||||||
: player.avatarRevision != null
|
: widget.player.avatarRevision != null
|
||||||
? FadeInImage.memoryNetwork(image: kIsWeb ? "https://ts.dan63.by/oskware_bridge.php?endpoint=TetrioProfilePicture&user=${player.userId}&rv=${player.avatarRevision}" : "https://tetr.io/user-content/avatars/${player.userId}.jpg?rv=${player.avatarRevision}",
|
? FadeInImage.memoryNetwork(image: kIsWeb ? "https://ts.dan63.by/oskware_bridge.php?endpoint=TetrioProfilePicture&user=${widget.player.userId}&rv=${widget.player.avatarRevision}" : "https://tetr.io/user-content/avatars/${widget.player.userId}.jpg?rv=${widget.player.avatarRevision}",
|
||||||
fit: BoxFit.fitHeight, height: 128, placeholder: kTransparentImage, fadeInCurve: Easing.emphasizedDecelerate, fadeInDuration: Durations.long4)
|
fit: BoxFit.fitHeight, height: 128, placeholder: kTransparentImage, fadeInCurve: Easing.emphasizedDecelerate, fadeInDuration: Durations.long4)
|
||||||
: Image.asset("res/avatars/tetrio_anon.png", fit: BoxFit.fitHeight, height: pfpHeight),
|
: Image.asset("res/avatars/tetrio_anon.png", fit: BoxFit.fitHeight, height: pfpHeight),
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
top: player.bannerRevision != null ? 120.0 : 40.0,
|
top: widget.player.bannerRevision != null ? 120.0 : 40.0,
|
||||||
left: 160.0,
|
left: 160.0,
|
||||||
child: Tooltip(
|
child: Tooltip(
|
||||||
message: "${player.userId}\n(Click to copy user ID)",
|
message: "${widget.player.userId}\n(Click to copy user ID)",
|
||||||
child: RichText(text: TextSpan(text: player.username, style: TextStyle(
|
child: RichText(text: TextSpan(text: widget.player.username, style: TextStyle(
|
||||||
fontFamily: fontStyle(player.username.length),
|
fontFamily: fontStyle(widget.player.username.length),
|
||||||
fontSize: 28,
|
fontSize: 28,
|
||||||
),
|
),
|
||||||
recognizer: TapGestureRecognizer()..onTap = (){
|
recognizer: TapGestureRecognizer()..onTap = (){
|
||||||
copyToClipboard(player.userId);
|
copyToClipboard(widget.player.userId);
|
||||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.copiedToClipboard)));
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.copiedToClipboard)));
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -731,23 +1216,23 @@ class NewUserThingy extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
top: player.bannerRevision != null ? 160.0 : 80.0,
|
top: widget.player.bannerRevision != null ? 160.0 : 80.0,
|
||||||
left: 160.0,
|
left: 160.0,
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(right: 4.0),
|
padding: const EdgeInsets.only(right: 4.0),
|
||||||
child: Chip(label: Text(player.role.toUpperCase(), style: const TextStyle(shadows: textShadow),), padding: const EdgeInsets.all(0.0), color: WidgetStatePropertyAll(roleColor(player.role))),
|
child: Chip(label: Text(widget.player.role.toUpperCase(), style: const TextStyle(shadows: textShadow),), padding: const EdgeInsets.all(0.0), color: WidgetStatePropertyAll(roleColor(widget.player.role))),
|
||||||
),
|
),
|
||||||
RichText(
|
RichText(
|
||||||
text: TextSpan(
|
text: TextSpan(
|
||||||
style: const TextStyle(fontFamily: "Eurostile Round"),
|
style: const TextStyle(fontFamily: "Eurostile Round"),
|
||||||
children:
|
children:
|
||||||
[
|
[
|
||||||
if (player.friendCount > 0) const WidgetSpan(child: Icon(Icons.person), alignment: PlaceholderAlignment.middle, baseline: TextBaseline.alphabetic),
|
if (widget.player.friendCount > 0) const WidgetSpan(child: Icon(Icons.person), alignment: PlaceholderAlignment.middle, baseline: TextBaseline.alphabetic),
|
||||||
if (player.friendCount > 0) TextSpan(text: "${intf.format(player.friendCount)} "),
|
if (widget.player.friendCount > 0) TextSpan(text: "${intf.format(widget.player.friendCount)} "),
|
||||||
if (player.supporterTier > 0) WidgetSpan(child: Icon(player.supporterTier > 1 ? Icons.star : Icons.star_border, color: player.supporterTier > 1 ? Colors.yellowAccent : Colors.white), alignment: PlaceholderAlignment.middle, baseline: TextBaseline.alphabetic),
|
if (widget.player.supporterTier > 0) WidgetSpan(child: Icon(widget.player.supporterTier > 1 ? Icons.star : Icons.star_border, color: widget.player.supporterTier > 1 ? Colors.yellowAccent : Colors.white), alignment: PlaceholderAlignment.middle, baseline: TextBaseline.alphabetic),
|
||||||
if (player.supporterTier > 0) TextSpan(text: player.supporterTier.toString(), style: TextStyle(color: player.supporterTier > 1 ? Colors.yellowAccent : Colors.white)),
|
if (widget.player.supporterTier > 0) TextSpan(text: widget.player.supporterTier.toString(), style: TextStyle(color: widget.player.supporterTier > 1 ? Colors.yellowAccent : Colors.white)),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -755,7 +1240,7 @@ class NewUserThingy extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
top: player.bannerRevision != null ? 193.0 : 113.0,
|
top: widget.player.bannerRevision != null ? 193.0 : 113.0,
|
||||||
left: 160.0,
|
left: 160.0,
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: 270,
|
width: 270,
|
||||||
|
@ -763,30 +1248,30 @@ class NewUserThingy extends StatelessWidget {
|
||||||
text: TextSpan(
|
text: TextSpan(
|
||||||
style: const TextStyle(fontFamily: "Eurostile Round"),
|
style: const TextStyle(fontFamily: "Eurostile Round"),
|
||||||
children: [
|
children: [
|
||||||
if (player.country != null) TextSpan(text: "${t.countries[player.country]} • "),
|
if (widget.player.country != null) TextSpan(text: "${t.countries[widget.player.country]} • "),
|
||||||
TextSpan(text: timestamp(player.registrationTime), style: const TextStyle(color: Colors.grey))
|
TextSpan(text: timestamp(widget.player.registrationTime), style: const TextStyle(color: Colors.grey))
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
top: player.bannerRevision != null ? 126.0 : 46.0,
|
top: widget.player.bannerRevision != null ? 126.0 : 46.0,
|
||||||
right: 16.0,
|
right: 16.0,
|
||||||
child: RichText(
|
child: RichText(
|
||||||
textAlign: TextAlign.end,
|
textAlign: TextAlign.end,
|
||||||
text: TextSpan(
|
text: TextSpan(
|
||||||
style: const TextStyle(fontFamily: "Eurostile Round"),
|
style: const TextStyle(fontFamily: "Eurostile Round"),
|
||||||
children: [
|
children: [
|
||||||
TextSpan(text: "Level ${(player.level.isNegative || player.level.isNaN) ? "---" : intf.format(player.level.floor())}", style: TextStyle(decoration: (player.level.isNegative || player.level.isNaN) ? null : TextDecoration.underline, decorationColor: Colors.white70, decorationStyle: TextDecorationStyle.dotted, color: (player.level.isNegative || player.level.isNaN) ? Colors.grey : Colors.white), recognizer: (player.level.isNegative || player.level.isNaN) ? null : TapGestureRecognizer()?..onTap = (){
|
TextSpan(text: "Level ${(widget.player.level.isNegative || widget.player.level.isNaN) ? "---" : intf.format(widget.player.level.floor())}", style: TextStyle(decoration: (widget.player.level.isNegative || widget.player.level.isNaN) ? null : TextDecoration.underline, decorationColor: Colors.white70, decorationStyle: TextDecorationStyle.dotted, color: (widget.player.level.isNegative || widget.player.level.isNaN) ? Colors.grey : Colors.white), recognizer: (widget.player.level.isNegative || widget.player.level.isNaN) ? null : TapGestureRecognizer()?..onTap = (){
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) => AlertDialog(
|
builder: (BuildContext context) => AlertDialog(
|
||||||
title: Text("Level ${intf.format(player.level.floor())}", textAlign: TextAlign.center),
|
title: Text("Level ${intf.format(widget.player.level.floor())}", textAlign: TextAlign.center),
|
||||||
content: SingleChildScrollView(
|
content: SingleChildScrollView(
|
||||||
child: ListBody(children: [
|
child: ListBody(children: [
|
||||||
Text(
|
Text(
|
||||||
"${NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 2).format(player.xp)} XP",
|
"${NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 2).format(widget.player.xp)} XP",
|
||||||
style: const TextStyle(fontFamily: "Eurostile Round", fontWeight: FontWeight.bold)
|
style: const TextStyle(fontFamily: "Eurostile Round", fontWeight: FontWeight.bold)
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
|
@ -796,15 +1281,15 @@ class NewUserThingy extends StatelessWidget {
|
||||||
maximum: 1,
|
maximum: 1,
|
||||||
interval: 1,
|
interval: 1,
|
||||||
ranges: [
|
ranges: [
|
||||||
LinearGaugeRange(startValue: 0, endValue: player.level - player.level.floor(), color: Colors.cyanAccent),
|
LinearGaugeRange(startValue: 0, endValue: widget.player.level - widget.player.level.floor(), color: Colors.cyanAccent),
|
||||||
LinearGaugeRange(startValue: 0, endValue: (player.xp / xpTableScuffed.values.toList()[xpTableID]), color: Colors.redAccent, position: LinearElementPosition.cross)
|
LinearGaugeRange(startValue: 0, endValue: (widget.player.xp / xpTableScuffed.values.toList()[xpTableID]), color: Colors.redAccent, position: LinearElementPosition.cross)
|
||||||
],
|
],
|
||||||
showTicks: true,
|
showTicks: true,
|
||||||
showLabels: false
|
showLabels: false
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text("${t.statCellNum.xpProgress}: ${((player.level - player.level.floor()) * 100).toStringAsFixed(2)} %"),
|
Text("${t.statCellNum.xpProgress}: ${((widget.player.level - widget.player.level.floor()) * 100).toStringAsFixed(2)} %"),
|
||||||
Text("${t.statCellNum.xpFrom0ToLevel(n: xpTableScuffed.keys.toList()[xpTableID])}: ${((player.xp / xpTableScuffed.values.toList()[xpTableID]) * 100).toStringAsFixed(2)} % (${NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 0).format(xpTableScuffed.values.toList()[xpTableID] - player.xp)} ${t.statCellNum.xpLeft})")
|
Text("${t.statCellNum.xpFrom0ToLevel(n: xpTableScuffed.keys.toList()[xpTableID])}: ${((widget.player.xp / xpTableScuffed.values.toList()[xpTableID]) * 100).toStringAsFixed(2)} % (${NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 0).format(xpTableScuffed.values.toList()[xpTableID] - widget.player.xp)} ${t.statCellNum.xpLeft})")
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -818,7 +1303,7 @@ class NewUserThingy extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
const TextSpan(text:"\n"),
|
const TextSpan(text:"\n"),
|
||||||
TextSpan(text: player.gameTime.isNegative ? "-h --m" : playtime(player.gameTime), style: TextStyle(color: player.gameTime.isNegative ? Colors.grey : Colors.white, decoration: player.gameTime.isNegative ? null : TextDecoration.underline, decorationColor: Colors.white70, decorationStyle: TextDecorationStyle.dotted), recognizer: !player.gameTime.isNegative ? (TapGestureRecognizer()..onTap = (){
|
TextSpan(text: widget.player.gameTime.isNegative ? "-h --m" : playtime(widget.player.gameTime), style: TextStyle(color: widget.player.gameTime.isNegative ? Colors.grey : Colors.white, decoration: widget.player.gameTime.isNegative ? null : TextDecoration.underline, decorationColor: Colors.white70, decorationStyle: TextDecorationStyle.dotted), recognizer: !widget.player.gameTime.isNegative ? (TapGestureRecognizer()..onTap = (){
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) => AlertDialog(
|
builder: (BuildContext context) => AlertDialog(
|
||||||
|
@ -826,17 +1311,17 @@ class NewUserThingy extends StatelessWidget {
|
||||||
content: SingleChildScrollView(
|
content: SingleChildScrollView(
|
||||||
child: ListBody(children: [
|
child: ListBody(children: [
|
||||||
Text(
|
Text(
|
||||||
"${intf.format(player.gameTime.inDays)}d ${nonsecs.format(player.gameTime.inHours%24)}h ${nonsecs.format(player.gameTime.inMinutes%60)}m ${nonsecs.format(player.gameTime.inSeconds%60)}s ${nonsecs3.format(player.gameTime.inMilliseconds%1000)}ms ${nonsecs3.format(player.gameTime.inMicroseconds%1000)}μs",
|
"${intf.format(widget.player.gameTime.inDays)}d ${nonsecs.format(widget.player.gameTime.inHours%24)}h ${nonsecs.format(widget.player.gameTime.inMinutes%60)}m ${nonsecs.format(widget.player.gameTime.inSeconds%60)}s ${nonsecs3.format(widget.player.gameTime.inMilliseconds%1000)}ms ${nonsecs3.format(widget.player.gameTime.inMicroseconds%1000)}μs",
|
||||||
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 24)
|
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 24)
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(top: 8.0),
|
padding: const EdgeInsets.only(top: 8.0),
|
||||||
child: Text("It's ${f4.format(player.gameTime.inSeconds/31536000)} years,"),
|
child: Text("It's ${f4.format(widget.player.gameTime.inSeconds/31536000)} years,"),
|
||||||
),
|
),
|
||||||
Text("${f4.format(player.gameTime.inSeconds/2628000)} monts,"),
|
Text("${f4.format(widget.player.gameTime.inSeconds/2628000)} monts,"),
|
||||||
Text("${f4.format(player.gameTime.inSeconds/3600)} hours,"),
|
Text("${f4.format(widget.player.gameTime.inSeconds/3600)} hours,"),
|
||||||
Text("${f2.format(player.gameTime.inMilliseconds/60000)} minutes,"),
|
Text("${f2.format(widget.player.gameTime.inMilliseconds/60000)} minutes,"),
|
||||||
Text("${intf.format(player.gameTime.inSeconds)} seconds"),
|
Text("${intf.format(widget.player.gameTime.inSeconds)} seconds"),
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -850,8 +1335,8 @@ class NewUserThingy extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}) : null),
|
}) : null),
|
||||||
const TextSpan(text:"\n"),
|
const TextSpan(text:"\n"),
|
||||||
TextSpan(text: player.gamesWon > -1 ? intf.format(player.gamesWon) : "---", style: TextStyle(color: player.gamesWon > -1 ? Colors.white : Colors.grey)),
|
TextSpan(text: widget.player.gamesWon > -1 ? intf.format(widget.player.gamesWon) : "---", style: TextStyle(color: widget.player.gamesWon > -1 ? Colors.white : Colors.grey)),
|
||||||
TextSpan(text: "/${player.gamesPlayed > -1 ? intf.format(player.gamesPlayed) : "---"}", style: const TextStyle(fontFamily: "Eurostile Round Condensed", color: Colors.grey)),
|
TextSpan(text: "/${widget.player.gamesPlayed > -1 ? intf.format(widget.player.gamesPlayed) : "---"}", style: const TextStyle(fontFamily: "Eurostile Round Condensed", color: Colors.grey)),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -863,12 +1348,52 @@ class NewUserThingy extends StatelessWidget {
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Expanded(child: ElevatedButton.icon(onPressed: (){print("ok, and?");}, icon: const Icon(Icons.person_add), label: Text(t.track), style: const ButtonStyle(shape: WidgetStatePropertyAll(RoundedRectangleBorder(borderRadius: BorderRadius.only(bottomLeft: Radius.circular(12.0))))))),
|
Expanded(
|
||||||
|
child: AnimatedBuilder(
|
||||||
|
animation: _addToTrackAnim,
|
||||||
|
builder: (context, child) {
|
||||||
|
double firstButtonPosition = 0-(_addToTrackAnim.value as double)*25;
|
||||||
|
double secondButtonPosition = 25-(_addToTrackAnim.value as double)*25;
|
||||||
|
double firstButtonOpacity = 1-(_addToTrackAnim.value as double)*2;
|
||||||
|
double secondButtonOpacity = _addToTrackAnim.value*2-1;
|
||||||
|
return ElevatedButton.icon(
|
||||||
|
onPressed: (){
|
||||||
|
_addToTrackAnim.isCompleted ? _addToTrackAnimController.reverse() : _addToTrackAnimController.forward();
|
||||||
|
},
|
||||||
|
icon: _addToTrackAnim.value < 0.5 ? Container(
|
||||||
|
transform: Matrix4.translationValues(0, firstButtonPosition, 0),
|
||||||
|
child: Opacity(
|
||||||
|
opacity: firstButtonOpacity,
|
||||||
|
child: const Icon(Icons.person_add)
|
||||||
|
)
|
||||||
|
) : Container(
|
||||||
|
transform: Matrix4.translationValues(0, secondButtonPosition, 0),
|
||||||
|
child: Opacity(
|
||||||
|
opacity: secondButtonOpacity,
|
||||||
|
child: const Icon(Icons.person_remove)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
label: _addToTrackAnim.value < 0.5 ? Container(
|
||||||
|
transform: Matrix4.translationValues(0, firstButtonPosition, 0),
|
||||||
|
child: Opacity(
|
||||||
|
opacity: firstButtonOpacity,
|
||||||
|
child: Text(t.track)
|
||||||
|
)
|
||||||
|
) : Container(
|
||||||
|
transform: Matrix4.translationValues(0, secondButtonPosition, 0),
|
||||||
|
child: Opacity(
|
||||||
|
opacity: secondButtonOpacity,
|
||||||
|
child: Text(t.stopTracking)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
style: const ButtonStyle(shape: WidgetStatePropertyAll(RoundedRectangleBorder(borderRadius: BorderRadius.only(bottomLeft: Radius.circular(12.0))))));
|
||||||
|
},
|
||||||
|
)),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: ElevatedButton.icon(
|
child: ElevatedButton.icon(
|
||||||
onPressed: (){
|
onPressed: (){
|
||||||
Navigator.push(context, MaterialPageRoute(
|
Navigator.push(context, MaterialPageRoute(
|
||||||
builder: (context) => CompareView(player),
|
builder: (context) => CompareView(widget.player),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue