Now it's possible to set player for app init.
Also added some logging, Changed come things. Now i gonna think about sqlite database
This commit is contained in:
parent
d69713664f
commit
10f4063bf2
|
@ -1,5 +1,6 @@
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
import 'package:vector_math/vector_math.dart';
|
import 'package:vector_math/vector_math.dart';
|
||||||
|
import 'dart:developer' as developer;
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
|
@ -70,7 +71,8 @@ class TetrioPlayer {
|
||||||
|
|
||||||
double get level => pow((xp / 500), 0.6) + (xp / (5000 + (max(0, xp - 4 * pow(10, 6)) / 5000))) + 1;
|
double get level => pow((xp / 500), 0.6) + (xp / (5000 + (max(0, xp - 4 * pow(10, 6)) / 5000))) + 1;
|
||||||
|
|
||||||
TetrioPlayer.fromJson(Map<String, dynamic> json, DateTime stateTime) {
|
TetrioPlayer.fromJson(Map<String, dynamic> json, DateTime stateTime, bool fetchRecords) {
|
||||||
|
developer.log("TetrioPlayer.fromJson $stateTime: $json", name: "data_objects/tetrio");
|
||||||
userId = json['_id'];
|
userId = json['_id'];
|
||||||
username = json['username'];
|
username = json['username'];
|
||||||
state = stateTime;
|
state = stateTime;
|
||||||
|
@ -95,23 +97,26 @@ class TetrioPlayer {
|
||||||
connections = Connections.fromJson(json['connections']);
|
connections = Connections.fromJson(json['connections']);
|
||||||
distinguishment = json['distinguishment'] != null ? Distinguishment.fromJson(json['distinguishment']) : null;
|
distinguishment = json['distinguishment'] != null ? Distinguishment.fromJson(json['distinguishment']) : null;
|
||||||
friendCount = json['friend_count'] ?? 0;
|
friendCount = json['friend_count'] ?? 0;
|
||||||
var url = Uri.https('ch.tetr.io', 'api/users/$userId/records');
|
|
||||||
Future response = http.get(url);
|
|
||||||
response.then((value) {
|
|
||||||
if (value.statusCode == 200) {
|
|
||||||
Map jsonRecords = jsonDecode(value.body);
|
|
||||||
sprint = jsonRecords['data']['records']['40l']['record'] != null
|
|
||||||
? [RecordSingle.fromJson(jsonRecords['data']['records']['40l']['record'], jsonRecords['data']['records']['40l']['rank'])]
|
|
||||||
: [];
|
|
||||||
blitz = jsonRecords['data']['records']['blitz']['record'] != null
|
|
||||||
? [RecordSingle.fromJson(jsonRecords['data']['records']['blitz']['record'], jsonRecords['data']['records']['blitz']['rank'])]
|
|
||||||
: [];
|
|
||||||
zen = TetrioZen.fromJson(jsonRecords['data']['zen']);
|
|
||||||
} else {
|
|
||||||
throw Exception('Failed to fetch player');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
badstanding = json['badstanding'];
|
badstanding = json['badstanding'];
|
||||||
|
if (fetchRecords) {
|
||||||
|
var url = Uri.https('ch.tetr.io', 'api/users/$userId/records');
|
||||||
|
Future response = http.get(url);
|
||||||
|
response.then((value) {
|
||||||
|
if (value.statusCode == 200) {
|
||||||
|
Map jsonRecords = jsonDecode(value.body);
|
||||||
|
sprint = jsonRecords['data']['records']['40l']['record'] != null
|
||||||
|
? [RecordSingle.fromJson(jsonRecords['data']['records']['40l']['record'], jsonRecords['data']['records']['40l']['rank'])]
|
||||||
|
: [];
|
||||||
|
blitz = jsonRecords['data']['records']['blitz']['record'] != null
|
||||||
|
? [RecordSingle.fromJson(jsonRecords['data']['records']['blitz']['record'], jsonRecords['data']['records']['blitz']['rank'])]
|
||||||
|
: [];
|
||||||
|
zen = TetrioZen.fromJson(jsonRecords['data']['zen']);
|
||||||
|
} else {
|
||||||
|
developer.log("TetrioPlayer.fromJson exception", name: "data_objects/tetrio", error: value.statusCode);
|
||||||
|
throw Exception('Failed to fetch player');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
|
@ -137,6 +142,7 @@ class TetrioPlayer {
|
||||||
data['friend_count'] = friendCount;
|
data['friend_count'] = friendCount;
|
||||||
data['badstanding'] = badstanding;
|
data['badstanding'] = badstanding;
|
||||||
data['bot'] = bot;
|
data['bot'] = bot;
|
||||||
|
developer.log("TetrioPlayer.toJson: $bot", name: "data_objects/tetrio");
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -468,7 +474,6 @@ class EstTr {
|
||||||
final double _apm;
|
final double _apm;
|
||||||
final double _pps;
|
final double _pps;
|
||||||
final double _vs;
|
final double _vs;
|
||||||
final double _rating;
|
|
||||||
final double _rd;
|
final double _rd;
|
||||||
final double _app;
|
final double _app;
|
||||||
final double _dss;
|
final double _dss;
|
||||||
|
@ -478,7 +483,7 @@ class EstTr {
|
||||||
late double srarea;
|
late double srarea;
|
||||||
late double statrank;
|
late double statrank;
|
||||||
|
|
||||||
EstTr(this._apm, this._pps, this._vs, this._rating, this._rd, this._app, this._dss, this._dsp, this._gbe) {
|
EstTr(this._apm, this._pps, this._vs, this._rd, this._app, this._dss, this._dsp, this._gbe) {
|
||||||
srarea = (_apm * 0) + (_pps * 135) + (_vs * 0) + (_app * 290) + (_dss * 0) + (_dsp * 700) + (_gbe * 0);
|
srarea = (_apm * 0) + (_pps * 135) + (_vs * 0) + (_app * 290) + (_dss * 0) + (_dsp * 700) + (_gbe * 0);
|
||||||
statrank = 11.2 * atan((srarea - 93) / 130) + 1;
|
statrank = 11.2 * atan((srarea - 93) / 130) + 1;
|
||||||
if (statrank <= 0) statrank = 0.001;
|
if (statrank <= 0) statrank = 0.001;
|
||||||
|
@ -493,11 +498,10 @@ class EstTr {
|
||||||
class Playstyle {
|
class Playstyle {
|
||||||
final double _apm;
|
final double _apm;
|
||||||
final double _pps;
|
final double _pps;
|
||||||
final double _vs;
|
//final double _vs;
|
||||||
final double _rd;
|
|
||||||
final double _app;
|
final double _app;
|
||||||
final double _vsapm;
|
final double _vsapm;
|
||||||
final double _dss;
|
//final double _dss;
|
||||||
final double _dsp;
|
final double _dsp;
|
||||||
final double _gbe;
|
final double _gbe;
|
||||||
final double _srarea;
|
final double _srarea;
|
||||||
|
@ -507,12 +511,12 @@ class Playstyle {
|
||||||
late double stride;
|
late double stride;
|
||||||
late double infds;
|
late double infds;
|
||||||
|
|
||||||
Playstyle(this._apm, this._pps, this._vs, this._rd, this._app, this._vsapm, this._dss, this._dsp, this._gbe, this._srarea, this._statrank) {
|
Playstyle(this._apm, this._pps, this._app, this._vsapm, this._dsp, this._gbe, this._srarea, this._statrank) {
|
||||||
double nmapm = ((_apm / _srarea) / ((0.069 * pow(1.0017, (pow(_statrank, 5) / 4700))) + _statrank / 360)) - 1;
|
double nmapm = ((_apm / _srarea) / ((0.069 * pow(1.0017, (pow(_statrank, 5) / 4700))) + _statrank / 360)) - 1;
|
||||||
double nmpps = ((_pps / _srarea) / (0.0084264 * pow(2.14, (-2 * (_statrank / 2.7 + 1.03))) - _statrank / 5750 + 0.0067)) - 1;
|
double nmpps = ((_pps / _srarea) / (0.0084264 * pow(2.14, (-2 * (_statrank / 2.7 + 1.03))) - _statrank / 5750 + 0.0067)) - 1;
|
||||||
double nmvs = ((_vs / _srarea) / (0.1333 * pow(1.0021, ((pow(_statrank, 7) * (_statrank / 16.5)) / 1400000)) + _statrank / 133)) - 1;
|
//double nmvs = ((_vs / _srarea) / (0.1333 * pow(1.0021, ((pow(_statrank, 7) * (_statrank / 16.5)) / 1400000)) + _statrank / 133)) - 1;
|
||||||
double nmapp = (_app / (0.1368803292 * pow(1.0024, (pow(_statrank, 5) / 2800)) + _statrank / 54)) - 1;
|
double nmapp = (_app / (0.1368803292 * pow(1.0024, (pow(_statrank, 5) / 2800)) + _statrank / 54)) - 1;
|
||||||
double nmdss = (_dss / (0.01436466667 * pow(4.1, ((_statrank - 9.6) / 2.9)) + _statrank / 140 + 0.01)) - 1;
|
//double nmdss = (_dss / (0.01436466667 * pow(4.1, ((_statrank - 9.6) / 2.9)) + _statrank / 140 + 0.01)) - 1;
|
||||||
double nmdsp = (_dsp / (0.02136327583 * pow(14, ((_statrank - 14.75) / 3.9)) + _statrank / 152 + 0.022)) - 1;
|
double nmdsp = (_dsp / (0.02136327583 * pow(14, ((_statrank - 14.75) / 3.9)) + _statrank / 152 + 0.022)) - 1;
|
||||||
double nmgbe = (_gbe / (_statrank / 350 + 0.005948424455 * pow(3.8, ((_statrank - 6.1) / 4)) + 0.006)) - 1;
|
double nmgbe = (_gbe / (_statrank / 350 + 0.005948424455 * pow(3.8, ((_statrank - 6.1) / 4)) + 0.006)) - 1;
|
||||||
double nmvsapm = (_vsapm / (-pow(((_statrank - 16) / 36), 2) + 2.133)) - 1;
|
double nmvsapm = (_vsapm / (-pow(((_statrank - 16) / 36), 2) + 2.133)) - 1;
|
||||||
|
@ -662,12 +666,9 @@ class TetraLeagueAlpha {
|
||||||
nextAt = json['next_at'];
|
nextAt = json['next_at'];
|
||||||
percentileRank = json['percentile_rank'];
|
percentileRank = json['percentile_rank'];
|
||||||
nerdStats = (apm != null && pps != null && apm != null) ? NerdStats(apm!, pps!, vs!) : null;
|
nerdStats = (apm != null && pps != null && apm != null) ? NerdStats(apm!, pps!, vs!) : null;
|
||||||
estTr =
|
estTr = (nerdStats != null) ? EstTr(apm!, pps!, vs!, (rd != null) ? rd! : 69, nerdStats!.app, nerdStats!.dss, nerdStats!.dsp, nerdStats!.gbe) : null;
|
||||||
(nerdStats != null) ? EstTr(apm!, pps!, vs!, rating, (rd != null) ? rd! : 69, nerdStats!.app, nerdStats!.dss, nerdStats!.dsp, nerdStats!.gbe) : null;
|
playstyle =
|
||||||
playstyle = (nerdStats != null)
|
(nerdStats != null) ? Playstyle(apm!, pps!, nerdStats!.app, nerdStats!.vsapm, nerdStats!.dsp, nerdStats!.gbe, estTr!.srarea, estTr!.statrank) : null;
|
||||||
? Playstyle(apm!, pps!, vs!, (rd != null) ? rd! : 69, nerdStats!.app, nerdStats!.vsapm, nerdStats!.dss, nerdStats!.dsp, nerdStats!.gbe, estTr!.srarea,
|
|
||||||
estTr!.statrank)
|
|
||||||
: null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
double? get esttracc => (estTr != null) ? estTr!.esttr - rating : null;
|
double? get esttracc => (estTr != null) ? estTr!.esttr - rating : null;
|
||||||
|
@ -701,6 +702,7 @@ class RecordSingle {
|
||||||
late String userId;
|
late String userId;
|
||||||
late String replayId;
|
late String replayId;
|
||||||
late String ownId;
|
late String ownId;
|
||||||
|
late String stream;
|
||||||
DateTime? timestamp;
|
DateTime? timestamp;
|
||||||
EndContextSingle? endContext;
|
EndContextSingle? endContext;
|
||||||
int? rank;
|
int? rank;
|
||||||
|
@ -708,9 +710,11 @@ class RecordSingle {
|
||||||
RecordSingle({required this.userId, required this.replayId, required this.ownId, this.timestamp, this.endContext, this.rank});
|
RecordSingle({required this.userId, required this.replayId, required this.ownId, this.timestamp, this.endContext, this.rank});
|
||||||
|
|
||||||
RecordSingle.fromJson(Map<String, dynamic> json, int? ran) {
|
RecordSingle.fromJson(Map<String, dynamic> json, int? ran) {
|
||||||
|
developer.log("RecordSingle.fromJson: $json", name: "data_objects/tetrio");
|
||||||
ownId = json['_id'];
|
ownId = json['_id'];
|
||||||
endContext = json['endcontext'] != null ? EndContextSingle.fromJson(json['endcontext']) : null;
|
endContext = json['endcontext'] != null ? EndContextSingle.fromJson(json['endcontext']) : null;
|
||||||
replayId = json['replayid'];
|
replayId = json['replayid'];
|
||||||
|
stream = json['stream'];
|
||||||
timestamp = DateTime.parse(json['ts']);
|
timestamp = DateTime.parse(json['ts']);
|
||||||
userId = json['user']['_id'];
|
userId = json['user']['_id'];
|
||||||
rank = ran;
|
rank = ran;
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:path/path.dart';
|
|
||||||
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
||||||
import 'package:tetra_stats/views/main_view.dart';
|
import 'package:tetra_stats/views/main_view.dart';
|
||||||
import 'package:tetra_stats/views/compare_view.dart';
|
import 'package:tetra_stats/views/compare_view.dart';
|
||||||
|
@ -10,8 +9,10 @@ void main() {
|
||||||
sqfliteFfiInit();
|
sqfliteFfiInit();
|
||||||
databaseFactory = databaseFactoryFfi;
|
databaseFactory = databaseFactoryFfi;
|
||||||
runApp(MaterialApp(
|
runApp(MaterialApp(
|
||||||
home: const MainView(),
|
home: const MainView(),
|
||||||
routes: {"/settings": (context) => const SettingsView(), "/compare": (context) => const CompareView(), "/states": (context) => const StatesView()},
|
routes: {"/settings": (context) => const SettingsView(), "/compare": (context) => const CompareView(), "/states": (context) => const StatesView()},
|
||||||
theme: ThemeData(fontFamily: 'Eurostile Round', colorScheme: const ColorScheme.dark(), scaffoldBackgroundColor: Colors.black),
|
theme: ThemeData(
|
||||||
));
|
fontFamily: 'Eurostile Round',
|
||||||
|
colorScheme: const ColorScheme.dark(primary: Colors.cyanAccent, secondary: Colors.purpleAccent),
|
||||||
|
scaffoldBackgroundColor: Colors.black)));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
import 'dart:async';
|
|
||||||
import 'dart:convert';
|
|
||||||
import 'package:sqflite/sqflite.dart';
|
|
||||||
import 'package:path_provider/path_provider.dart'
|
|
||||||
show MissingPlatformDirectoryException, getApplicationDocumentsDirectory;
|
|
||||||
import 'package:tetra_stats/services/crud_exceptions.dart';
|
|
||||||
import 'package:path/path.dart' show join;
|
|
||||||
|
|
||||||
const String dbName = "TetraStats.db";
|
|
||||||
const String tetrioUsersTable = "settings";
|
|
||||||
const String userTetrioId = "userTetrioId";
|
|
||||||
const String createSettingsTable = '''
|
|
||||||
CREATE TABLE IF NOT EXISTS "settings" (
|
|
||||||
"userTetrioId" TEXT
|
|
||||||
)''';
|
|
||||||
|
|
||||||
class SettingsService {}
|
|
|
@ -1,11 +1,8 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
|
||||||
import 'package:sqflite/sqflite.dart';
|
import 'package:sqflite/sqflite.dart';
|
||||||
import 'package:path_provider/path_provider.dart'
|
import 'package:path_provider/path_provider.dart' show MissingPlatformDirectoryException, getApplicationDocumentsDirectory;
|
||||||
show MissingPlatformDirectoryException, getApplicationDocumentsDirectory;
|
|
||||||
import 'package:tetra_stats/services/crud_exceptions.dart';
|
import 'package:tetra_stats/services/crud_exceptions.dart';
|
||||||
import 'package:tetra_stats/services/tetrio_crud.dart';
|
import 'package:tetra_stats/services/tetrio_crud.dart';
|
||||||
import 'package:tetra_stats/services/settings_crud.dart';
|
|
||||||
import 'package:path/path.dart' show join;
|
import 'package:path/path.dart' show join;
|
||||||
|
|
||||||
const String dbName = "TetraStats.db";
|
const String dbName = "TetraStats.db";
|
||||||
|
@ -22,7 +19,6 @@ class DB {
|
||||||
final db = await openDatabase(dbPath);
|
final db = await openDatabase(dbPath);
|
||||||
_db = db;
|
_db = db;
|
||||||
await db.execute(createTetrioUsersTable);
|
await db.execute(createTetrioUsersTable);
|
||||||
await db.execute(createSettingsTable);
|
|
||||||
} on MissingPlatformDirectoryException {
|
} on MissingPlatformDirectoryException {
|
||||||
throw UnableToGetDocuments();
|
throw UnableToGetDocuments();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:sqflite/sqflite.dart';
|
import 'dart:developer' as developer;
|
||||||
import 'package:path_provider/path_provider.dart'
|
// import 'package:sqflite/sqflite.dart';
|
||||||
show MissingPlatformDirectoryException, getApplicationDocumentsDirectory;
|
// import 'package:path_provider/path_provider.dart' show MissingPlatformDirectoryException, getApplicationDocumentsDirectory;
|
||||||
import 'package:path/path.dart' show join;
|
// import 'package:path/path.dart' show join;
|
||||||
import 'package:tetra_stats/services/crud_exceptions.dart';
|
import 'package:tetra_stats/services/crud_exceptions.dart';
|
||||||
import 'package:tetra_stats/services/sqlite_db_controller.dart';
|
import 'package:tetra_stats/services/sqlite_db_controller.dart';
|
||||||
import 'package:tetra_stats/data_objects/tetrio.dart';
|
import 'package:tetra_stats/data_objects/tetrio.dart';
|
||||||
|
@ -23,19 +23,22 @@ const String createTetrioUsersTable = '''
|
||||||
|
|
||||||
class TetrioService {
|
class TetrioService {
|
||||||
Map<String, List<TetrioPlayer>> _players = {};
|
Map<String, List<TetrioPlayer>> _players = {};
|
||||||
final _tetrioStreamController =
|
final _tetrioStreamController = StreamController<Map<String, List<TetrioPlayer>>>.broadcast();
|
||||||
StreamController<Map<String, List<TetrioPlayer>>>.broadcast();
|
|
||||||
|
TetrioService(DB udb) {
|
||||||
|
_cachePlayers(udb);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _cachePlayers(DB udb) async {
|
Future<void> _cachePlayers(DB udb) async {
|
||||||
final allPlayers = await getAllPlayers(udb: udb);
|
final allPlayers = await getAllPlayers(udb: udb);
|
||||||
_players = allPlayers.first;
|
_players = allPlayers.first;
|
||||||
_tetrioStreamController.add(_players);
|
_tetrioStreamController.add(_players);
|
||||||
|
developer.log("_cachePlayers: $_players", name: "services/tetrio_crud");
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> deletePlayer({required String id, required DB udb}) async {
|
Future<void> deletePlayer({required String id, required DB udb}) async {
|
||||||
final db = udb.getDatabaseOrThrow();
|
final db = udb.getDatabaseOrThrow();
|
||||||
final deletedPlayer = await db.delete(tetrioUsersTable,
|
final deletedPlayer = await db.delete(tetrioUsersTable, where: '$idCol = ?', whereArgs: [id.toLowerCase()]);
|
||||||
where: '$idCol = ?', whereArgs: [id.toLowerCase()]);
|
|
||||||
if (deletedPlayer != 1) {
|
if (deletedPlayer != 1) {
|
||||||
throw CouldNotDeletePlayer();
|
throw CouldNotDeletePlayer();
|
||||||
} else {
|
} else {
|
||||||
|
@ -54,25 +57,14 @@ class TetrioService {
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
Future<void> createPlayer(
|
Future<void> createPlayer({required TetrioPlayer tetrioPlayer, required DB udb}) async {
|
||||||
{required TetrioPlayer tetrioPlayer, required DB udb}) async {
|
|
||||||
final db = udb.getDatabaseOrThrow();
|
final db = udb.getDatabaseOrThrow();
|
||||||
final results = await db.query(tetrioUsersTable,
|
final results = await db.query(tetrioUsersTable, limit: 1, where: '$idCol = ?', whereArgs: [tetrioPlayer.userId.toLowerCase()]);
|
||||||
limit: 1,
|
|
||||||
where: '$idCol = ?',
|
|
||||||
whereArgs: [tetrioPlayer.userId.toLowerCase()]);
|
|
||||||
if (results.isNotEmpty) {
|
if (results.isNotEmpty) {
|
||||||
throw TetrioPlayerAlreadyExist();
|
throw TetrioPlayerAlreadyExist();
|
||||||
}
|
}
|
||||||
final Map<String, dynamic> statesJson = {
|
final Map<String, dynamic> statesJson = {tetrioPlayer.state.millisecondsSinceEpoch.toString(): tetrioPlayer.toJson()};
|
||||||
tetrioPlayer.state.millisecondsSinceEpoch.toString():
|
db.insert(tetrioUsersTable, {idCol: tetrioPlayer.userId, nickCol: tetrioPlayer.username, statesCol: jsonEncode(statesJson)});
|
||||||
tetrioPlayer.toJson()
|
|
||||||
};
|
|
||||||
db.insert(tetrioUsersTable, {
|
|
||||||
idCol: tetrioPlayer.userId,
|
|
||||||
nickCol: tetrioPlayer.username,
|
|
||||||
statesCol: jsonEncode(statesJson)
|
|
||||||
});
|
|
||||||
_players.addEntries({
|
_players.addEntries({
|
||||||
tetrioPlayer.userId: [tetrioPlayer]
|
tetrioPlayer.userId: [tetrioPlayer]
|
||||||
}.entries);
|
}.entries);
|
||||||
|
@ -81,40 +73,28 @@ class TetrioService {
|
||||||
|
|
||||||
Future<void> storeState(TetrioPlayer tetrioPlayer, DB udb) async {
|
Future<void> storeState(TetrioPlayer tetrioPlayer, DB udb) async {
|
||||||
final db = udb.getDatabaseOrThrow();
|
final db = udb.getDatabaseOrThrow();
|
||||||
List<TetrioPlayer> states =
|
List<TetrioPlayer> states = await getPlayer(id: tetrioPlayer.userId, udb: udb);
|
||||||
await getPlayer(id: tetrioPlayer.userId, udb: udb);
|
|
||||||
states.add(tetrioPlayer);
|
states.add(tetrioPlayer);
|
||||||
final Map<String, dynamic> statesJson = {};
|
final Map<String, dynamic> statesJson = {};
|
||||||
for (var e in states) {
|
for (var e in states) {
|
||||||
statesJson.addEntries(
|
statesJson.addEntries({e.state.millisecondsSinceEpoch.toString(): e.toJson()}.entries);
|
||||||
{e.state.millisecondsSinceEpoch.toString(): e.toJson()}.entries);
|
|
||||||
}
|
}
|
||||||
db.update(
|
db.update(tetrioUsersTable, {idCol: tetrioPlayer.userId, nickCol: tetrioPlayer.username, statesCol: jsonEncode(statesJson)},
|
||||||
tetrioUsersTable,
|
where: '$idCol = ?', whereArgs: [tetrioPlayer.userId]);
|
||||||
{
|
|
||||||
idCol: tetrioPlayer.userId,
|
|
||||||
nickCol: tetrioPlayer.username,
|
|
||||||
statesCol: jsonEncode(statesJson)
|
|
||||||
},
|
|
||||||
where: '$idCol = ?',
|
|
||||||
whereArgs: [tetrioPlayer.userId]);
|
|
||||||
_players[tetrioPlayer.userId]!.add(tetrioPlayer);
|
_players[tetrioPlayer.userId]!.add(tetrioPlayer);
|
||||||
_tetrioStreamController.add(_players);
|
_tetrioStreamController.add(_players);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<TetrioPlayer>> getPlayer(
|
Future<List<TetrioPlayer>> getPlayer({required String id, required DB udb}) async {
|
||||||
{required String id, required DB udb}) async {
|
|
||||||
final db = udb.getDatabaseOrThrow();
|
final db = udb.getDatabaseOrThrow();
|
||||||
List<TetrioPlayer> states = [];
|
List<TetrioPlayer> states = [];
|
||||||
final results = await db.query(tetrioUsersTable,
|
final results = await db.query(tetrioUsersTable, limit: 1, where: '$idCol = ?', whereArgs: [id.toLowerCase()]);
|
||||||
limit: 1, where: '$idCol = ?', whereArgs: [id.toLowerCase()]);
|
|
||||||
if (results.isEmpty) {
|
if (results.isEmpty) {
|
||||||
throw TetrioPlayerNotExist();
|
throw TetrioPlayerNotExist();
|
||||||
} else {
|
} else {
|
||||||
dynamic rawStates = results.first['jsonStates'] as String;
|
dynamic rawStates = results.first['jsonStates'] as String;
|
||||||
rawStates = json.decode(rawStates);
|
rawStates = json.decode(rawStates);
|
||||||
rawStates.forEach((k, v) => states.add(TetrioPlayer.fromJson(
|
rawStates.forEach((k, v) => states.add(TetrioPlayer.fromJson(v, DateTime.fromMillisecondsSinceEpoch(int.parse(k)), false)));
|
||||||
v, DateTime.fromMillisecondsSinceEpoch(int.parse(k)))));
|
|
||||||
_players.removeWhere((key, value) => key == id);
|
_players.removeWhere((key, value) => key == id);
|
||||||
_players.addEntries({states.last.userId: states}.entries);
|
_players.addEntries({states.last.userId: states}.entries);
|
||||||
_tetrioStreamController.add(_players);
|
_tetrioStreamController.add(_players);
|
||||||
|
@ -122,17 +102,24 @@ class TetrioService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Iterable<Map<String, List<TetrioPlayer>>>> getAllPlayers(
|
Future<void> _ensureDbIsOpen(DB udb) async {
|
||||||
{required DB udb}) async {
|
try {
|
||||||
//await _ensureDbIsOpen();
|
await udb.open();
|
||||||
|
} on DatabaseAlreadyOpen {
|
||||||
|
// empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Iterable<Map<String, List<TetrioPlayer>>>> getAllPlayers({required DB udb}) async {
|
||||||
|
await _ensureDbIsOpen(udb);
|
||||||
final db = udb.getDatabaseOrThrow();
|
final db = udb.getDatabaseOrThrow();
|
||||||
final players = await db.query(tetrioUsersTable);
|
final players = await db.query(tetrioUsersTable);
|
||||||
Map<String, List<TetrioPlayer>> data = {};
|
Map<String, List<TetrioPlayer>> data = {};
|
||||||
|
//developer.log("getAllPlayers: $players", name: "services/tetrio_crud");
|
||||||
return players.map((row) {
|
return players.map((row) {
|
||||||
var test = json.decode(row['jsonStates'] as String);
|
var test = json.decode(row['jsonStates'] as String);
|
||||||
List<TetrioPlayer> states = [];
|
List<TetrioPlayer> states = [];
|
||||||
test.forEach(
|
test.forEach((k, v) => states.add(TetrioPlayer.fromJson(v, DateTime.fromMillisecondsSinceEpoch(int.parse(k)), false)));
|
||||||
(k, v) => states.add(TetrioPlayer.fromJson(v, DateTime.now())));
|
|
||||||
data.addEntries({states.last.userId: states}.entries);
|
data.addEntries({states.last.userId: states}.entries);
|
||||||
return data;
|
return data;
|
||||||
});
|
});
|
||||||
|
|
|
@ -12,12 +12,12 @@ class CompareState extends State<CompareView> {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text("you vs someone"),
|
title: const Text("you vs someone"),
|
||||||
),
|
),
|
||||||
backgroundColor: Colors.black,
|
backgroundColor: Colors.black,
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: ListView(
|
child: ListView(
|
||||||
children: [
|
children: const [
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Center(child: Text("So thats gonna be the main purpose of the app")),
|
title: Center(child: Text("So thats gonna be the main purpose of the app")),
|
||||||
subtitle: Center(child: Text("We gonna look who is the best")),
|
subtitle: Center(child: Text("We gonna look who is the best")),
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:path/path.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
import 'dart:developer' as developer;
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:tetra_stats/data_objects/tetrio.dart';
|
import 'package:tetra_stats/data_objects/tetrio.dart';
|
||||||
import 'package:tetra_stats/services/tetrio_crud.dart';
|
import 'package:tetra_stats/services/tetrio_crud.dart';
|
||||||
import 'package:tetra_stats/services/sqlite_db_controller.dart';
|
import 'package:tetra_stats/services/sqlite_db_controller.dart';
|
||||||
|
@ -16,7 +18,8 @@ extension StringExtension on String {
|
||||||
String _searchFor = "dan63047";
|
String _searchFor = "dan63047";
|
||||||
Future<TetrioPlayer>? me;
|
Future<TetrioPlayer>? me;
|
||||||
DB db = DB();
|
DB db = DB();
|
||||||
TetrioService teto = TetrioService();
|
late TetrioService teto;
|
||||||
|
late SharedPreferences prefs;
|
||||||
const allowedHeightForPlayerIdInPixels = 40.0;
|
const allowedHeightForPlayerIdInPixels = 40.0;
|
||||||
const allowedHeightForPlayerBioInPixels = 30.0;
|
const allowedHeightForPlayerBioInPixels = 30.0;
|
||||||
const givenTextHeightByScreenPercentage = 0.3;
|
const givenTextHeightByScreenPercentage = 0.3;
|
||||||
|
@ -30,6 +33,10 @@ class MainView extends StatefulWidget {
|
||||||
State<MainView> createState() => _MainState();
|
State<MainView> createState() => _MainState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> copyToClipboard(String text) async {
|
||||||
|
await Clipboard.setData(ClipboardData(text: text));
|
||||||
|
}
|
||||||
|
|
||||||
Future<TetrioPlayer> fetchTetrioPlayer(String user) async {
|
Future<TetrioPlayer> fetchTetrioPlayer(String user) async {
|
||||||
var url = Uri.https('ch.tetr.io', 'api/users/${user.toLowerCase().trim()}');
|
var url = Uri.https('ch.tetr.io', 'api/users/${user.toLowerCase().trim()}');
|
||||||
final response = await http.get(url);
|
final response = await http.get(url);
|
||||||
|
@ -37,11 +44,13 @@ Future<TetrioPlayer> fetchTetrioPlayer(String user) async {
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
if (jsonDecode(response.body)['success']) {
|
if (jsonDecode(response.body)['success']) {
|
||||||
return TetrioPlayer.fromJson(
|
return TetrioPlayer.fromJson(
|
||||||
jsonDecode(response.body)['data']['user'], DateTime.fromMillisecondsSinceEpoch(jsonDecode(response.body)['cache']['cached_at'], isUtc: true));
|
jsonDecode(response.body)['data']['user'], DateTime.fromMillisecondsSinceEpoch(jsonDecode(response.body)['cache']['cached_at'], isUtc: true), true);
|
||||||
} else {
|
} else {
|
||||||
|
developer.log("fetchTetrioPlayer User dosen't exist", name: "main_view", error: response.body);
|
||||||
throw Exception("User doesn't exist");
|
throw Exception("User doesn't exist");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
developer.log("fetchTetrioPlayer Failed to fetch player", name: "main_view", error: response.statusCode);
|
||||||
throw Exception('Failed to fetch player');
|
throw Exception('Failed to fetch player');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,7 +71,7 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
||||||
Widget _searchTextField() {
|
Widget _searchTextField() {
|
||||||
return TextField(
|
return TextField(
|
||||||
maxLength: 25,
|
maxLength: 25,
|
||||||
decoration: InputDecoration(counter: Offstage()),
|
decoration: const InputDecoration(counter: Offstage()),
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
shadows: <Shadow>[
|
shadows: <Shadow>[
|
||||||
Shadow(
|
Shadow(
|
||||||
|
@ -78,7 +87,6 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
onSubmitted: (String value) {
|
onSubmitted: (String value) {
|
||||||
_tabController.animateTo(0, duration: Duration(milliseconds: 300));
|
|
||||||
changePlayer(value);
|
changePlayer(value);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -86,10 +94,13 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
db.open();
|
||||||
|
teto = TetrioService(db);
|
||||||
_scrollController = ScrollController();
|
_scrollController = ScrollController();
|
||||||
_tabController = TabController(length: 4, vsync: this);
|
_tabController = TabController(length: 4, vsync: this);
|
||||||
changePlayer("dan63047");
|
_getPreferences().then((value) => changePlayer(prefs.getString("player") ?? "dan63047"));
|
||||||
super.initState();
|
super.initState();
|
||||||
|
developer.log("Main view initialized", name: "main_view");
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -97,28 +108,34 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
||||||
_tabController.dispose();
|
_tabController.dispose();
|
||||||
_scrollController.dispose();
|
_scrollController.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
|
developer.log("Main view disposed", name: "main_view");
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _getPreferences() async {
|
||||||
|
prefs = await SharedPreferences.getInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
void changePlayer(String player) {
|
void changePlayer(String player) {
|
||||||
setState(() {
|
setState(() {
|
||||||
|
_tabController.animateTo(0, duration: const Duration(milliseconds: 300));
|
||||||
_searchFor = player;
|
_searchFor = player;
|
||||||
me = fetchTetrioPlayer(player);
|
me = fetchTetrioPlayer(player);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_scrollListener() {
|
// _scrollListener() {
|
||||||
if (fixedScroll) {
|
// if (fixedScroll) {
|
||||||
_scrollController.jumpTo(0);
|
// _scrollController.jumpTo(0);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
_smoothScrollToTop() {
|
// _smoothScrollToTop() {
|
||||||
_scrollController.animateTo(
|
// _scrollController.animateTo(
|
||||||
0,
|
// 0,
|
||||||
duration: const Duration(microseconds: 300),
|
// duration: const Duration(microseconds: 300),
|
||||||
curve: Curves.ease,
|
// curve: Curves.ease,
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -150,7 +167,6 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
||||||
? IconButton(
|
? IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
//add
|
|
||||||
_searchBoolean = true;
|
_searchBoolean = true;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -160,7 +176,6 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
||||||
: IconButton(
|
: IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
//add
|
|
||||||
_searchBoolean = false;
|
_searchBoolean = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -192,6 +207,7 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
||||||
child: FutureBuilder<TetrioPlayer>(
|
child: FutureBuilder<TetrioPlayer>(
|
||||||
future: me,
|
future: me,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
|
developer.log("builder ($context): $snapshot", name: "main_view");
|
||||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||||
return const Center(
|
return const Center(
|
||||||
child: CircularProgressIndicator(
|
child: CircularProgressIndicator(
|
||||||
|
@ -210,8 +226,10 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
||||||
controller: _tabController,
|
controller: _tabController,
|
||||||
isScrollable: true,
|
isScrollable: true,
|
||||||
tabs: myTabs,
|
tabs: myTabs,
|
||||||
onTap: (int sus) {
|
onTap: (int tabId) {
|
||||||
setState(() {});
|
setState(() {
|
||||||
|
developer.log("Tab changed to $tabId", name: "main_view");
|
||||||
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -438,9 +456,9 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
||||||
radarShape: RadarShape.polygon,
|
radarShape: RadarShape.polygon,
|
||||||
tickCount: 4,
|
tickCount: 4,
|
||||||
ticksTextStyle: const TextStyle(color: Colors.transparent, fontSize: 10),
|
ticksTextStyle: const TextStyle(color: Colors.transparent, fontSize: 10),
|
||||||
radarBorderData: BorderSide(color: Colors.transparent, width: 1),
|
radarBorderData: const BorderSide(color: Colors.transparent, width: 1),
|
||||||
gridBorderData: BorderSide(color: Colors.white24, width: 1),
|
gridBorderData: const BorderSide(color: Colors.white24, width: 1),
|
||||||
tickBorderData: BorderSide(color: Colors.transparent, width: 1),
|
tickBorderData: const BorderSide(color: Colors.transparent, width: 1),
|
||||||
getTitle: (index, angle) {
|
getTitle: (index, angle) {
|
||||||
switch (index) {
|
switch (index) {
|
||||||
case 0:
|
case 0:
|
||||||
|
@ -492,16 +510,16 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
||||||
fillColor: Colors.transparent,
|
fillColor: Colors.transparent,
|
||||||
borderColor: Colors.transparent,
|
borderColor: Colors.transparent,
|
||||||
dataEntries: [
|
dataEntries: [
|
||||||
RadarEntry(value: 0),
|
const RadarEntry(value: 0),
|
||||||
RadarEntry(value: 0),
|
const RadarEntry(value: 0),
|
||||||
RadarEntry(value: 0),
|
const RadarEntry(value: 0),
|
||||||
RadarEntry(value: 0),
|
const RadarEntry(value: 0),
|
||||||
RadarEntry(value: 0),
|
const RadarEntry(value: 0),
|
||||||
RadarEntry(value: 0),
|
const RadarEntry(value: 0),
|
||||||
RadarEntry(value: 0),
|
const RadarEntry(value: 0),
|
||||||
RadarEntry(value: 0),
|
const RadarEntry(value: 0),
|
||||||
RadarEntry(value: 0),
|
const RadarEntry(value: 0),
|
||||||
RadarEntry(value: 0),
|
const RadarEntry(value: 0),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
@ -521,9 +539,9 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
||||||
radarShape: RadarShape.polygon,
|
radarShape: RadarShape.polygon,
|
||||||
tickCount: 4,
|
tickCount: 4,
|
||||||
ticksTextStyle: const TextStyle(color: Colors.transparent, fontSize: 10),
|
ticksTextStyle: const TextStyle(color: Colors.transparent, fontSize: 10),
|
||||||
radarBorderData: BorderSide(color: Colors.transparent, width: 1),
|
radarBorderData: const BorderSide(color: Colors.transparent, width: 1),
|
||||||
gridBorderData: BorderSide(color: Colors.white24, width: 1),
|
gridBorderData: const BorderSide(color: Colors.white24, width: 1),
|
||||||
tickBorderData: BorderSide(color: Colors.transparent, width: 1),
|
tickBorderData: const BorderSide(color: Colors.transparent, width: 1),
|
||||||
getTitle: (index, angle) {
|
getTitle: (index, angle) {
|
||||||
switch (index) {
|
switch (index) {
|
||||||
case 0:
|
case 0:
|
||||||
|
@ -557,20 +575,20 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
||||||
fillColor: Colors.transparent,
|
fillColor: Colors.transparent,
|
||||||
borderColor: Colors.transparent,
|
borderColor: Colors.transparent,
|
||||||
dataEntries: [
|
dataEntries: [
|
||||||
RadarEntry(value: 0),
|
const RadarEntry(value: 0),
|
||||||
RadarEntry(value: 0),
|
const RadarEntry(value: 0),
|
||||||
RadarEntry(value: 0),
|
const RadarEntry(value: 0),
|
||||||
RadarEntry(value: 0),
|
const RadarEntry(value: 0),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
RadarDataSet(
|
RadarDataSet(
|
||||||
fillColor: Colors.transparent,
|
fillColor: Colors.transparent,
|
||||||
borderColor: Colors.transparent,
|
borderColor: Colors.transparent,
|
||||||
dataEntries: [
|
dataEntries: [
|
||||||
RadarEntry(value: 1),
|
const RadarEntry(value: 1),
|
||||||
RadarEntry(value: 1),
|
const RadarEntry(value: 1),
|
||||||
RadarEntry(value: 1),
|
const RadarEntry(value: 1),
|
||||||
RadarEntry(value: 1),
|
const RadarEntry(value: 1),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
@ -1087,7 +1105,8 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else if (snapshot.hasError) {
|
} else if (snapshot.hasError) {
|
||||||
return Center(child: Text('${snapshot.error}', style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 42)));
|
return Center(
|
||||||
|
child: Text('${snapshot.error}', style: const TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 42), textAlign: TextAlign.center));
|
||||||
}
|
}
|
||||||
return const Center(
|
return const Center(
|
||||||
child: CircularProgressIndicator(
|
child: CircularProgressIndicator(
|
||||||
|
@ -1101,8 +1120,8 @@ class _MainState extends State<MainView> with SingleTickerProviderStateMixin {
|
||||||
}
|
}
|
||||||
|
|
||||||
class NavDrawer extends StatelessWidget {
|
class NavDrawer extends StatelessWidget {
|
||||||
Function changePlayer;
|
final Function changePlayer;
|
||||||
NavDrawer(this.changePlayer, {super.key});
|
const NavDrawer(this.changePlayer, {super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -1116,10 +1135,11 @@ class NavDrawer extends StatelessWidget {
|
||||||
style: TextStyle(color: Colors.white, fontSize: 25),
|
style: TextStyle(color: Colors.white, fontSize: 25),
|
||||||
)),
|
)),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.verified_user),
|
leading: const Icon(Icons.home),
|
||||||
title: const Text('dan63047'),
|
title: Text(prefs.getString("player") ?? "dan63047"),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
changePlayer('dan63047');
|
developer.log("Navigator changed player", name: "main_view");
|
||||||
|
changePlayer(prefs.getString("player") ?? "dan63047");
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -1178,7 +1198,7 @@ class _StatCellNum extends StatelessWidget {
|
||||||
|
|
||||||
class _UserThingy extends StatelessWidget {
|
class _UserThingy extends StatelessWidget {
|
||||||
final TetrioPlayer player;
|
final TetrioPlayer player;
|
||||||
_UserThingy({Key? key, required this.player}) : super(key: key);
|
const _UserThingy({Key? key, required this.player}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -1201,6 +1221,12 @@ class _UserThingy extends StatelessWidget {
|
||||||
"https://tetr.io/user-content/banners/${player.userId}.jpg?rv=${player.bannerRevision}",
|
"https://tetr.io/user-content/banners/${player.userId}.jpg?rv=${player.bannerRevision}",
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
height: bannerHeight,
|
height: bannerHeight,
|
||||||
|
errorBuilder: (context, error, stackTrace) {
|
||||||
|
developer.log("Error with building banner image", name: "main_view", error: error, stackTrace: stackTrace);
|
||||||
|
return const Placeholder(
|
||||||
|
color: Colors.black,
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
padding: EdgeInsets.fromLTRB(0, player.bannerRevision != null ? bannerHeight / 1.4 : pfpHeight, 0, 0),
|
padding: EdgeInsets.fromLTRB(0, player.bannerRevision != null ? bannerHeight / 1.4 : pfpHeight, 0, 0),
|
||||||
|
@ -1212,16 +1238,21 @@ class _UserThingy extends StatelessWidget {
|
||||||
fit: BoxFit.fitHeight,
|
fit: BoxFit.fitHeight,
|
||||||
height: 128,
|
height: 128,
|
||||||
)
|
)
|
||||||
: Image.network(
|
: player.avatarRevision != null
|
||||||
"https://tetr.io/user-content/avatars/${player.userId}.jpg?rv=${player.avatarRevision}",
|
? Image.network("https://tetr.io/user-content/avatars/${player.userId}.jpg?rv=${player.avatarRevision}",
|
||||||
fit: BoxFit.fitHeight,
|
fit: BoxFit.fitHeight, height: 128, errorBuilder: (context, error, stackTrace) {
|
||||||
height: 128,
|
developer.log("Error with building profile picture", name: "main_view", error: error, stackTrace: stackTrace);
|
||||||
errorBuilder: (context, error, stackTrace) => Image.asset(
|
return Image.asset(
|
||||||
"res/avatars/tetrio_anon.png",
|
"res/avatars/tetrio_anon.png",
|
||||||
fit: BoxFit.fitHeight,
|
fit: BoxFit.fitHeight,
|
||||||
height: 128,
|
height: 128,
|
||||||
),
|
);
|
||||||
),
|
})
|
||||||
|
: Image.asset(
|
||||||
|
"res/avatars/tetrio_anon.png",
|
||||||
|
fit: BoxFit.fitHeight,
|
||||||
|
height: 128,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -1230,10 +1261,12 @@ class _UserThingy extends StatelessWidget {
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Text(player.username, style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)),
|
Text(player.username, style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 42 : 28)),
|
||||||
Text(
|
TextButton(
|
||||||
player.userId,
|
child: Text(player.userId, style: const TextStyle(fontFamily: "Eurostile Round Condensed", fontSize: 14)),
|
||||||
style: const TextStyle(fontFamily: "Eurostile Round Condensed", fontSize: 14),
|
onPressed: () {
|
||||||
),
|
copyToClipboard(player.userId);
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("Copied to clipboard!")));
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1311,7 +1344,6 @@ class _UserThingy extends StatelessWidget {
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () => showDialog<void>(
|
onPressed: () => showDialog<void>(
|
||||||
context: context,
|
context: context,
|
||||||
barrierDismissible: false, // user must tap button!
|
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: Text(
|
title: Text(
|
||||||
|
@ -1350,6 +1382,10 @@ class _UserThingy extends StatelessWidget {
|
||||||
"res/tetrio_badges/${badge.badgeId}.png",
|
"res/tetrio_badges/${badge.badgeId}.png",
|
||||||
height: 64,
|
height: 64,
|
||||||
width: 64,
|
width: 64,
|
||||||
|
errorBuilder: (context, error, stackTrace) {
|
||||||
|
developer.log("Error with building $badge", name: "main_view", error: error, stackTrace: stackTrace);
|
||||||
|
return Image.asset("res/icons/kagari.png", height: 64, width: 64);
|
||||||
|
},
|
||||||
))
|
))
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
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';
|
||||||
|
|
||||||
class SettingsView extends StatefulWidget {
|
class SettingsView extends StatefulWidget {
|
||||||
const SettingsView({Key? key}) : super(key: key);
|
const SettingsView({Key? key}) : super(key: key);
|
||||||
|
@ -10,11 +11,14 @@ class SettingsView extends StatefulWidget {
|
||||||
|
|
||||||
class SettingsState extends State<SettingsView> {
|
class SettingsState extends State<SettingsView> {
|
||||||
PackageInfo _packageInfo = PackageInfo(appName: "TetraStats", packageName: "idk man", version: "some numbers", buildNumber: "anotherNumber");
|
PackageInfo _packageInfo = PackageInfo(appName: "TetraStats", packageName: "idk man", version: "some numbers", buildNumber: "anotherNumber");
|
||||||
|
late SharedPreferences prefs;
|
||||||
|
final TextEditingController _playertext = TextEditingController();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_initPackageInfo();
|
_initPackageInfo();
|
||||||
|
_getPreferences();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _initPackageInfo() async {
|
Future<void> _initPackageInfo() async {
|
||||||
|
@ -24,6 +28,14 @@ class SettingsState extends State<SettingsView> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _getPreferences() async {
|
||||||
|
prefs = await SharedPreferences.getInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _setPlayer(String player) async {
|
||||||
|
await prefs.setString('player', player);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
@ -37,22 +49,23 @@ class SettingsState extends State<SettingsView> {
|
||||||
ListTile(
|
ListTile(
|
||||||
title: const Text("So there you gonna be able to change some settings"),
|
title: const Text("So there you gonna be able to change some settings"),
|
||||||
subtitle: const Text(
|
subtitle: const Text(
|
||||||
"They are not implemented yet. But its gonna be possible to change player for main view init, save logs, as well as import and export app sqlite database."),
|
"Only \"Your TETR.IO account nickname or ID\" implemented yet. But its gonna be possible to change player for main view init, save logs, as well as import and export app sqlite database."),
|
||||||
trailing: Switch(
|
trailing: Switch(
|
||||||
value: true,
|
value: true,
|
||||||
onChanged: (bool value) {},
|
onChanged: (bool value) {},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: const Text("Very egg"),
|
title: const Text("Your TETR.IO account nickname or ID"),
|
||||||
subtitle: const Text("very ass"),
|
subtitle:
|
||||||
trailing: const Text("dan63047"),
|
const Text("Every time when app loads, stats of that player will be fetched. Please prefer ID over nickname because nickname can be changed."),
|
||||||
|
trailing: Text(prefs.getString("player") ?? "dan63047"),
|
||||||
onTap: () => showDialog(
|
onTap: () => showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) => AlertDialog(
|
builder: (BuildContext context) => AlertDialog(
|
||||||
title: const Text("Your username in TETR.IO", style: TextStyle(fontFamily: "Eurostile Round Extended")),
|
title: const Text("Your TETR.IO account nickname or ID", style: TextStyle(fontFamily: "Eurostile Round Extended")),
|
||||||
content: SingleChildScrollView(
|
content: SingleChildScrollView(
|
||||||
child: ListBody(children: [const TextField()]),
|
child: ListBody(children: [TextField(controller: _playertext)]),
|
||||||
),
|
),
|
||||||
actions: <Widget>[
|
actions: <Widget>[
|
||||||
TextButton(
|
TextButton(
|
||||||
|
@ -64,13 +77,15 @@ class SettingsState extends State<SettingsView> {
|
||||||
TextButton(
|
TextButton(
|
||||||
child: const Text('Submit'),
|
child: const Text('Submit'),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
|
_setPlayer(_playertext.text.toLowerCase().trim());
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
|
setState(() {});
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
)),
|
)),
|
||||||
),
|
),
|
||||||
Divider(),
|
const Divider(),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: const Text("About app"),
|
title: const Text("About app"),
|
||||||
subtitle: Text("${_packageInfo.appName} (${_packageInfo.packageName}) Version ${_packageInfo.version} Build ${_packageInfo.buildNumber}"),
|
subtitle: Text("${_packageInfo.appName} (${_packageInfo.packageName}) Version ${_packageInfo.version} Build ${_packageInfo.buildNumber}"),
|
||||||
|
|
|
@ -7,10 +7,12 @@ import Foundation
|
||||||
|
|
||||||
import package_info_plus
|
import package_info_plus
|
||||||
import path_provider_foundation
|
import path_provider_foundation
|
||||||
|
import shared_preferences_foundation
|
||||||
import sqflite
|
import sqflite
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin"))
|
FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin"))
|
||||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||||
|
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||||
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
||||||
}
|
}
|
||||||
|
|
56
pubspec.lock
56
pubspec.lock
|
@ -264,6 +264,62 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.2.4"
|
version: "4.2.4"
|
||||||
|
shared_preferences:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: shared_preferences
|
||||||
|
sha256: "16d3fb6b3692ad244a695c0183fca18cf81fd4b821664394a781de42386bf022"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.1"
|
||||||
|
shared_preferences_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_android
|
||||||
|
sha256: "6478c6bbbecfe9aced34c483171e90d7c078f5883558b30ec3163cf18402c749"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.4"
|
||||||
|
shared_preferences_foundation:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_foundation
|
||||||
|
sha256: e014107bb79d6d3297196f4f2d0db54b5d1f85b8ea8ff63b8e8b391a02700feb
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.2"
|
||||||
|
shared_preferences_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_linux
|
||||||
|
sha256: "9d387433ca65717bbf1be88f4d5bb18f10508917a8fa2fb02e0fd0d7479a9afa"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.0"
|
||||||
|
shared_preferences_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_platform_interface
|
||||||
|
sha256: fb5cf25c0235df2d0640ac1b1174f6466bd311f621574997ac59018a6664548d
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.0"
|
||||||
|
shared_preferences_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_web
|
||||||
|
sha256: "74083203a8eae241e0de4a0d597dbedab3b8fef5563f33cf3c12d7e93c655ca5"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.0"
|
||||||
|
shared_preferences_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_windows
|
||||||
|
sha256: "5e588e2efef56916a3b229c3bfe81e6a525665a454519ca51dbcc4236a274173"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.0"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
|
|
|
@ -14,7 +14,7 @@ publish_to: 'none'
|
||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
# In Windows, build-name is used as the major, minor, and patch parts
|
# In Windows, build-name is used as the major, minor, and patch parts
|
||||||
# of the product and file versions while build-number is used as the build suffix.
|
# of the product and file versions while build-number is used as the build suffix.
|
||||||
version: 0.0.1+1
|
version: 0.0.2+2
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=2.19.6 <3.0.0'
|
sdk: '>=2.19.6 <3.0.0'
|
||||||
|
@ -37,6 +37,7 @@ dependencies:
|
||||||
path: ^1.8.2
|
path: ^1.8.2
|
||||||
fl_chart: ^0.62.0
|
fl_chart: ^0.62.0
|
||||||
package_info_plus: ^4.0.2
|
package_info_plus: ^4.0.2
|
||||||
|
shared_preferences: ^2.1.1
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
Loading…
Reference in New Issue