Compare commits

..

No commits in common. "9394b818cc04b0f314be381afe6375efb18f269b" and "cedb304c1a51935352e156988dccce7f969ecae8" have entirely different histories.

13 changed files with 78 additions and 205 deletions

View File

@ -1,18 +1,13 @@
import 'dart:async'; import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'dart:isolate';
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'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:developer' as developer; import 'dart:developer' as developer;
import 'package:package_info_plus/package_info_plus.dart'; import 'package:package_info_plus/package_info_plus.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:tetra_stats/services/tetrio_crud.dart'; import 'package:tetra_stats/services/tetrio_crud.dart';
import 'package:tetra_stats/views/customization_view.dart'; import 'package:tetra_stats/views/customization_view.dart';
import 'package:tetra_stats/views/ranks_averages_view.dart';
import 'package:tetra_stats/views/sprint_and_blitz_averages.dart';
import 'package:tetra_stats/views/tl_leaderboard_view.dart';
import 'package:window_manager/window_manager.dart'; import 'package:window_manager/window_manager.dart';
import 'package:sqflite_common_ffi/sqflite_ffi.dart'; import 'package:sqflite_common_ffi/sqflite_ffi.dart';
import 'package:sqflite_common_ffi_web/sqflite_ffi_web.dart'; import 'package:sqflite_common_ffi_web/sqflite_ffi_web.dart';
@ -29,38 +24,6 @@ late SharedPreferences prefs;
late TetrioService teto; late TetrioService teto;
ColorScheme sheme = const ColorScheme.dark(primary: Colors.cyanAccent, secondary: Colors.white); ColorScheme sheme = const ColorScheme.dark(primary: Colors.cyanAccent, secondary: Colors.white);
Future<dynamic> computeIsolate(Future Function() function) async {
final receivePort = ReceivePort();
var rootToken = RootIsolateToken.instance!;
await Isolate.spawn<_IsolateData>(
_isolateEntry,
_IsolateData(
token: rootToken,
function: function,
answerPort: receivePort.sendPort,
),
);
return await receivePort.first;
}
void _isolateEntry(_IsolateData isolateData) async {
BackgroundIsolateBinaryMessenger.ensureInitialized(isolateData.token);
final answer = await isolateData.function();
isolateData.answerPort.send(answer);
}
class _IsolateData {
final RootIsolateToken token;
final Function function;
final SendPort answerPort;
_IsolateData({
required this.token,
required this.function,
required this.answerPort,
});
}
void setAccentColor(Color color){ // does this thing work??? yes??? no??? void setAccentColor(Color color){ // does this thing work??? yes??? no???
sheme = ColorScheme.dark(primary: color, secondary: Colors.white); sheme = ColorScheme.dark(primary: color, secondary: Colors.white);
} }
@ -75,26 +38,6 @@ final router = GoRouter(
GoRoute( GoRoute(
path: 'settings', path: 'settings',
builder: (_, __) => const SettingsView(), builder: (_, __) => const SettingsView(),
routes: [
GoRoute(
path: 'customization',
builder: (_, __) => const CustomizationView(),
),
]
),
GoRoute(
path: "leaderboard",
builder: (_, __) => const TLLeaderboardView(),
routes: [
GoRoute(
path: "LBvalues",
builder: (_, __) => const RankAveragesView(),
),
]
),
GoRoute(
path: "LBvalues",
builder: (_, __) => const RankAveragesView(),
), ),
GoRoute( GoRoute(
path: 'states', path: 'states',
@ -105,9 +48,9 @@ final router = GoRouter(
builder: (_, __) => const CalcView(), builder: (_, __) => const CalcView(),
), ),
GoRoute( GoRoute(
path: 'sprintAndBlitzAverages', path: 'customization',
builder: (_, __) => const SprintAndBlitzView(), builder: (_, __) => const CustomizationView(),
) ),
] ]
), ),
GoRoute( // that one intended for Android users, that can open https://ch.tetr.io/u/ links GoRoute( // that one intended for Android users, that can open https://ch.tetr.io/u/ links
@ -149,10 +92,10 @@ void main() async {
} }
// I dont want to store old cache // I dont want to store old cache
Timer.periodic(Duration(minutes: 5), (Timer timer) { Timer.periodic(Duration(minutes: 5), (Timer timer) async {
teto.cacheRoutine(); teto.cacheRoutine();
developer.log("Cache routine complete, next one in ${DateTime.now().add(Duration(minutes: 5))}", name: "main"); developer.log("Cache routine complete, next one in ${DateTime.now().add(Duration(minutes: 5))}", name: "main");
// if (prefs.getBool("updateInBG") == true) teto.fetchTracked(); // TODO: Somehow avoid doing that in main isolate if (prefs.getBool("updateInBG") == true) await teto.fetchTracked();
}); });
runApp(TranslationProvider( runApp(TranslationProvider(

View File

@ -8,6 +8,5 @@ final NumberFormat f4 = NumberFormat.decimalPatternDigits(locale: LocaleSettings
final NumberFormat f3 = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 3); final NumberFormat f3 = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 3);
final NumberFormat f2 = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 2); final NumberFormat f2 = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 2);
final NumberFormat f2l = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 2)..minimumFractionDigits = 0; final NumberFormat f2l = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 2)..minimumFractionDigits = 0;
final NumberFormat f1 = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 1);
final NumberFormat f0 = NumberFormat.decimalPattern(LocaleSettings.currentLocale.languageCode); final NumberFormat f0 = NumberFormat.decimalPattern(LocaleSettings.currentLocale.languageCode);
final NumberFormat percentage = NumberFormat.percentPattern(LocaleSettings.currentLocale.languageCode)..maximumFractionDigits = 2; final NumberFormat percentage = NumberFormat.percentPattern(LocaleSettings.currentLocale.languageCode)..maximumFractionDigits = 2;

View File

@ -1,59 +0,0 @@
import 'package:tetra_stats/utils/numers_formats.dart';
/// Returns string, that represents time difference between [dateTime] and now
String relativeDateTime(DateTime dateTime){
Duration difference = dateTime.difference(DateTime.now());
bool inPast = difference.isNegative;
Duration absDifference = difference.abs();
double timeInterval;
// years
timeInterval = absDifference.inSeconds / 31536000;
if (timeInterval >= 100.0) {
return inPast ? "${timeInterval.truncate()} years ago" : "in ${timeInterval.truncate()} years";
} else if (timeInterval >= 10.0) {
return inPast ? "${f1.format(timeInterval)} years ago" : "in ${f1.format(timeInterval)} years";
} else if (timeInterval >= 1.0) {
return inPast ? "${f2.format(timeInterval)} years ago" : "in ${f2.format(timeInterval)} years";
}
// months
timeInterval = absDifference.inSeconds / 2592000;
if (timeInterval >= 10.0) {
return inPast ? "${timeInterval.truncate()} months ago" : "in ${timeInterval.truncate()} months";
} else if (timeInterval >= 1.0) {
return inPast ? "${f1.format(timeInterval)} months ago" : "in ${f1.format(timeInterval)} months";
}
// days
timeInterval = absDifference.inSeconds / 86400;
if (timeInterval >= 10.0) {
return inPast ? "${timeInterval.truncate()} days ago" : "in ${timeInterval.truncate()} days";
} else if (timeInterval >= 1.0) {
return inPast ? "${f1.format(timeInterval)} days ago" : "in ${f1.format(timeInterval)} days";
}
// hours
timeInterval = absDifference.inSeconds / 3600;
if (timeInterval >= 10.0) {
return inPast ? "${timeInterval.truncate()} hours ago" : "in ${timeInterval.truncate()} hours";
} else if (timeInterval >= 1.0) {
return inPast ? "${f1.format(timeInterval)} hours ago" : "in ${f1.format(timeInterval)} hours";
}
// minutes
timeInterval = absDifference.inSeconds / 60;
if (timeInterval >= 10.0) {
return inPast ? "${timeInterval.truncate()} minutes ago" : "in ${timeInterval.truncate()} minutes";
} else if (timeInterval >= 1.0) {
return inPast ? "${f1.format(timeInterval)} minutes ago" : "in ${f1.format(timeInterval)} minutes";
}
// seconds
timeInterval = absDifference.inMilliseconds / 1000;
if (timeInterval >= 10.0) {
return inPast ? "${timeInterval.truncate()} seconds ago" : "in ${timeInterval.truncate()} seconds";
} else {
return inPast ? "${f1.format(timeInterval)} seconds ago" : "in ${f1.format(timeInterval)} seconds";
}
}

View File

@ -2,7 +2,7 @@ import 'dart:io';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:tetra_stats/views/settings_view.dart' show subtitleStyle; import 'package:tetra_stats/views/settings_view.dart' show subtitleStyle;
import 'package:tetra_stats/main.dart' show prefs; import 'package:shared_preferences/shared_preferences.dart';
import 'package:tetra_stats/gen/strings.g.dart'; import 'package:tetra_stats/gen/strings.g.dart';
import 'package:window_manager/window_manager.dart'; import 'package:window_manager/window_manager.dart';
@ -18,10 +18,10 @@ class CustomizationView extends StatefulWidget {
} }
class CustomizationState extends State<CustomizationView> { class CustomizationState extends State<CustomizationView> {
late SharedPreferences prefs;
late bool oskKagariGimmick; late bool oskKagariGimmick;
late bool sheetbotRadarGraphs; late bool sheetbotRadarGraphs;
late int ratingMode; late int ratingMode;
late int timestampMode;
void changeColor(Color color) { void changeColor(Color color) {
setState(() => pickerColor = color); setState(() => pickerColor = color);
@ -33,7 +33,7 @@ class CustomizationState extends State<CustomizationView> {
windowManager.getTitle().then((value) => oldWindowTitle = value); windowManager.getTitle().then((value) => oldWindowTitle = value);
windowManager.setTitle("Tetra Stats: ${t.settings}"); windowManager.setTitle("Tetra Stats: ${t.settings}");
} }
_getPreferences(); _getPreferences().then((value) => setState((){}));
super.initState(); super.initState();
} }
@ -43,7 +43,8 @@ class CustomizationState extends State<CustomizationView> {
super.dispose(); super.dispose();
} }
void _getPreferences() { Future<void> _getPreferences() async {
prefs = await SharedPreferences.getInstance();
if (prefs.getBool("oskKagariGimmick") != null) { if (prefs.getBool("oskKagariGimmick") != null) {
oskKagariGimmick = prefs.getBool("oskKagariGimmick")!; oskKagariGimmick = prefs.getBool("oskKagariGimmick")!;
} else { } else {
@ -59,11 +60,6 @@ class CustomizationState extends State<CustomizationView> {
} else { } else {
ratingMode = 0; ratingMode = 0;
} }
if (prefs.getInt("timestampMode") != null) {
timestampMode = prefs.getInt("timestampMode")!;
} else {
timestampMode = 0;
}
} }
ThemeData getTheme(BuildContext context, Color color){ ThemeData getTheme(BuildContext context, Color color){
@ -129,23 +125,6 @@ class CustomizationState extends State<CustomizationView> {
oskKagariGimmick = value; oskKagariGimmick = value;
}); });
}),), }),),
ListTile(title: Text("Timestamps"),
subtitle: Text(t.oskKagariDescription, style: subtitleStyle),
trailing: DropdownButton(
value: timestampMode,
items: <DropdownMenuItem>[
DropdownMenuItem(value: 0, child: Text("Absolute (GMT)")),
DropdownMenuItem(value: 1, child: Text("Absolute (Local Time)")),
DropdownMenuItem(value: 2, child: Text("Relative"))
],
onChanged: (dynamic value){
prefs.setInt("timestampMode", value);
setState(() {
timestampMode = value;
});
},
),
),
ListTile(title: Text("Main representation of rating"), ListTile(title: Text("Main representation of rating"),
subtitle: Text(t.oskKagariDescription, style: subtitleStyle), subtitle: Text(t.oskKagariDescription, style: subtitleStyle),
trailing: DropdownButton( trailing: DropdownButton(
@ -162,6 +141,13 @@ class CustomizationState extends State<CustomizationView> {
}); });
}, },
), ),
// trailing: Switch(value: sheetbotRadarGraphs, onChanged: (bool value){
// prefs.setBool("sheetbotRadarGraphs", value);
// setState(() {
// sheetbotRadarGraphs = value;
// });
// }
// ),
), ),
ListTile(title: Text("Sheetbot-like behavior for radar graphs"), ListTile(title: Text("Sheetbot-like behavior for radar graphs"),
subtitle: Text(t.oskKagariDescription, style: subtitleStyle), subtitle: Text(t.oskKagariDescription, style: subtitleStyle),

View File

@ -18,13 +18,15 @@ import 'package:tetra_stats/main.dart' show prefs, teto;
import 'package:tetra_stats/services/crud_exceptions.dart'; import 'package:tetra_stats/services/crud_exceptions.dart';
import 'package:tetra_stats/utils/numers_formats.dart'; import 'package:tetra_stats/utils/numers_formats.dart';
import 'package:tetra_stats/utils/text_shadow.dart'; import 'package:tetra_stats/utils/text_shadow.dart';
import 'package:tetra_stats/views/ranks_averages_view.dart' show RankAveragesView;
import 'package:tetra_stats/views/sprint_and_blitz_averages.dart';
import 'package:tetra_stats/views/tl_leaderboard_view.dart' show TLLeaderboardView;
import 'package:tetra_stats/views/tl_match_view.dart' show TlMatchResultView; import 'package:tetra_stats/views/tl_match_view.dart' show TlMatchResultView;
import 'package:tetra_stats/widgets/finesse_thingy.dart'; import 'package:tetra_stats/widgets/finesse_thingy.dart';
import 'package:tetra_stats/widgets/lineclears_thingy.dart'; import 'package:tetra_stats/widgets/lineclears_thingy.dart';
import 'package:tetra_stats/widgets/list_tile_trailing_stats.dart'; import 'package:tetra_stats/widgets/list_tile_trailing_stats.dart';
import 'package:tetra_stats/widgets/search_box.dart'; import 'package:tetra_stats/widgets/search_box.dart';
import 'package:tetra_stats/widgets/stat_sell_num.dart'; import 'package:tetra_stats/widgets/stat_sell_num.dart';
import 'package:tetra_stats/widgets/text_timestamp.dart';
import 'package:tetra_stats/widgets/tl_thingy.dart'; import 'package:tetra_stats/widgets/tl_thingy.dart';
import 'package:tetra_stats/widgets/user_thingy.dart'; import 'package:tetra_stats/widgets/user_thingy.dart';
import 'package:window_manager/window_manager.dart'; import 'package:window_manager/window_manager.dart';
@ -39,6 +41,7 @@ List _historyShortTitles = ["TR", "Glicko", "RD", "APM", "PPS", "VS", "APP", "DS
late ScrollController _scrollController; late ScrollController _scrollController;
final NumberFormat _timeInSec = NumberFormat("#,###.###s.", LocaleSettings.currentLocale.languageCode); final NumberFormat _timeInSec = NumberFormat("#,###.###s.", LocaleSettings.currentLocale.languageCode);
final NumberFormat secs = NumberFormat("00.###", LocaleSettings.currentLocale.languageCode); final NumberFormat secs = NumberFormat("00.###", LocaleSettings.currentLocale.languageCode);
final DateFormat _dateFormat = DateFormat.yMMMd(LocaleSettings.currentLocale.languageCode).add_Hms();
class MainView extends StatefulWidget { class MainView extends StatefulWidget {
@ -95,6 +98,7 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
bool _TLHistoryWasFetched = false; bool _TLHistoryWasFetched = false;
late TabController _tabController; late TabController _tabController;
late TabController _wideScreenTabController; late TabController _wideScreenTabController;
late bool fixedScroll;
String get title => "Tetra Stats: $_titleNickname"; String get title => "Tetra Stats: $_titleNickname";
@ -648,7 +652,12 @@ class _NavDrawerState extends State<NavDrawer> {
leading: const Icon(Icons.leaderboard), leading: const Icon(Icons.leaderboard),
title: Text(t.tlLeaderboard), title: Text(t.tlLeaderboard),
onTap: () { onTap: () {
context.go("/leaderboard"); Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const TLLeaderboardView(),
),
);
}, },
), ),
), ),
@ -657,7 +666,12 @@ class _NavDrawerState extends State<NavDrawer> {
leading: const Icon(Icons.compress), leading: const Icon(Icons.compress),
title: Text(t.rankAveragesViewTitle), title: Text(t.rankAveragesViewTitle),
onTap: () { onTap: () {
context.go("/LBvalues"); Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const RankAveragesView(),
),
);
}, },
), ),
), ),
@ -666,7 +680,12 @@ class _NavDrawerState extends State<NavDrawer> {
leading: const Icon(Icons.bar_chart), leading: const Icon(Icons.bar_chart),
title: Text(t.sprintAndBlitsViewTitle), title: Text(t.sprintAndBlitsViewTitle),
onTap: () { onTap: () {
context.go("/sprintAndBlitzAverages"); Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const SprintAndBlitzView(),
),
);
}, },
), ),
), ),
@ -748,7 +767,7 @@ class _TLRecords extends StatelessWidget {
leading: Text("${data[index].endContext.firstWhere((element) => element.userId == userID).points} : ${data[index].endContext.firstWhere((element) => element.userId != userID).points}", leading: Text("${data[index].endContext.firstWhere((element) => element.userId == userID).points} : ${data[index].endContext.firstWhere((element) => element.userId != userID).points}",
style: bigScreen ? const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28, shadows: textShadow) : const TextStyle(fontSize: 28, shadows: textShadow)), style: bigScreen ? const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 28, shadows: textShadow) : const TextStyle(fontSize: 28, shadows: textShadow)),
title: Text("vs. ${data[index].endContext.firstWhere((element) => element.userId != userID).username}"), title: Text("vs. ${data[index].endContext.firstWhere((element) => element.userId != userID).username}"),
subtitle: Text(timestamp(data[index].timestamp)), subtitle: Text(_dateFormat.format(data[index].timestamp)),
trailing: TrailingStats( trailing: TrailingStats(
data[index].endContext.firstWhere((element) => element.userId == userID).secondary, data[index].endContext.firstWhere((element) => element.userId == userID).secondary,
data[index].endContext.firstWhere((element) => element.userId == userID).tertiary, data[index].endContext.firstWhere((element) => element.userId == userID).tertiary,
@ -911,7 +930,7 @@ class _HistoryChartThigyState extends State<_HistoryChartThigy> {
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 20), style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 20),
), ),
), ),
Text(_gamesPlayedInsteadOfDateAndTime ? t.gamesPlayed(games: t.games(n: data.gamesPlayed)) : timestamp(data.timestamp)) Text(_gamesPlayedInsteadOfDateAndTime ? t.gamesPlayed(games: t.games(n: data.gamesPlayed)) : _dateFormat.format(data.timestamp))
], ],
), ),
); );
@ -1067,7 +1086,7 @@ class _TwoRecordsThingy extends StatelessWidget {
)), )),
if (sprint!.rank != null) TextSpan(text: "${sprint!.rank}", style: TextStyle(color: getColorOfRank(sprint!.rank!))), if (sprint!.rank != null) TextSpan(text: "${sprint!.rank}", style: TextStyle(color: getColorOfRank(sprint!.rank!))),
if (sprint!.rank != null) const TextSpan(text: ""), if (sprint!.rank != null) const TextSpan(text: ""),
TextSpan(text: timestamp(sprint!.timestamp!)), TextSpan(text: _dateFormat.format(sprint!.timestamp!)),
] ]
), ),
), ),
@ -1123,7 +1142,7 @@ class _TwoRecordsThingy extends StatelessWidget {
else TextSpan(text: "${t.verdictGeneral(n: readableIntDifference(blitz!.endContext!.score, closestAverageBlitz.value), verdict: blitzBetterThanClosestAverage ? t.verdictBetter : t.verdictWorse, rank: closestAverageBlitz.key.toUpperCase())}\n", style: TextStyle( else TextSpan(text: "${t.verdictGeneral(n: readableIntDifference(blitz!.endContext!.score, closestAverageBlitz.value), verdict: blitzBetterThanClosestAverage ? t.verdictBetter : t.verdictWorse, rank: closestAverageBlitz.key.toUpperCase())}\n", style: TextStyle(
color: blitzBetterThanClosestAverage ? Colors.greenAccent : Colors.redAccent color: blitzBetterThanClosestAverage ? Colors.greenAccent : Colors.redAccent
)), )),
TextSpan(text: timestamp(blitz!.timestamp!)), TextSpan(text: _dateFormat.format(blitz!.timestamp!)),
if (blitz!.rank != null) const TextSpan(text: ""), if (blitz!.rank != null) const TextSpan(text: ""),
if (blitz!.rank != null) TextSpan(text: "${blitz!.rank}", style: TextStyle(color: getColorOfRank(blitz!.rank!))), if (blitz!.rank != null) TextSpan(text: "${blitz!.rank}", style: TextStyle(color: getColorOfRank(blitz!.rank!))),
] ]
@ -1235,7 +1254,7 @@ class _RecordThingy extends StatelessWidget {
)), )),
if (record!.rank != null) TextSpan(text: "${record!.rank}", style: TextStyle(color: getColorOfRank(record!.rank!))), if (record!.rank != null) TextSpan(text: "${record!.rank}", style: TextStyle(color: getColorOfRank(record!.rank!))),
if (record!.rank != null) const TextSpan(text: ""), if (record!.rank != null) const TextSpan(text: ""),
TextSpan(text: timestamp(record!.timestamp!)), TextSpan(text: _dateFormat.format(record!.timestamp!)),
] ]
), ),
) )
@ -1349,7 +1368,7 @@ class _OtherThingy extends StatelessWidget {
] ]
) )
), ),
subtitle: Text(timestamp(news.timestamp)), subtitle: Text(_dateFormat.format(news.timestamp)),
); );
case "personalbest": case "personalbest":
return ListTile( return ListTile(
@ -1364,7 +1383,7 @@ class _OtherThingy extends StatelessWidget {
] ]
) )
), ),
subtitle: Text(timestamp(news.timestamp)), subtitle: Text(_dateFormat.format(news.timestamp)),
leading: Image.asset( leading: Image.asset(
"res/icons/improvement-local.png", "res/icons/improvement-local.png",
height: 48, height: 48,
@ -1386,7 +1405,7 @@ class _OtherThingy extends StatelessWidget {
] ]
) )
), ),
subtitle: Text(timestamp(news.timestamp)), subtitle: Text(_dateFormat.format(news.timestamp)),
leading: Image.asset( leading: Image.asset(
"res/tetrio_badges/${news.data["type"]}.png", "res/tetrio_badges/${news.data["type"]}.png",
height: 48, height: 48,
@ -1408,7 +1427,7 @@ class _OtherThingy extends StatelessWidget {
] ]
) )
), ),
subtitle: Text(timestamp(news.timestamp)), subtitle: Text(_dateFormat.format(news.timestamp)),
leading: Image.asset( leading: Image.asset(
"res/tetrio_tl_alpha_ranks/${news.data["rank"]}.png", "res/tetrio_tl_alpha_ranks/${news.data["rank"]}.png",
height: 48, height: 48,
@ -1429,7 +1448,7 @@ class _OtherThingy extends StatelessWidget {
] ]
) )
), ),
subtitle: Text(timestamp(news.timestamp)), subtitle: Text(_dateFormat.format(news.timestamp)),
leading: Image.asset( leading: Image.asset(
"res/icons/supporter-tag.png", "res/icons/supporter-tag.png",
height: 48, height: 48,
@ -1450,7 +1469,7 @@ class _OtherThingy extends StatelessWidget {
] ]
) )
), ),
subtitle: Text(timestamp(news.timestamp)), subtitle: Text(_dateFormat.format(news.timestamp)),
leading: Image.asset( leading: Image.asset(
"res/icons/supporter-tag.png", "res/icons/supporter-tag.png",
height: 48, height: 48,
@ -1463,7 +1482,7 @@ class _OtherThingy extends StatelessWidget {
default: // if type is unknown default: // if type is unknown
return ListTile( return ListTile(
title: Text(t.newsParts.unknownNews(type: news.type)), title: Text(t.newsParts.unknownNews(type: news.type)),
subtitle: Text(timestamp(news.timestamp)), subtitle: Text(_dateFormat.format(news.timestamp)),
); );
} }
} }

View File

@ -1,12 +1,13 @@
import 'dart:io'; import 'dart:io';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:tetra_stats/data_objects/tetrio.dart'; import 'package:tetra_stats/data_objects/tetrio.dart';
import 'package:tetra_stats/main.dart' show packageInfo, teto, prefs; import 'package:tetra_stats/main.dart' show packageInfo, teto;
import 'package:file_selector/file_selector.dart'; import 'package:file_selector/file_selector.dart';
import 'package:file_picker/file_picker.dart'; import 'package:file_picker/file_picker.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:shared_preferences/shared_preferences.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/open_in_browser.dart'; import 'package:tetra_stats/utils/open_in_browser.dart';
@ -23,6 +24,7 @@ class SettingsView extends StatefulWidget {
} }
class SettingsState extends State<SettingsView> { class SettingsState extends State<SettingsView> {
late SharedPreferences prefs;
String defaultNickname = "Checking..."; String defaultNickname = "Checking...";
late bool showPositions; late bool showPositions;
late bool updateInBG; late bool updateInBG;
@ -44,7 +46,8 @@ class SettingsState extends State<SettingsView> {
super.dispose(); super.dispose();
} }
void _getPreferences() { Future<void> _getPreferences() async {
prefs = await SharedPreferences.getInstance();
showPositions = prefs.getBool("showPositions") ?? false; showPositions = prefs.getBool("showPositions") ?? false;
updateInBG = prefs.getBool("updateInBG") ?? false; updateInBG = prefs.getBool("updateInBG") ?? false;
_setDefaultNickname(prefs.getString("player")); _setDefaultNickname(prefs.getString("player"));
@ -261,10 +264,10 @@ class SettingsState extends State<SettingsView> {
subtitle: Text(t.customizationDescription, style: const TextStyle(fontFamily: "Eurostile Round Condensed", color: Colors.grey)), subtitle: Text(t.customizationDescription, style: const TextStyle(fontFamily: "Eurostile Round Condensed", color: Colors.grey)),
trailing: const Icon(Icons.arrow_right), trailing: const Icon(Icons.arrow_right),
onTap: () { onTap: () {
context.go("/settings/customization"); context.go("/customization");
},), },),
ListTile(title: Text("Update stats in the background"), ListTile(title: Text("Update stats in the background"),
subtitle: Text("While tetra stats is running, it can update stats of the current player when cache expires", style: const TextStyle(fontFamily: "Eurostile Round Condensed", color: Colors.grey)), subtitle: Text("While tetra stats is running, it can update stats of the current player when cache expires, as well, as tetra league stats of tracked players", style: const TextStyle(fontFamily: "Eurostile Round Condensed", color: Colors.grey)),
trailing: Switch(value: updateInBG, onChanged: (bool value){ trailing: Switch(value: updateInBG, onChanged: (bool value){
prefs.setBool("updateInBG", value); prefs.setBool("updateInBG", value);
setState(() { setState(() {

View File

@ -7,7 +7,6 @@ import 'package:tetra_stats/gen/strings.g.dart';
import 'package:tetra_stats/main.dart' show teto; import 'package:tetra_stats/main.dart' show teto;
import 'package:tetra_stats/views/mathes_view.dart'; import 'package:tetra_stats/views/mathes_view.dart';
import 'package:tetra_stats/views/state_view.dart'; import 'package:tetra_stats/views/state_view.dart';
import 'package:tetra_stats/widgets/text_timestamp.dart';
import 'package:window_manager/window_manager.dart'; import 'package:window_manager/window_manager.dart';
class StatesView extends StatefulWidget { class StatesView extends StatefulWidget {
@ -39,6 +38,7 @@ class StatesState extends State<StatesView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final t = Translations.of(context); final t = Translations.of(context);
final DateFormat dateFormat = DateFormat.yMMMd(LocaleSettings.currentLocale.languageCode).add_Hms();
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(t.statesViewTitle(number: widget.states.length, nickname: widget.states.last.username.toUpperCase())), title: Text(t.statesViewTitle(number: widget.states.length, nickname: widget.states.last.username.toUpperCase())),
@ -60,14 +60,14 @@ class StatesState extends State<StatesView> {
itemCount: widget.states.length, itemCount: widget.states.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
return ListTile( return ListTile(
title: Text(timestamp(widget.states[index].state)), title: Text(dateFormat.format(widget.states[index].state)),
subtitle: Text(t.statesViewEntry(level: widget.states[index].level.toStringAsFixed(2), gameTime: widget.states[index].gameTime, friends: widget.states[index].friendCount, rd: NumberFormat.compact().format(widget.states[index].tlSeason1.rd))), subtitle: Text(t.statesViewEntry(level: widget.states[index].level.toStringAsFixed(2), gameTime: widget.states[index].gameTime, friends: widget.states[index].friendCount, rd: NumberFormat.compact().format(widget.states[index].tlSeason1.rd))),
trailing: IconButton( trailing: IconButton(
icon: const Icon(Icons.delete_forever), icon: const Icon(Icons.delete_forever),
onPressed: () { onPressed: () {
DateTime nn = widget.states[index].state; DateTime nn = widget.states[index].state;
teto.deleteState(widget.states[index]).then((value) => setState(() { teto.deleteState(widget.states[index]).then((value) => setState(() {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.stateRemoved(date: timestamp(nn))))); ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.stateRemoved(date: dateFormat.format(nn)))));
})); }));
}, },
), ),

View File

@ -174,8 +174,8 @@ class TLLeaderboardState extends State<TLLeaderboardView> {
prototypeItem: ListTile( prototypeItem: ListTile(
leading: Text("0", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 28 : 24, height: 0.9)), leading: Text("0", style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 28 : 24, height: 0.9)),
title: Text("ehhh...", style: TextStyle(fontFamily: bigScreen ? "Eurostile Round Extended" : "Eurostile Round", height: 0.9)), title: Text("ehhh...", style: TextStyle(fontFamily: bigScreen ? "Eurostile Round Extended" : "Eurostile Round", height: 0.9)),
trailing: SizedBox(height: bigScreen ? 48 : 36, width: 1,), trailing: Container(height: bigScreen ? 48 : 36, width: 1,),
subtitle: const Text("eh..."), subtitle: Text("eh..."),
), ),
itemBuilder: (context, index) { itemBuilder: (context, index) {
return ListTile( return ListTile(

View File

@ -5,7 +5,6 @@ import 'package:tetra_stats/data_objects/tetrio_multiplayer_replay.dart';
import 'package:tetra_stats/services/crud_exceptions.dart'; import 'package:tetra_stats/services/crud_exceptions.dart';
import 'package:tetra_stats/views/compare_view.dart' show CompareThingy, CompareBoolThingy; import 'package:tetra_stats/views/compare_view.dart' show CompareThingy, CompareBoolThingy;
import 'package:tetra_stats/widgets/list_tile_trailing_stats.dart'; import 'package:tetra_stats/widgets/list_tile_trailing_stats.dart';
import 'package:tetra_stats/widgets/text_timestamp.dart';
import 'package:tetra_stats/widgets/vs_graphs.dart'; import 'package:tetra_stats/widgets/vs_graphs.dart';
import 'main_view.dart' show secs; import 'main_view.dart' show secs;
import 'package:tetra_stats/main.dart' show teto; import 'package:tetra_stats/main.dart' show teto;
@ -16,8 +15,11 @@ import 'package:tetra_stats/data_objects/tetrio.dart';
import 'package:tetra_stats/gen/strings.g.dart'; import 'package:tetra_stats/gen/strings.g.dart';
import 'package:tetra_stats/utils/open_in_browser.dart'; import 'package:tetra_stats/utils/open_in_browser.dart';
import 'package:window_manager/window_manager.dart'; import 'package:window_manager/window_manager.dart';
// ignore: avoid_web_libraries_in_flutter
// import 'dart:html' show AnchorElement, document;
final DateFormat dateFormat = DateFormat.yMMMd(LocaleSettings.currentLocale.languageCode).add_Hms();
int roundSelector = -1; // -1 = match averages, otherwise round number-1 int roundSelector = -1; // -1 = match averages, otherwise round number-1
List<DropdownMenuItem> rounds = []; // index zero will be match stats List<DropdownMenuItem> rounds = []; // index zero will be match stats
bool timeWeightedStatsAvaliable = true; bool timeWeightedStatsAvaliable = true;
@ -48,7 +50,7 @@ class TlMatchResultState extends State<TlMatchResultView> {
replayData = teto.analyzeReplay(widget.record.replayId, widget.record.replayAvalable); replayData = teto.analyzeReplay(widget.record.replayId, widget.record.replayAvalable);
if (!kIsWeb && !Platform.isAndroid && !Platform.isIOS){ if (!kIsWeb && !Platform.isAndroid && !Platform.isIOS){
windowManager.getTitle().then((value) => oldWindowTitle = value); windowManager.getTitle().then((value) => oldWindowTitle = value);
windowManager.setTitle("Tetra Stats: ${widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).username.toUpperCase()} ${t.vs} ${widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).username.toUpperCase()} ${t.inTLmatch} ${timestamp(widget.record.timestamp)}"); windowManager.setTitle("Tetra Stats: ${widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).username.toUpperCase()} ${t.vs} ${widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).username.toUpperCase()} ${t.inTLmatch} ${dateFormat.format(widget.record.timestamp)}");
} }
super.initState(); super.initState();
} }
@ -707,7 +709,7 @@ class TlMatchResultState extends State<TlMatchResultView> {
final t = Translations.of(context); final t = Translations.of(context);
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text("${widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).username.toUpperCase()} ${t.vs} ${widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).username.toUpperCase()} ${t.inTLmatch} ${timestamp(widget.record.timestamp)}"), title: Text("${widget.record.endContext.firstWhere((element) => element.userId == widget.initPlayerId).username.toUpperCase()} ${t.vs} ${widget.record.endContext.firstWhere((element) => element.userId != widget.initPlayerId).username.toUpperCase()} ${t.inTLmatch} ${dateFormat.format(widget.record.timestamp)}"),
actions: [ actions: [
PopupMenuButton( PopupMenuButton(
enabled: widget.record.replayAvalable, enabled: widget.record.replayAvalable,

View File

@ -7,7 +7,6 @@ import 'package:tetra_stats/gen/strings.g.dart';
import 'package:tetra_stats/main.dart' show teto; import 'package:tetra_stats/main.dart' show teto;
import 'package:tetra_stats/utils/filesizes_converter.dart'; import 'package:tetra_stats/utils/filesizes_converter.dart';
import 'package:tetra_stats/views/states_view.dart'; import 'package:tetra_stats/views/states_view.dart';
import 'package:tetra_stats/widgets/text_timestamp.dart';
import 'package:window_manager/window_manager.dart'; import 'package:window_manager/window_manager.dart';
late String oldWindowTitle; late String oldWindowTitle;
@ -38,6 +37,7 @@ class TrackedPlayersState extends State<TrackedPlayersView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final t = Translations.of(context); final t = Translations.of(context);
final DateFormat dateFormat = DateFormat.yMMMd(LocaleSettings.currentLocale.languageCode).add_Hms();
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(t.trackedPlayersViewTitle), title: Text(t.trackedPlayersViewTitle),
@ -109,7 +109,7 @@ class TrackedPlayersState extends State<TrackedPlayersView> {
itemBuilder: (context, index) { itemBuilder: (context, index) {
return ListTile( return ListTile(
title: Text(t.trackedPlayersEntry(nickname: allPlayers[keys[index]]!.last.username, numberOfStates: allPlayers[keys[index]]!.length)), title: Text(t.trackedPlayersEntry(nickname: allPlayers[keys[index]]!.last.username, numberOfStates: allPlayers[keys[index]]!.length)),
subtitle: Text(t.trackedPlayersDescription(firstStateDate: timestamp(allPlayers[keys[index]]!.first.state), lastStateDate: timestamp(allPlayers[keys[index]]!.last.state))), subtitle: Text(t.trackedPlayersDescription(firstStateDate: dateFormat.format(allPlayers[keys[index]]!.first.state), lastStateDate: dateFormat.format(allPlayers[keys[index]]!.last.state))),
trailing: IconButton( trailing: IconButton(
icon: const Icon(Icons.delete_forever), icon: const Icon(Icons.delete_forever),
onPressed: () { onPressed: () {

View File

@ -1,21 +0,0 @@
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/relative_timestamps.dart';
final DateFormat dateFormat = DateFormat.yMMMd(LocaleSettings.currentLocale.languageCode).add_Hms();
String timestamp(DateTime dateTime){
int timestampMode = prefs.getInt("timestampMode")??0;
return timestampMode == 2 ? relativeDateTime(dateTime) : dateFormat.format(timestampMode == 1 ? dateTime.toLocal() : dateTime);
}
// class TextTimestamp extends StatelessWidget{
// @override
// Widget build(BuildContext context) {
// // TODO: implement build
// return;
// }
// }

View File

@ -3,17 +3,18 @@ import 'package:intl/intl.dart';
import 'package:tetra_stats/data_objects/tetrio.dart'; import 'package:tetra_stats/data_objects/tetrio.dart';
import 'package:syncfusion_flutter_gauges/gauges.dart'; import 'package:syncfusion_flutter_gauges/gauges.dart';
import 'package:tetra_stats/gen/strings.g.dart'; import 'package:tetra_stats/gen/strings.g.dart';
import 'package:tetra_stats/utils/colors_functions.dart'; import 'package:tetra_stats/utils/colors_functions.dart';
import 'package:tetra_stats/utils/numers_formats.dart'; import 'package:tetra_stats/utils/numers_formats.dart';
import 'package:tetra_stats/widgets/gauget_num.dart'; import 'package:tetra_stats/widgets/gauget_num.dart';
import 'package:tetra_stats/widgets/graphs.dart'; import 'package:tetra_stats/widgets/graphs.dart';
import 'package:tetra_stats/widgets/stat_sell_num.dart'; import 'package:tetra_stats/widgets/stat_sell_num.dart';
import 'package:tetra_stats/widgets/text_timestamp.dart';
import 'package:tetra_stats/widgets/tl_progress_bar.dart'; import 'package:tetra_stats/widgets/tl_progress_bar.dart';
import 'package:tetra_stats/widgets/tl_rating_thingy.dart'; import 'package:tetra_stats/widgets/tl_rating_thingy.dart';
var intFDiff = NumberFormat("+#,###;-#,###"); var intFDiff = NumberFormat("+#,###;-#,###");
final DateFormat dateFormat = DateFormat.yMMMd(LocaleSettings.currentLocale.languageCode).add_Hms();
class TLThingy extends StatefulWidget { class TLThingy extends StatefulWidget {
final TetraLeagueAlpha tl; final TetraLeagueAlpha tl;
@ -68,7 +69,7 @@ class _TLThingyState extends State<TLThingy> {
return Column( return Column(
children: [ children: [
if (widget.showTitle) Text(t.tetraLeague, style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)), if (widget.showTitle) Text(t.tetraLeague, style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)),
if (oldTl != null) Text(t.comparingWith(newDate: timestamp(currentTl.timestamp), oldDate: timestamp(oldTl!.timestamp)), if (oldTl != null) Text(t.comparingWith(newDate: dateFormat.format(currentTl.timestamp), oldDate: dateFormat.format(oldTl!.timestamp)),
textAlign: TextAlign.center,), textAlign: TextAlign.center,),
if (oldTl != null) RangeSlider(values: _currentRangeValues, max: widget.states.length.toDouble(), if (oldTl != null) RangeSlider(values: _currentRangeValues, max: widget.states.length.toDouble(),
labels: RangeLabels( labels: RangeLabels(

View File

@ -10,7 +10,6 @@ import 'package:intl/intl.dart';
import 'package:tetra_stats/utils/text_shadow.dart'; import 'package:tetra_stats/utils/text_shadow.dart';
import 'dart:developer' as developer; import 'dart:developer' as developer;
import 'package:tetra_stats/widgets/stat_sell_num.dart'; import 'package:tetra_stats/widgets/stat_sell_num.dart';
import 'package:tetra_stats/widgets/text_timestamp.dart';
const Map<int, double> xpTableScuffed = { // level: xp required const Map<int, double> xpTableScuffed = { // level: xp required
05000: 67009018.4885772, 05000: 67009018.4885772,
@ -37,6 +36,7 @@ class UserThingy extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final t = Translations.of(context); final t = Translations.of(context);
final DateFormat dateFormat = DateFormat.yMMMd(LocaleSettings.currentLocale.languageCode).add_Hms();
return LayoutBuilder(builder: (context, constraints) { return LayoutBuilder(builder: (context, constraints) {
bool bigScreen = constraints.maxWidth > 768; bool bigScreen = constraints.maxWidth > 768;
double bannerHeight = bigScreen ? 240 : 120; double bannerHeight = bigScreen ? 240 : 120;
@ -126,7 +126,7 @@ class UserThingy extends StatelessWidget {
], ],
), ),
showStateTimestamp showStateTimestamp
? Text(t.fetchDate(date: timestamp(player.state))) ? Text(t.fetchDate(date: dateFormat.format(player.state)))
: Wrap(direction: Axis.horizontal, alignment: WrapAlignment.center, spacing: 25, crossAxisAlignment: WrapCrossAlignment.start, children: [ : Wrap(direction: Axis.horizontal, alignment: WrapAlignment.center, spacing: 25, crossAxisAlignment: WrapCrossAlignment.start, children: [
FutureBuilder( FutureBuilder(
future: teto.isPlayerTracking(player.userId), future: teto.isPlayerTracking(player.userId),
@ -340,7 +340,7 @@ class UserThingy extends StatelessWidget {
), ),
children: [ children: [
if (player.country != null) TextSpan(text: "${t.countries[player.country]}"), if (player.country != null) TextSpan(text: "${t.countries[player.country]}"),
TextSpan(text: "${t.playerRole[player.role]}${t.playerRoleAccount}${player.registrationTime == null ? t.wasFromBeginning : '${t.created} ${timestamp(player.registrationTime!)}'}"), TextSpan(text: "${t.playerRole[player.role]}${t.playerRoleAccount}${player.registrationTime == null ? t.wasFromBeginning : '${t.created} ${dateFormat.format(player.registrationTime!)}'}"),
if (player.supporterTier > 0) const TextSpan(text: ""), if (player.supporterTier > 0) const TextSpan(text: ""),
if (player.supporterTier > 0) WidgetSpan(child: Icon(player.supporterTier > 1 ? Icons.star : Icons.star_border, color: player.supporterTier > 1 ? Colors.yellowAccent : Colors.white), alignment: PlaceholderAlignment.middle, baseline: TextBaseline.alphabetic), if (player.supporterTier > 0) WidgetSpan(child: Icon(player.supporterTier > 1 ? Icons.star : Icons.star_border, color: player.supporterTier > 1 ? Colors.yellowAccent : Colors.white), alignment: PlaceholderAlignment.middle, baseline: TextBaseline.alphabetic),
if (player.supporterTier > 0) TextSpan(text: player.supporterTier.toString(), style: TextStyle(color: player.supporterTier > 1 ? Colors.yellowAccent : Colors.white)) if (player.supporterTier > 0) TextSpan(text: player.supporterTier.toString(), style: TextStyle(color: player.supporterTier > 1 ? Colors.yellowAccent : Colors.white))
@ -386,7 +386,7 @@ class UserThingy extends StatelessWidget {
children: [ children: [
Image.asset("res/tetrio_badges/${badge.badgeId}.png"), Image.asset("res/tetrio_badges/${badge.badgeId}.png"),
Text(badge.ts != null Text(badge.ts != null
? t.obtainDate(date: timestamp(badge.ts!)) ? t.obtainDate(date: dateFormat.format(badge.ts!))
: t.assignedManualy), : t.assignedManualy),
], ],
) )