Compare commits

...

2 Commits

Author SHA1 Message Date
dan63047 9394b818cc I'm stupid 2024-06-11 23:51:57 +03:00
dan63047 751e7a7071 ustomization -> Relative timestamps 2024-06-11 19:30:13 +03:00
13 changed files with 205 additions and 78 deletions

View File

@ -1,13 +1,18 @@
import 'dart:async';
import 'dart:io';
import 'dart:isolate';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
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/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:sqflite_common_ffi/sqflite_ffi.dart';
import 'package:sqflite_common_ffi_web/sqflite_ffi_web.dart';
@ -24,6 +29,38 @@ late SharedPreferences prefs;
late TetrioService teto;
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???
sheme = ColorScheme.dark(primary: color, secondary: Colors.white);
}
@ -38,6 +75,26 @@ final router = GoRouter(
GoRoute(
path: 'settings',
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(
path: 'states',
@ -48,9 +105,9 @@ final router = GoRouter(
builder: (_, __) => const CalcView(),
),
GoRoute(
path: 'customization',
builder: (_, __) => const CustomizationView(),
),
path: 'sprintAndBlitzAverages',
builder: (_, __) => const SprintAndBlitzView(),
)
]
),
GoRoute( // that one intended for Android users, that can open https://ch.tetr.io/u/ links
@ -92,10 +149,10 @@ void main() async {
}
// I dont want to store old cache
Timer.periodic(Duration(minutes: 5), (Timer timer) async {
Timer.periodic(Duration(minutes: 5), (Timer timer) {
teto.cacheRoutine();
developer.log("Cache routine complete, next one in ${DateTime.now().add(Duration(minutes: 5))}", name: "main");
if (prefs.getBool("updateInBG") == true) await teto.fetchTracked();
// if (prefs.getBool("updateInBG") == true) teto.fetchTracked(); // TODO: Somehow avoid doing that in main isolate
});
runApp(TranslationProvider(

View File

@ -8,5 +8,6 @@ final NumberFormat f4 = NumberFormat.decimalPatternDigits(locale: LocaleSettings
final NumberFormat f3 = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 3);
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 f1 = NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 1);
final NumberFormat f0 = NumberFormat.decimalPattern(LocaleSettings.currentLocale.languageCode);
final NumberFormat percentage = NumberFormat.percentPattern(LocaleSettings.currentLocale.languageCode)..maximumFractionDigits = 2;

View File

@ -0,0 +1,59 @@
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/material.dart';
import 'package:tetra_stats/views/settings_view.dart' show subtitleStyle;
import 'package:shared_preferences/shared_preferences.dart';
import 'package:tetra_stats/main.dart' show prefs;
import 'package:tetra_stats/gen/strings.g.dart';
import 'package:window_manager/window_manager.dart';
@ -18,10 +18,10 @@ class CustomizationView extends StatefulWidget {
}
class CustomizationState extends State<CustomizationView> {
late SharedPreferences prefs;
late bool oskKagariGimmick;
late bool sheetbotRadarGraphs;
late int ratingMode;
late int timestampMode;
void changeColor(Color color) {
setState(() => pickerColor = color);
@ -33,7 +33,7 @@ class CustomizationState extends State<CustomizationView> {
windowManager.getTitle().then((value) => oldWindowTitle = value);
windowManager.setTitle("Tetra Stats: ${t.settings}");
}
_getPreferences().then((value) => setState((){}));
_getPreferences();
super.initState();
}
@ -43,8 +43,7 @@ class CustomizationState extends State<CustomizationView> {
super.dispose();
}
Future<void> _getPreferences() async {
prefs = await SharedPreferences.getInstance();
void _getPreferences() {
if (prefs.getBool("oskKagariGimmick") != null) {
oskKagariGimmick = prefs.getBool("oskKagariGimmick")!;
} else {
@ -60,6 +59,11 @@ class CustomizationState extends State<CustomizationView> {
} else {
ratingMode = 0;
}
if (prefs.getInt("timestampMode") != null) {
timestampMode = prefs.getInt("timestampMode")!;
} else {
timestampMode = 0;
}
}
ThemeData getTheme(BuildContext context, Color color){
@ -125,6 +129,23 @@ class CustomizationState extends State<CustomizationView> {
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"),
subtitle: Text(t.oskKagariDescription, style: subtitleStyle),
trailing: DropdownButton(
@ -141,13 +162,6 @@ 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"),
subtitle: Text(t.oskKagariDescription, style: subtitleStyle),

View File

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

View File

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

View File

@ -7,6 +7,7 @@ import 'package:tetra_stats/gen/strings.g.dart';
import 'package:tetra_stats/main.dart' show teto;
import 'package:tetra_stats/views/mathes_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';
class StatesView extends StatefulWidget {
@ -38,7 +39,6 @@ class StatesState extends State<StatesView> {
@override
Widget build(BuildContext context) {
final t = Translations.of(context);
final DateFormat dateFormat = DateFormat.yMMMd(LocaleSettings.currentLocale.languageCode).add_Hms();
return Scaffold(
appBar: AppBar(
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,
itemBuilder: (context, index) {
return ListTile(
title: Text(dateFormat.format(widget.states[index].state)),
title: Text(timestamp(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))),
trailing: IconButton(
icon: const Icon(Icons.delete_forever),
onPressed: () {
DateTime nn = widget.states[index].state;
teto.deleteState(widget.states[index]).then((value) => setState(() {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.stateRemoved(date: dateFormat.format(nn)))));
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t.stateRemoved(date: timestamp(nn)))));
}));
},
),

View File

@ -174,8 +174,8 @@ class TLLeaderboardState extends State<TLLeaderboardView> {
prototypeItem: ListTile(
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)),
trailing: Container(height: bigScreen ? 48 : 36, width: 1,),
subtitle: Text("eh..."),
trailing: SizedBox(height: bigScreen ? 48 : 36, width: 1,),
subtitle: const Text("eh..."),
),
itemBuilder: (context, index) {
return ListTile(

View File

@ -5,6 +5,7 @@ import 'package:tetra_stats/data_objects/tetrio_multiplayer_replay.dart';
import 'package:tetra_stats/services/crud_exceptions.dart';
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/text_timestamp.dart';
import 'package:tetra_stats/widgets/vs_graphs.dart';
import 'main_view.dart' show secs;
import 'package:tetra_stats/main.dart' show teto;
@ -15,11 +16,8 @@ import 'package:tetra_stats/data_objects/tetrio.dart';
import 'package:tetra_stats/gen/strings.g.dart';
import 'package:tetra_stats/utils/open_in_browser.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
List<DropdownMenuItem> rounds = []; // index zero will be match stats
bool timeWeightedStatsAvaliable = true;
@ -50,7 +48,7 @@ class TlMatchResultState extends State<TlMatchResultView> {
replayData = teto.analyzeReplay(widget.record.replayId, widget.record.replayAvalable);
if (!kIsWeb && !Platform.isAndroid && !Platform.isIOS){
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} ${dateFormat.format(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} ${timestamp(widget.record.timestamp)}");
}
super.initState();
}
@ -709,7 +707,7 @@ class TlMatchResultState extends State<TlMatchResultView> {
final t = Translations.of(context);
return Scaffold(
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} ${dateFormat.format(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} ${timestamp(widget.record.timestamp)}"),
actions: [
PopupMenuButton(
enabled: widget.record.replayAvalable,

View File

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

View File

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

View File

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