diff --git a/.metadata b/.metadata index 541a1ac..32d8944 100644 --- a/.metadata +++ b/.metadata @@ -4,7 +4,7 @@ # This file should be version controlled. version: - revision: e3c29ec00c9c825c891d75054c63fcc46454dca1 + revision: 135454af32477f815a7525073027a3ff9eff1bfd channel: stable project_type: app @@ -13,17 +13,11 @@ project_type: app migration: platforms: - platform: root - create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1 - base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1 - - platform: linux - create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1 - base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1 - - platform: macos - create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1 - base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1 - - platform: windows - create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1 - base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1 + create_revision: 135454af32477f815a7525073027a3ff9eff1bfd + base_revision: 135454af32477f815a7525073027a3ff9eff1bfd + - platform: web + create_revision: 135454af32477f815a7525073027a3ff9eff1bfd + base_revision: 135454af32477f815a7525073027a3ff9eff1bfd # User provided section diff --git a/lib/main.dart b/lib/main.dart index a26592d..85e48a4 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,12 +1,7 @@ -import 'dart:io'; - -import 'package:dev_server_starter/src/app.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:steamwar_multitool/src/app.dart'; void main() { runApp(const ProviderScope(child: DevServerStarterApp())); } - -String? get userHome => - Platform.environment['HOME'] ?? Platform.environment['USERPROFILE']; diff --git a/lib/src/app.dart b/lib/src/app.dart index 2e4ea3f..3305fe7 100644 --- a/lib/src/app.dart +++ b/lib/src/app.dart @@ -1,8 +1,8 @@ -import 'package:dev_server_starter/src/screens/login.dart'; -import 'package:dev_server_starter/src/screens/userinfo.dart'; import 'package:flutter/material.dart'; import 'screens/home.dart'; +import 'screens/login.dart'; +import 'screens/userinfo.dart'; class DevServerStarterApp extends StatelessWidget { const DevServerStarterApp({Key? key}) : super(key: key); @@ -15,7 +15,7 @@ class DevServerStarterApp extends StatelessWidget { "/home": (context) => const HomeScreen(), "/settings": (context) => const SettingsScreen(), }, - title: 'Dev Server Starter', + title: 'Event-Tool', theme: ThemeData( useMaterial3: true, colorSchemeSeed: const Color(0xFFFFFF55), diff --git a/lib/src/provider/events.dart b/lib/src/provider/events.dart index 63c58bb..6484dac 100644 --- a/lib/src/provider/events.dart +++ b/lib/src/provider/events.dart @@ -1,56 +1,14 @@ -import 'dart:io'; import 'dart:ui'; -import 'package:dev_server_starter/src/provider/user.dart'; +import 'package:dio/dio.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:mysql_client/mysql_client.dart'; +import 'package:json_annotation/json_annotation.dart'; -final portProvider = Provider((ref) { - return 49507; -}); - -final portForwardProvider = FutureProvider((ref) async { - final client = await ref.watch(sshClientProvider.future); - final port = ref.watch(portProvider); - final serverSocket = await ServerSocket.bind("127.0.0.1", port); - serverSocket.listen((socket) async { - final forward = await client.forwardLocal('127.0.0.1', 3306); - forward.stream.cast>().pipe(socket); - socket.pipe(forward.sink); - }); - ref.onDispose(() async { - try { - await serverSocket.close(); - } catch (e) { - print(e); - } - }); - return serverSocket.port; -}); - -final mysqlClientProvider = FutureProvider((ref) async { - final port = await ref.watch(portForwardProvider.future); - final userData = await ref.watch(userDataProvider.future); - final conn = await MySQLConnection.createConnection( - host: "localhost", - port: port, - userName: userData.sqlUserName!, - password: userData.sqlPassword!, - secure: false, - databaseName: userData.useDevDB ? "developer" : "core", - ); - await conn.connect(timeoutMs: 200).catchError((err) { - ref.invalidate(portForwardProvider); - }); - ref.onDispose(() { - if (conn.connected) conn.close(); - }); - return conn; -}, dependencies: [portForwardProvider, userDataProvider]); +import 'http.dart'; final eventRepositoryProvider = FutureProvider((ref) async { - return EventRepository(await ref.watch(mysqlClientProvider.future)); -}, dependencies: [mysqlClientProvider]); + return EventRepository(await ref.read(httpClient.future)); +}, dependencies: [httpClient]); final eventsListProvider = FutureProvider>((ref) async { final repo = await ref.watch(eventRepositoryProvider.future); @@ -58,43 +16,18 @@ final eventsListProvider = FutureProvider>((ref) async { }, dependencies: [eventRepositoryProvider]); class EventRepository { - final MySQLConnection _connection; + final Dio _client; - final _statements = >{}; - - EventRepository(this._connection); - - Future getStatement(String sql) async { - return _statements.putIfAbsent( - sql.hashCode, () => _connection.prepare(sql)); - } + EventRepository(this._client); Future> listEvents() async { - final result = await _connection.execute( - "SELECT EventName, EventID, Start, End FROM Event ORDER BY Start DESC"); - return result.rows - .map((e) => ShortEvent( - e.colByName("EventName")!, - e.typedColByName("EventID")!, - DateTime.parse(e.colByName("Start")!), - DateTime.parse(e.colByName("End")!))) - .toList(); + final res = await _client.get("/events"); + return (res.data as List).map((e) => ShortEvent.fromJson(e)).toList(); } - Future getEvent(int id) async { - final result = await getStatement("SELECT * FROM Event WHERE EventID = ?") - .then((value) => value.execute([id])); - return result.rows.map((e) => eventFromResult(e)).first; - } - - Future> getTeams(int eventId) async { - final result = await getStatement( - "SELECT Team.TeamID, TeamName, TeamColor FROM TeamTeilnahme JOIN Team ON TeamTeilnahme.TeamID = Team.TeamID WHERE EventID = ?") - .then((value) => value.execute([eventId])); - return result.rows - .map((e) => ShortTeam(e.typedColByName("TeamID")!, - e.colByName("TeamName")!, e.colByName("TeamColor")!)) - .toList(); + Future getEvent(int id) async { + final res = await _client.get("/events/$id"); + return EventExtended.fromJson(res.data); } Future updateEvent( @@ -107,76 +40,60 @@ class EventRepository { String? schemType, bool publicOnly, bool useSpectateSystem) async { - await getStatement("" - "UPDATE `Event` SET `EventName`=?,`Deadline`=?,`Start`=?,`End`=?,`MaximumTeamMembers`=?,`SchemType`=?,`PublicSchemsOnly`=?,`SpectateSystem`=? WHERE `EventID` = ?") - .then((value) => value.execute([ - name, - deadline.toIso8601String(), - start.toIso8601String(), - end.toIso8601String(), - maxTeamMembers, - schemType, - publicOnly ? 1 : 0, - useSpectateSystem ? 1 : 0, - id - ])); - } - - Event eventFromResult(ResultSetRow e) { - return Event( - id: e.typedColByName("EventID")!, - name: e.colByName("EventName")!, - deadline: DateTime.parse(e.colByName("Deadline")!), - start: DateTime.parse(e.colByName("Start")!), - end: DateTime.parse(e.colByName("End")!), - maxTeamMembers: e.typedColByName("MaximumTeamMembers")!, - schemType: e.colByName("SchemType"), - publicOnly: e.typedColByName("PublicSchemsOnly")!, - useSpectateSystem: e.typedColByName("SpectateSystem")!, - ); + await _client.put("/events/$id", data: { + "name": name, + "deadline": deadline.millisecondsSinceEpoch, + "start": start.millisecondsSinceEpoch, + "end": end.millisecondsSinceEpoch, + "maxTeamMembers": maxTeamMembers, + "schemType": schemType, + "publicSchemsOnly": publicOnly, + "spectateSystem": useSpectateSystem, + }); } Future createEvent(String name, DateTime start, DateTime end) async { - await getStatement( - "INSERT INTO `Event` (`EventName`, `Start`, `End`, `MaximumTeamMembers`, `PublicSchemsOnly`) VALUES (?, ?, ?, 5, 0)") - .then((value) => value - .execute([name, start.toIso8601String(), end.toIso8601String()])); - final result = await getStatement("SELECT * FROM Event WHERE EventName = ?") - .then((value) => value.execute([name])); - return result.rows.map((e) => eventFromResult(e)).first; + final res = await _client.post("/events", data: { + "name": name, + "start": start.millisecondsSinceEpoch, + "end": end.millisecondsSinceEpoch, + }); + return Event.fromJson(res.data); } Future deleteEvent(int id) async { - await getStatement("DELETE FROM Event WHERE EventID = ?") - .then((value) => value.execute([id])); - } - - Future> getFightOfEvent(int id) async { - final result = await getStatement( - "SELECT FightID, StartTime, Spielmodus, Map, tb.TeamID as BlueTeamID, tb.TeamColor as BlueTeamColor, tb.Teamname as BlueTeamName, tr.TeamID as RedTeamID, tr.TeamColor as RedTeamColor, tr.Teamname as RedTeamName, Kampfleiter, Ergebnis FROM EventFight JOIN Team tb ON tb.TeamID = EventFight.TeamBlue JOIN Team tr ON tr.TeamID = EventFight.TeamRed WHERE EventID = ?") - .then((value) => value.execute([id])); - return result.rows.map((e) => EventFight.fromResult(e)).toList(); + await _client.delete("/events/$id"); } Future deleteFight(int id) async { - await getStatement("DELETE FROM EventFight WHERE FightID = ?") - .then((value) => value.execute([id])); + await _client.delete("/fights/$id"); } Future createFight(int eventId, DateTime start, String mode, String map, - ShortTeam blue, ShortTeam red) { - return getStatement( - "INSERT INTO `EventFight` (`EventID`, `StartTime`, `Spielmodus`, `Map`, `TeamBlue`, `TeamRed`, `Kampfleiter`) VALUES (?, ?, ?, ?, ?, ?, 0)") - .then((value) => value.execute( - [eventId, start.toIso8601String(), mode, map, blue.id, red.id])); + Team blue, Team red) { + return _client.post("/fights", data: { + "event": eventId, + "spielmodus": mode, + "map": map, + "start": start.millisecondsSinceEpoch, + "blueTeam": blue.id, + "redTeam": red.id, + }); + } + + Future> listFights(int eventId) async { + final res = await _client.get("/events/$eventId/fights"); + return (res.data as List).map((e) => EventFight.fromJson(e)).toList(); } Future updateFight( int id, DateTime start, String mode, String map, int referee) { - return getStatement( - "UPDATE `EventFight` SET `StartTime`=?,`Spielmodus`=?,`Map`=?,`Kampfleiter`=? WHERE `FightID` = ?") - .then((value) => - value.execute([start.toIso8601String(), mode, map, referee, id])); + return _client.put("/fights/$id", data: { + "spielmodus": mode, + "map": map, + "start": start.millisecondsSinceEpoch, + "kampfleiter": referee, + }); } } @@ -185,49 +102,47 @@ class EventFight { final String gameMode; final String map; final DateTime start; - final ShortTeam redTeam; - final ShortTeam blueTeam; + final Team redTeam; + final Team blueTeam; final int fightLeaderId; final int score; EventFight(this.id, this.gameMode, this.map, this.start, this.redTeam, this.blueTeam, this.fightLeaderId, this.score); - ShortTeam get winner { + Team get winner { switch (score) { case 1: return blueTeam; case 2: return redTeam; case 3: - return ShortTeam(-1, "Tie", "7"); + return Team(-1, "Tie", "TIE", "7"); default: - return ShortTeam(-1, "Unknown", "7"); + return Team(-1, "Unknown", "UNK", "7"); } } - factory EventFight.fromResult(ResultSetRow e) { + factory EventFight.fromJson(Map json) { return EventFight( - e.typedColByName("FightID")!, - e.colByName("Spielmodus")!, - e.colByName("Map")!, - DateTime.parse(e.colByName("StartTime")!), - ShortTeam(e.typedColByName("RedTeamID")!, - e.colByName("RedTeamName")!, e.colByName("RedTeamColor")!), - ShortTeam(e.typedColByName("BlueTeamID")!, - e.colByName("BlueTeamName")!, e.colByName("BlueTeamColor")!), - e.typedColByName("Kampfleiter")!, - e.typedColByName("Ergebnis")!, - ); + json["id"], + json["spielmodus"], + json["map"], + DateTime.fromMillisecondsSinceEpoch(json["start"]), + Team.fromJson(json["redTeam"]), + Team.fromJson(json["blueTeam"]), + json["kampfleiter"], + json["ergebnis"]); } } -class ShortTeam { +class Team { final int id; final String name; + final String kuerzel; final String colorCode; - ShortTeam(this.id, this.name, this.colorCode); + Team(this.id, this.name, this.kuerzel, this.colorCode); Color get color { switch (colorCode) { @@ -265,6 +180,33 @@ class ShortTeam { return const Color(0xFF000000); } } + + factory Team.fromJson(Map json) { + return Team( + json['id'] as int, + json['name'] as String, + json['kuerzel'] as String, + json['color'] as String, + ); + } +} + +class EventExtended { + final Event event; + final List fights; + final List teams; + + EventExtended(this.event, this.fights, this.teams); + + factory EventExtended.fromJson(Map json) { + return EventExtended( + Event.fromJson(json['event']), + (json['fights'] as List) + .map((e) => EventFight.fromJson(e)) + .toList(), + (json['teams'] as List).map((e) => Team.fromJson(e)).toList(), + ); + } } class Event { @@ -275,8 +217,8 @@ class Event { final DateTime end; final int maxTeamMembers; final String? schemType; - final bool publicOnly; - final bool useSpectateSystem; + final bool publicSchemsOnly; + final bool spectateSystem; Event({ required this.id, @@ -286,11 +228,26 @@ class Event { required this.end, required this.maxTeamMembers, required this.schemType, - required this.publicOnly, - required this.useSpectateSystem, + required this.publicSchemsOnly, + required this.spectateSystem, }); + + factory Event.fromJson(Map json) { + return Event( + id: json['id'], + name: json['name'], + deadline: DateTime.fromMillisecondsSinceEpoch(json['deadline']), + start: DateTime.fromMillisecondsSinceEpoch(json['start']), + end: DateTime.fromMillisecondsSinceEpoch(json['end']), + maxTeamMembers: json['maxTeamMembers'], + schemType: json['schemType'], + publicSchemsOnly: json['publicSchemsOnly'], + spectateSystem: json['spectateSystem'], + ); + } } +@JsonSerializable() class ShortEvent { final String name; final int id; @@ -300,4 +257,12 @@ class ShortEvent { ShortEvent(this.name, this.id, this.start, this.end); get isUpcoming => start.isAfter(DateTime.now()); + + factory ShortEvent.fromJson(Map json) { + return ShortEvent( + json["name"], + json["id"], + DateTime.fromMillisecondsSinceEpoch(json["start"]), + DateTime.fromMillisecondsSinceEpoch(json["end"])); + } } diff --git a/lib/src/provider/http.dart b/lib/src/provider/http.dart new file mode 100644 index 0000000..4d8ead0 --- /dev/null +++ b/lib/src/provider/http.dart @@ -0,0 +1,28 @@ +import 'package:dio/dio.dart'; +import 'package:flutter/foundation.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:steamwar_multitool/src/provider/user.dart'; + +final serverUrlProvider = Provider((ref) { + if (kDebugMode) { + return "http://localhost:8000"; + } else { + return "https://steamwar.de"; + } +}); + +final httpClient = FutureProvider( + (ref) async => Dio(BaseOptions( + baseUrl: ref.watch(serverUrlProvider), + headers: { + "X-SW-Auth": + await ref.watch(userDataProvider.future).then((value) => value.key), + }, + contentType: "application/json", + )), +); + +final test = FutureProvider((ref) async { + final client = await ref.watch(httpClient.future); + await client.get("/data"); +}); diff --git a/lib/src/provider/server.dart b/lib/src/provider/server.dart index 4af64cc..c0e20ac 100644 --- a/lib/src/provider/server.dart +++ b/lib/src/provider/server.dart @@ -1,27 +1,19 @@ import 'dart:convert'; -import 'package:dartssh2/dartssh2.dart'; -import 'package:dev_server_starter/src/screens/console.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:shared_preferences/shared_preferences.dart'; -import 'user.dart'; +import '../screens/console.dart'; final serversProvider = FutureProvider>((ref) async { - final sftp = await ref.watch(sftpProvider.future); - final ret = (await sftp.readdir("/servers").first) - .map((e) => e.filename) - .where((element) => element != "." && element != "..") - .toList(); - ret.sort(); - return ret; -}); - -final sftpProvider = FutureProvider((ref) async { - final client = await ref.watch(sshClientProvider.future); - final sftp = await client.sftp(); - ref.onDispose(sftp.close); - return sftp; + return []; + //final sftp = await ref.watch(sftpProvider.future); + //final ret = (await sftp.readdir("/servers").first) + // .map((e) => e.filename) + // .where((element) => element != "." && element != "..") + // .toList(); + //ret.sort(); + //return ret; }); final favoriteServersProvider = FutureProvider((ref) async { diff --git a/lib/src/provider/types.dart b/lib/src/provider/types.dart index f7ecd9c..53ffcec 100644 --- a/lib/src/provider/types.dart +++ b/lib/src/provider/types.dart @@ -1,56 +1,37 @@ -import 'package:dev_server_starter/src/provider/events.dart'; -import 'package:dev_server_starter/src/provider/server.dart'; -import 'package:dev_server_starter/src/provider/user.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:yaml/yaml.dart'; + +import 'http.dart'; final fightServersProvider = FutureProvider((ref) async { - final client = await ref.watch(sftpProvider.future); - return await client.listdir("/configs/GameModes").then( - (value) => value - .map((e) => e.filename) - .where((element) => element != "." && element != "..") - .where((element) => - element.endsWith(".yml") && !element.endsWith(".kits.yml")) - .map((e) => e.substring(0, e.length - 4)) - .toList(), - ); + final client = await ref.watch(httpClient.future); + final res = await client.get("/data/gamemodes"); + return (res.data as List).map((e) => e.toString()).toList(); }); final schematicTypesProvider = FutureProvider((ref) async { - final client = await ref.watch(sshClientProvider.future); - final result = await client - .run("grep ' Type: ' /configs/GameModes/*.yml") - .then((value) => String.fromCharCodes(value)); - return result - .split("\n") - .where((element) => element.isNotEmpty) - .map((e) => e.split(": ")[2]) - .toList(); + return await ref.watch(httpClient.future).then((client) async { + final res = await client.get("/data/schematicTypes"); + return (res.data as List) + .map((e) => SchematicType.fromJson(e)) + .toList(); + }); }); final mapsProvider = FutureProvider>>((ref) async { - final client = await ref.watch(sshClientProvider.future); - final gamemodes = await ref.read(fightServersProvider.future); - final maps = >{}; - for (var gm in gamemodes) { - final result = loadYaml(await client - .run("cat /configs/GameModes/$gm.yml") - .then((value) => String.fromCharCodes(value))); - final gmMaps = result?["Server"]?["Maps"] as YamlList?; - if (gmMaps != null) { - maps[gm] = gmMaps.toList().map((e) => e.toString()).toList(); - } + final client = await ref.watch(httpClient.future); + final servers = await ref.watch(fightServersProvider.future); + final ret = >{}; + for (final server in servers) { + final res = await client.get("/data/gamemodes/$server/maps"); + ret[server] = (res.data as List).map((e) => e.toString()).toList(); } - return maps; + return ret; }); final usersProvider = FutureProvider((ref) async { - final client = await ref.watch(mysqlClientProvider.future); - final result = await client.execute("SELECT id, UserName FROM UserData"); - return result.rows - .map((e) => User(e.typedColByName("id")!, e.colByName("UserName")!)) - .toList(); + final client = await ref.watch(httpClient.future); + final res = await client.get("/data/users"); + return (res.data as List).map((e) => User.fromJson(e)).toList(); }); class User { @@ -58,4 +39,19 @@ class User { final String name; User(this.id, this.name); + + factory User.fromJson(Map json) { + return User(json["id"] as int, json["name"] as String); + } +} + +class SchematicType { + final String name; + final String db; + + SchematicType(this.name, this.db); + + factory SchematicType.fromJson(Map json) { + return SchematicType(json["name"], json["db"]); + } } diff --git a/lib/src/provider/user.dart b/lib/src/provider/user.dart index 44bb20a..fc001fa 100644 --- a/lib/src/provider/user.dart +++ b/lib/src/provider/user.dart @@ -1,82 +1,23 @@ -import 'dart:io'; - -import 'package:dartssh2/dartssh2.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:shared_preferences/shared_preferences.dart'; -final clientFunctionProvider = - FutureProvider((ref) async { - final userData = await ref.watch(userDataProvider.future); - switch (userData.method) { - case 0: - return (socket) => SSHClient( - socket, - username: userData.username, - onPasswordRequest: () => userData.password, - ); - case 1: - return (socket) => SSHClient( - socket, - username: userData.username, - identities: [ - ...SSHKeyPair.fromPem( - File(userData.privateKeyFile!).readAsStringSync()), - ], - ); - default: - throw Exception("Invalid method"); - } -}, dependencies: [userDataProvider]); - final userDataProvider = FutureProvider((ref) async { final prefs = await SharedPreferences.getInstance(); - final username = prefs.getString("username")!; - final method = prefs.getInt("method")!; - final password = prefs.getString("password"); - final privateKey = prefs.getString("privateKeyFile"); - final sqlUsername = prefs.getString("sqlUsername"); - final sqlPassword = prefs.getString("sqlPassword"); - final useDevDB = prefs.getBool("sqlUseDevDB") ?? false; + final key = prefs.getString("key")!; final uneditablePastEvents = prefs.getBool("uneditablePastEvents") ?? false; return UserData( - username: username, - method: method, - password: password, - privateKeyFile: privateKey, - sqlPassword: sqlPassword, - sqlUserName: sqlUsername, - useDevDB: useDevDB, + key: key, uneditablePastEvents: uneditablePastEvents, ); }); class UserData { - final String username; - final String? password; - final String? privateKeyFile; - final int method; - - final String? sqlUserName; - final String? sqlPassword; - final bool useDevDB; + final String key; final bool uneditablePastEvents; UserData({ - required this.username, - required this.password, - required this.privateKeyFile, - required this.method, - required this.sqlUserName, - required this.sqlPassword, - required this.useDevDB, + required this.key, required this.uneditablePastEvents, }); } - -final sshClientProvider = FutureProvider((ref) async { - final clientFunction = await ref.watch(clientFunctionProvider.future); - final client = clientFunction(await SSHSocket.connect("steamwar.de", 22)); - await client.authenticated; - return client; -}, dependencies: [clientFunctionProvider]); diff --git a/lib/src/screens/components/event_dialog.dart b/lib/src/screens/components/event_dialog.dart index b53252e..514cc09 100644 --- a/lib/src/screens/components/event_dialog.dart +++ b/lib/src/screens/components/event_dialog.dart @@ -1,9 +1,10 @@ -import 'package:dev_server_starter/src/provider/events.dart'; -import 'package:dev_server_starter/src/screens/event.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import '../../provider/events.dart'; +import '../event.dart'; + class EventDialog extends HookConsumerWidget { const EventDialog({Key? key}) : super(key: key); @@ -68,11 +69,14 @@ class EventDialog extends HookConsumerWidget { final event = await ref.read(eventRepositoryProvider.future).then( (value) => value.createEvent( eventName.text, startTime.value, endTime.value)); + final eventFull = await ref + .read(eventRepositoryProvider.future) + .then((value) => value.getEvent(event.id)); nav.pop(); nav.push( MaterialPageRoute( builder: (context) { - return EditEventScreen(true, event); + return EditEventScreen(true, eventFull); }, ), ); diff --git a/lib/src/screens/components/events_list.dart b/lib/src/screens/components/events_list.dart index 45de4c0..67dfc76 100644 --- a/lib/src/screens/components/events_list.dart +++ b/lib/src/screens/components/events_list.dart @@ -1,10 +1,10 @@ -import 'package:dev_server_starter/src/provider/events.dart'; -import 'package:dev_server_starter/src/provider/user.dart'; -import 'package:dev_server_starter/src/screens/components/error.dart'; -import 'package:dev_server_starter/src/screens/event.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import '../../provider/events.dart'; +import '../../provider/user.dart'; +import '../event.dart'; +import 'error.dart'; import 'event_dialog.dart'; class EventListComponent extends HookConsumerWidget { @@ -84,20 +84,6 @@ class EventListComponent extends HookConsumerWidget { ], ); }, error: (err, stack) { - final userdata = ref.read(userDataProvider); - if (userdata.value?.sqlUserName == null || - userdata.value?.sqlPassword == null) { - return const Center( - child: Text("Please set your SQL credentials in the settings"), - ); - } - if (err.toString() == - "MySQLClientException: Can not execute query: connection closed") { - ref.invalidate(mysqlClientProvider); - return const Center( - child: CircularProgressIndicator(), - ); - } return ErrorComponent(err, stack); }, loading: () { return const Center( diff --git a/lib/src/screens/components/server_list.dart b/lib/src/screens/components/server_list.dart index 41f77a6..e5363a7 100644 --- a/lib/src/screens/components/server_list.dart +++ b/lib/src/screens/components/server_list.dart @@ -1,6 +1,5 @@ import 'dart:convert'; -import 'package:dev_server_starter/src/screens/components/error.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; @@ -9,6 +8,7 @@ import 'package:shared_preferences/shared_preferences.dart'; import '../../provider/server.dart'; import '../console.dart'; +import 'error.dart'; class ServerListComponent extends HookConsumerWidget { const ServerListComponent({Key? key}) : super(key: key); diff --git a/lib/src/screens/console.dart b/lib/src/screens/console.dart index 947e83d..5415973 100644 --- a/lib/src/screens/console.dart +++ b/lib/src/screens/console.dart @@ -1,7 +1,6 @@ import 'dart:typed_data'; -import 'package:dartssh2/dartssh2.dart'; -import 'package:dev_server_starter/src/provider/user.dart'; +//import 'package:dartssh2/dartssh2.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; @@ -19,7 +18,7 @@ class ConsoleScreen extends StatefulHookConsumerWidget { } class _ConsoleScreenState extends ConsumerState { - late final SSHSession session; + //late final SSHSession session; late final Terminal terminal; final _controller = ScrollController(keepScrollOffset: true); @@ -56,8 +55,8 @@ class _ConsoleScreenState extends ConsumerState { inputFieldController.text = ""; terminal.write("> $value"); terminal.nextLine(); - session.write( - Uint8List.fromList(value.codeUnits + '\n'.codeUnits)); + //session.write( + // Uint8List.fromList(value.codeUnits + '\n'.codeUnits)); inputFieldFocusNode.requestFocus(); }, decoration: const InputDecoration( @@ -81,8 +80,8 @@ class _ConsoleScreenState extends ConsumerState { @override void deactivate() { super.deactivate(); - session.kill(SSHSignal.KILL); - session.close(); + //session.kill(SSHSignal.KILL); + //session.close(); } @override @@ -90,7 +89,7 @@ class _ConsoleScreenState extends ConsumerState { super.initState(); terminal = Terminal(); terminal.setLineFeedMode(true); - final clientFut = ref.read(sshClientProvider); + /*final clientFut = ref.read(sshClientProvider); final client = clientFut.value!; client .execute( @@ -111,7 +110,7 @@ class _ConsoleScreenState extends ConsumerState { } } }); - }); + });*/ } } diff --git a/lib/src/screens/event.dart b/lib/src/screens/event.dart index 7d86cd8..526c336 100644 --- a/lib/src/screens/event.dart +++ b/lib/src/screens/event.dart @@ -1,23 +1,30 @@ import 'dart:math'; -import 'package:dev_server_starter/src/provider/events.dart'; -import 'package:dev_server_starter/src/provider/types.dart'; -import 'package:dev_server_starter/src/screens/components/error.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import '../provider/events.dart'; +import '../provider/types.dart'; import 'search_delegates.dart'; +T? catchToNull(T Function() f) { + try { + return f(); + } catch (e) { + return null; + } +} + class EditEventScreen extends HookConsumerWidget { final bool editable; - final Event event; - const EditEventScreen(this.editable, this.event, {Key? key}) + final EventExtended eventData; + const EditEventScreen(this.editable, this.eventData, {Key? key}) : super(key: key); @override Widget build(BuildContext context, WidgetRef ref) { + final event = eventData.event; final nameController = useTextEditingController(text: event.name); final deadlineState = useState(event.deadline); final startDateState = useState(event.start); @@ -26,18 +33,15 @@ class EditEventScreen extends HookConsumerWidget { final maxTeamMembersController = useTextEditingController(text: event.maxTeamMembers.toString()); final invalidMaxTeamMembers = useState(false); - final schematicTypeState = useState(event.schemType); - final publicOnlyState = useState(event.publicOnly); - final spectateSystemState = useState(event.useSpectateSystem); + final schematicTypeState = useState(catchToNull(() => ref + .watch(schematicTypesProvider) + .value! + .firstWhere((element) => element.db == event.schemType))); + final publicOnlyState = useState(event.publicSchemsOnly); + final spectateSystemState = useState(event.spectateSystem); final changed = useState(false); - final teams = useMemoized(() { - return ref - .read(eventRepositoryProvider.future) - .then((value) => value.getTeams(event.id)); - }); - return Scaffold( appBar: AppBar( title: Text("${editable ? "Edit" : "View"} ${event.name}"), @@ -85,7 +89,7 @@ class EditEventScreen extends HookConsumerWidget { startDateState.value, endDateState.value, maxTeamMembersState.value, - schematicTypeState.value, + schematicTypeState.value?.db, publicOnlyState.value, spectateSystemState.value, )) @@ -223,7 +227,7 @@ class EditEventScreen extends HookConsumerWidget { final out = await showSearch( context: context, delegate: SchematicTypeSearchDelegate(types)); - if (out == "reset") { + if (out == RESET_TYPE) { schematicTypeState.value = null; changed.value = true; } else { @@ -233,7 +237,7 @@ class EditEventScreen extends HookConsumerWidget { } } : null, - child: Text(schematicTypeState.value ?? "Select")), + child: Text(schematicTypeState.value?.name ?? "Select")), ], ), const SizedBox(height: 8), @@ -258,36 +262,27 @@ class EditEventScreen extends HookConsumerWidget { ), const SizedBox(height: 8), Text("Teams", style: Theme.of(context).textTheme.headline6), - FutureBuilder>( - future: teams, - builder: (context, snap) { - if (snap.hasData) { - return Wrap( - children: [ - for (final team in snap.data!) - Padding( - padding: const EdgeInsets.all(8.0), - child: Chip( - label: Text(team.name, - style: TextStyle( - color: team.color.computeLuminance() > 0.5 - ? Colors.black - : Colors.white)), - backgroundColor: team.color, - ), - ) - ], - ); - } else if (snap.hasError) { - return ErrorComponent(snap.error!, null); - } else { - return const LinearProgressIndicator(); - } - }, + Wrap( + children: [ + for (final team in eventData.teams) + Padding( + padding: const EdgeInsets.all(8.0), + child: Chip( + label: Text(team.name, + style: TextStyle( + color: team.color.computeLuminance() > 0.5 + ? Colors.black + : Colors.white)), + backgroundColor: team.color, + ), + ) + ], ), const SizedBox(height: 8), Text("Fights", style: Theme.of(context).textTheme.headline6), - _EventFightList(event: event), + _EventFightList( + eventData: eventData, + ), ], ), ), @@ -296,68 +291,58 @@ class EditEventScreen extends HookConsumerWidget { } class _EventFightList extends HookConsumerWidget { - final Event event; + final EventExtended eventData; - const _EventFightList({Key? key, required this.event}) : super(key: key); + const _EventFightList({Key? key, required this.eventData}) : super(key: key); @override Widget build(BuildContext context, WidgetRef ref) { - final fightsRefresher = useState(0); - final fights = useMemoized(() { - return ref - .read(eventRepositoryProvider.future) - .then((value) => value.getFightOfEvent(event.id)); - }, [fightsRefresher.value]); + final event = eventData.event; + final fights = useState>(eventData.fights); - return FutureBuilder>( - future: fights, - builder: (context, data) { - if (data.hasError) { - return ErrorComponent(data.error!, null); - } else if (data.hasData) { - return ListView( - shrinkWrap: true, - children: [ - const SizedBox(height: 8), - FloatingActionButton.extended( - onPressed: () { - showDialog( - context: context, - builder: (context) { - return _AddFightWidget( - event, - () => fightsRefresher.value++, - ); - }, - ); - }, - label: const Text("Add Fight"), - icon: const Icon(Icons.add), - ), - const SizedBox(height: 8), - for (final fight in data.data!) - ListTile( - title: - Text("${fight.blueTeam.name} vs ${fight.redTeam.name}"), - subtitle: fight.score != 0 - ? Text("Winner: ${fight.winner.name}") - : Text(fight.start.toString()), - onTap: () { - showDialog( - context: context, - builder: (context) { - return EditEventFightDialog( - fight: fight, fightsRefresher: fightsRefresher); - }); - }, - tileColor: fight.score != 0 ? fight.winner.color : null, - ) - ], + void update() async { + final repo = await ref.read(eventRepositoryProvider.future); + fights.value = await repo.listFights(event.id); + } + + return ListView( + shrinkWrap: true, + children: [ + const SizedBox(height: 8), + FloatingActionButton.extended( + onPressed: () { + showDialog( + context: context, + builder: (context) { + return _AddFightWidget( + eventData, + () => update(), + ); + }, ); - } else { - return const LinearProgressIndicator(); - } - }); + }, + label: const Text("Add Fight"), + icon: const Icon(Icons.add), + ), + const SizedBox(height: 8), + for (final fight in fights.value) + ListTile( + title: Text("${fight.blueTeam.name} vs ${fight.redTeam.name}"), + subtitle: fight.score != 0 + ? Text("Winner: ${fight.winner.name}") + : Text(fight.start.toString()), + onTap: () { + showDialog( + context: context, + builder: (context) { + return EditEventFightDialog( + fight: fight, fightsRefresher: () => update()); + }); + }, + tileColor: fight.score != 0 ? fight.winner.color : null, + ) + ], + ); } } @@ -369,7 +354,7 @@ class EditEventFightDialog extends HookConsumerWidget { }) : super(key: key); final EventFight fight; - final ValueNotifier fightsRefresher; + final void Function() fightsRefresher; @override Widget build(BuildContext context, WidgetRef ref) { @@ -421,7 +406,7 @@ class EditEventFightDialog extends HookConsumerWidget { delegate: MapSearchDelegate(maps[mode.value] ?? [])); if (out != null) { map.value = out; - fightsRefresher.value++; + fightsRefresher(); } }, child: Text(map.value), @@ -436,7 +421,7 @@ class EditEventFightDialog extends HookConsumerWidget { context: context, delegate: UserSearchDelegate(users)); if (user != null) { referrer.value = user.id; - fightsRefresher.value++; + fightsRefresher(); } }, child: @@ -451,7 +436,7 @@ class EditEventFightDialog extends HookConsumerWidget { ref.read(eventRepositoryProvider.future).then( (value) => value.deleteFight(fight.id).then( (value) { - fightsRefresher.value++; + fightsRefresher(); }, ), ); @@ -468,7 +453,7 @@ class EditEventFightDialog extends HookConsumerWidget { final repo = await ref.read(eventRepositoryProvider.future); await repo.updateFight( fight.id, start.value, mode.value, map.value, referrer.value); - fightsRefresher.value++; + fightsRefresher(); nav.pop(); }, child: const Text("Save")), @@ -479,19 +464,20 @@ class EditEventFightDialog extends HookConsumerWidget { class _AddFightWidget extends HookConsumerWidget { final void Function() onAdded; - final Event event; + final EventExtended eventData; const _AddFightWidget( - this.event, + this.eventData, this.onAdded, { Key? key, }) : super(key: key); @override Widget build(BuildContext context, WidgetRef ref) { + final event = eventData.event; final gamemode = useState(null); final map = useState(null); - final blueTeam = useState(null); - final redTeam = useState(null); + final blueTeam = useState(null); + final redTeam = useState(null); final date = useState(event.start); final canCreate = useMemoized(() { @@ -538,10 +524,12 @@ class _AddFightWidget extends HookConsumerWidget { ), const SizedBox(height: 8), const Text("Blue Team"), - _TeamSelector(event, blueTeam.value, (p0) => blueTeam.value = p0), + _TeamSelector(event, blueTeam.value, (p0) => blueTeam.value = p0, + eventData.teams), const SizedBox(height: 8), const Text("Red Team"), - _TeamSelector(event, redTeam.value, (p0) => redTeam.value = p0), + _TeamSelector(event, redTeam.value, (p0) => redTeam.value = p0, + eventData.teams), const SizedBox(height: 8), DateTimeEditor((p0) => date.value = p0, date.value, true), ], @@ -576,12 +564,15 @@ class _AddFightWidget extends HookConsumerWidget { class _TeamSelector extends HookConsumerWidget { final Event event; - final ShortTeam? selectedTeam; - final void Function(ShortTeam) onSelected; + final Team? selectedTeam; + final void Function(Team) onSelected; + final List teams; + const _TeamSelector( this.event, this.selectedTeam, - this.onSelected, { + this.onSelected, + this.teams, { Key? key, }) : super(key: key); @@ -589,9 +580,6 @@ class _TeamSelector extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { return ElevatedButton( onPressed: () async { - final teams = await ref - .read(eventRepositoryProvider.future) - .then((value) => value.getTeams(event.id)); final team = await showSearch( context: context, delegate: TeamSearchDelegate(teams)); if (team != null) { @@ -635,6 +623,14 @@ class DateTimeEditor extends HookConsumerWidget { onPressed: enabled ? () async { final time = await showTimePicker( + builder: (context, child) { + return MediaQuery( + data: MediaQuery.of(context).copyWith( + alwaysUse24HourFormat: true, + ), + child: child!, + ); + }, context: context, initialTime: TimeOfDay( hour: initialDate.hour, minute: initialDate.minute), diff --git a/lib/src/screens/home.dart b/lib/src/screens/home.dart index 565d6a6..e357442 100644 --- a/lib/src/screens/home.dart +++ b/lib/src/screens/home.dart @@ -1,14 +1,12 @@ -import 'package:dev_server_starter/src/provider/events.dart'; -import 'package:dev_server_starter/src/screens/components/events_list.dart'; -import 'package:dev_server_starter/src/screens/components/server_list.dart'; -import 'package:dev_server_starter/src/screens/console.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import '../provider/events.dart'; import '../provider/server.dart'; import '../provider/user.dart'; +import 'components/events_list.dart'; class HomeScreen extends HookConsumerWidget { const HomeScreen({Key? key}) : super(key: key); @@ -21,12 +19,12 @@ class HomeScreen extends HookConsumerWidget { return Scaffold( appBar: AppBar( title: const Text('Home'), + automaticallyImplyLeading: false, actions: [ IconButton( onPressed: servers.isLoading ? null : () { - ref.refresh(serversProvider); ref.refresh(eventsListProvider); }, icon: const Icon(Icons.refresh), @@ -48,7 +46,7 @@ class HomeScreen extends HookConsumerWidget { onPressed: () async { final nav = Navigator.of(context); final prefs = await SharedPreferences.getInstance(); - await prefs.remove("username"); + await prefs.remove("key"); ref.refresh(userDataProvider); nav.pop(); nav.pushReplacementNamed("/"); @@ -68,14 +66,14 @@ class HomeScreen extends HookConsumerWidget { NavigationRail( elevation: 1, destinations: const [ - NavigationRailDestination( - icon: Icon(Icons.dns), - label: Text('Server'), - ), NavigationRailDestination( icon: Icon(Icons.calendar_today), label: Text('Events'), ), + NavigationRailDestination( + icon: Icon(Icons.dns), + label: Text('Server'), + ), ], labelType: NavigationRailLabelType.selected, trailing: IconButton( @@ -93,8 +91,9 @@ class HomeScreen extends HookConsumerWidget { child: IndexedStack( index: navRailIndex.value, children: const [ - ServerListComponent(), + //ServerListComponent(), EventListComponent(), + Placeholder(), ], ), ), diff --git a/lib/src/screens/login.dart b/lib/src/screens/login.dart index 5c5c5b9..b9b4ad9 100644 --- a/lib/src/screens/login.dart +++ b/lib/src/screens/login.dart @@ -1,14 +1,9 @@ -import 'dart:io'; - -import 'package:dev_server_starter/src/provider/user.dart'; -import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:path/path.dart'; import 'package:shared_preferences/shared_preferences.dart'; -import '../../main.dart'; +import '../provider/user.dart'; class LoginScreenWidget extends HookConsumerWidget { const LoginScreenWidget({ @@ -17,145 +12,60 @@ class LoginScreenWidget extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final clientFunction = ref.watch(clientFunctionProvider); + final keyController = useTextEditingController(); + final updater = useState(0); - useEffect(() { - clientFunction.whenData((clientFunction) { - Navigator.of(context).pushReplacementNamed("/home"); - }); - return null; - }, []); + ref.read(userDataProvider).when( + data: (data) { + keyController.text = data.key; + updater.value++; + }, + loading: () {}, + error: (error, stack) {}, + ); - return clientFunction.when(data: (data) { - return Scaffold( - appBar: AppBar( - title: const Text('Login'), - ), - body: Center( + return Scaffold( + appBar: AppBar( + title: const Text('Login'), + automaticallyImplyLeading: false, + ), + body: Center( + child: SizedBox( + width: 200, child: Column( - crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ - const Text("Redirecting..."), - ElevatedButton( - onPressed: () { - Navigator.of(context).popAndPushNamed("/home"); - }, - child: const Text("Go to home")) + Text( + 'Login', + style: Theme.of(context).textTheme.headline4, + ), + TextField( + decoration: const InputDecoration( + hintText: 'Key', + ), + controller: keyController, + obscureText: true, + onChanged: (v) => updater.value++, + ), + const SizedBox( + height: 10, + ), + FloatingActionButton.large( + onPressed: keyController.text.isNotEmpty + ? () async { + final nav = Navigator.of(context); + final prefs = await SharedPreferences.getInstance(); + await prefs.setString("key", keyController.text); + ref.invalidate(userDataProvider); + nav.pushReplacementNamed("/home"); + } + : null, + child: const Icon(Icons.forward), + ) ], ), ), - ); - }, error: (error, stack) { - final _userNameField = useTextEditingController(); - final _passwordField = useTextEditingController(); - final _privateKeyField = useState(null); - - final canProceed = useState(false); - - void _checkCanProceed(String _) { - canProceed.value = canProceed.value = _userNameField.text.isNotEmpty && - (_passwordField.text.isNotEmpty || _privateKeyField.value != null); - } - - return Scaffold( - appBar: AppBar( - title: const Text('Login'), - ), - body: Center( - child: SizedBox( - width: 200, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - 'Login', - style: Theme.of(context).textTheme.headline4, - ), - TextField( - decoration: const InputDecoration( - hintText: 'Username', - ), - controller: _userNameField, - onChanged: _checkCanProceed, - ), - const SizedBox( - height: 10, - ), - TextField( - decoration: const InputDecoration( - hintText: 'Password', - ), - obscureText: true, - controller: _passwordField, - onChanged: _checkCanProceed, - ), - const SizedBox( - height: 10, - ), - const Text("Or"), - const SizedBox( - height: 10, - ), - ElevatedButton( - onPressed: () async { - final result = await FilePicker.platform.pickFiles( - type: FileType.any, - allowMultiple: false, - dialogTitle: "Select a private key", - initialDirectory: - userHome != null ? join(userHome!, ".ssh") : null, - ); - - if (result != null) { - final file = result.files.first; - _privateKeyField.value = file.path; - _checkCanProceed(""); - } - }, - child: const Text('Select Private Key'), - ), - if (_privateKeyField.value != null) - Text(_privateKeyField.value!), - const SizedBox( - height: 10, - ), - FloatingActionButton.large( - onPressed: canProceed.value - ? () async { - final nav = Navigator.of(context); - final prefs = await SharedPreferences.getInstance(); - await prefs.setString( - "username", _userNameField.text); - if (_privateKeyField.value != null) { - await prefs.setInt("method", 1); - await prefs.setString( - "privateKeyFile", _privateKeyField.value!); - } else { - await prefs.setInt("method", 0); - } - await prefs.setString( - "password", _passwordField.text); - ref.refresh(userDataProvider); - nav.pushReplacementNamed("/home"); - } - : null, - child: const Icon(Icons.forward), - ) - ], - ), - ), - ), - ); - }, loading: () { - return Scaffold( - appBar: AppBar( - title: const Text('Login'), - ), - body: const Center( - child: CircularProgressIndicator(), - ), - ); - }); + ), + ); } } diff --git a/lib/src/screens/search_delegates.dart b/lib/src/screens/search_delegates.dart index 86e1a04..68628d8 100644 --- a/lib/src/screens/search_delegates.dart +++ b/lib/src/screens/search_delegates.dart @@ -1,10 +1,12 @@ -import 'package:dev_server_starter/src/provider/types.dart'; import 'package:flutter/material.dart'; import '../provider/events.dart'; +import '../provider/types.dart'; -class SchematicTypeSearchDelegate extends SearchDelegate { - final List types; +final RESET_TYPE = SchematicType("RESET", "RESET"); + +class SchematicTypeSearchDelegate extends SearchDelegate { + final List types; SchematicTypeSearchDelegate(this.types); @@ -42,7 +44,7 @@ class SchematicTypeSearchDelegate extends SearchDelegate { Widget _buildList(BuildContext context) { final out = types - .where((element) => element.toLowerCase().contains(query.toLowerCase())) + .where((element) => element.name.contains(query.toLowerCase())) .toList(); return ListView.builder( itemCount: out.length + 1, @@ -52,7 +54,7 @@ class SchematicTypeSearchDelegate extends SearchDelegate { title: const Text("Reset"), leading: const Icon(Icons.clear), onTap: () { - close(context, "reset"); + close(context, RESET_TYPE); }, ); } @@ -60,7 +62,7 @@ class SchematicTypeSearchDelegate extends SearchDelegate { final type = out[index - 1]; return ListTile( - title: Text(type), + title: Text(type.name), onTap: () { close(context, type); }, @@ -244,8 +246,8 @@ class UserSearchDelegate extends SearchDelegate { } } -class TeamSearchDelegate extends SearchDelegate { - final List teams; +class TeamSearchDelegate extends SearchDelegate { + final List teams; TeamSearchDelegate(this.teams); diff --git a/lib/src/screens/userinfo.dart b/lib/src/screens/userinfo.dart index dd8a293..80332be 100644 --- a/lib/src/screens/userinfo.dart +++ b/lib/src/screens/userinfo.dart @@ -1,13 +1,10 @@ -import 'package:dev_server_starter/src/provider/user.dart'; -import 'package:dev_server_starter/src/screens/components/error.dart'; -import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:path/path.dart'; import 'package:shared_preferences/shared_preferences.dart'; -import '../../main.dart'; +import '../provider/user.dart'; +import 'components/error.dart'; class SettingsScreen extends HookConsumerWidget { const SettingsScreen({Key? key}) : super(key: key); @@ -18,14 +15,7 @@ class SettingsScreen extends HookConsumerWidget { return userDate.when( data: (data) { - final userName = useTextEditingController(text: data.username); - final password = useTextEditingController(text: data.password ?? ""); - final privateKeyFile = useState(data.privateKeyFile); - final sqlUsername = - useTextEditingController(text: data.sqlUserName ?? ""); - final sqlPassword = - useTextEditingController(text: data.sqlPassword ?? ""); - final useDevDB = useState(data.useDevDB); + final keyController = useTextEditingController(text: data.key); final uneditablePastEvents = useState(data.uneditablePastEvents); final changed = useState(false); @@ -37,43 +27,16 @@ class SettingsScreen extends HookConsumerWidget { IconButton( onPressed: changed.value ? () async { - if (userName.text.isEmpty || - (privateKeyFile.value == null && - password.text.isEmpty)) { + if (keyController.text.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( - content: Text( - "Username and password or private key are required"), + content: Text("The Key must be Set"), ), ); return; } final prefs = await SharedPreferences.getInstance(); - await prefs.setString("username", userName.text); - if (password.text.isNotEmpty) { - await prefs.setString("password", password.text); - } else { - await prefs.remove("password"); - } - if (privateKeyFile.value != null) { - await prefs.setString( - "privateKeyFile", privateKeyFile.value!); - } else { - await prefs.remove("privateKeyFile"); - } - if (sqlUsername.text.isNotEmpty) { - await prefs.setString( - "sqlUsername", sqlUsername.text); - } else { - await prefs.remove("sqlUsername"); - } - if (sqlPassword.text.isNotEmpty) { - await prefs.setString( - "sqlPassword", sqlPassword.text); - } else { - await prefs.remove("sqlPassword"); - } - await prefs.setBool("sqlUseDevDB", useDevDB.value); + await prefs.setString("key", keyController.text); await prefs.setBool( "uneditablePastEvents", uneditablePastEvents.value); ref.refresh(userDataProvider); @@ -91,105 +54,17 @@ class SettingsScreen extends HookConsumerWidget { mainAxisAlignment: MainAxisAlignment.start, children: [ TextField( - controller: userName, + controller: keyController, decoration: const InputDecoration( border: OutlineInputBorder(), - labelText: 'Username', + labelText: 'Key', ), onChanged: (value) { changed.value = true; }, - ), - const SizedBox(height: 10), - TextField( - controller: password, obscureText: true, - decoration: const InputDecoration( - border: OutlineInputBorder(), - labelText: 'Password', - ), - onChanged: (value) { - changed.value = true; - }, ), const SizedBox(height: 10), - ElevatedButton( - onPressed: () async { - final result = await FilePicker.platform.pickFiles( - type: FileType.any, - allowMultiple: false, - dialogTitle: "Select a private key", - initialDirectory: - userHome != null ? join(userHome!, ".ssh") : null, - ); - - if (result != null) { - final file = result.files.first; - privateKeyFile.value = file.path; - changed.value = true; - } - }, - child: - Text(privateKeyFile.value ?? "Select private key file"), - onLongPress: () async { - final remove = await showDialog( - context: context, - builder: (context) { - return AlertDialog( - title: const Text("Remove private key file?"), - actions: [ - TextButton( - onPressed: () { - Navigator.pop(context, false); - }, - child: const Text("Cancel"), - ), - TextButton( - onPressed: () { - Navigator.pop(context, true); - }, - child: const Text("Remove"), - ), - ], - ); - }); - if (remove) { - privateKeyFile.value = null; - changed.value = true; - } - }, - ), - const SizedBox(height: 10), - TextField( - controller: sqlUsername, - decoration: const InputDecoration( - border: OutlineInputBorder(), - labelText: 'SQL Username', - ), - onChanged: (value) { - changed.value = true; - }, - ), - const SizedBox(height: 10), - TextField( - controller: sqlPassword, - obscureText: true, - decoration: const InputDecoration( - border: OutlineInputBorder(), - labelText: 'SQL Password', - ), - onChanged: (value) { - changed.value = true; - }, - ), - CheckboxListTile( - title: const Text("Use dev database"), - value: useDevDB.value, - onChanged: (value) { - useDevDB.value = value ?? false; - changed.value = true; - }, - ), CheckboxListTile( title: const Text("Disable Past Events uneditable"), value: uneditablePastEvents.value, diff --git a/linux/my_application.cc b/linux/my_application.cc index 6edd267..32bf3b3 100644 --- a/linux/my_application.cc +++ b/linux/my_application.cc @@ -40,11 +40,11 @@ static void my_application_activate(GApplication* application) { if (use_header_bar) { GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); gtk_widget_show(GTK_WIDGET(header_bar)); - gtk_header_bar_set_title(header_bar, "SteamWar Dev Server"); + gtk_header_bar_set_title(header_bar, "SteamWar Multitool"); gtk_header_bar_set_show_close_button(header_bar, TRUE); gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); } else { - gtk_window_set_title(window, "SteamWar Dev Server"); + gtk_window_set_title(window, "SteamWar Multitool"); } gtk_window_set_default_size(window, 1280, 720); diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 14e6964..287b6a9 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,10 +5,8 @@ import FlutterMacOS import Foundation -import path_provider_macos import shared_preferences_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { - PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) } diff --git a/pubspec.lock b/pubspec.lock index 61a7989..1fece27 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -22,13 +22,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.3.1" - asn1lib: - dependency: transitive - description: - name: asn1lib - url: "https://pub.dartlang.org" - source: hosted - version: "1.4.0" async: dependency: transitive description: @@ -43,13 +36,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.0" - buffer: - dependency: transitive - description: - name: buffer - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.1" build: dependency: transitive description: @@ -169,13 +155,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.2.4" - dartssh2: + dio: dependency: "direct main" description: - name: dartssh2 + name: dio url: "https://pub.dartlang.org" source: hosted - version: "2.7.3" + version: "4.0.6" equatable: dependency: transitive description: @@ -204,13 +190,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "6.1.4" - file_picker: - dependency: "direct main" - description: - name: file_picker - url: "https://pub.dartlang.org" - source: hosted - version: "5.2.4" fixnum: dependency: transitive description: @@ -237,13 +216,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.1" - flutter_plugin_android_lifecycle: - dependency: transitive - description: - name: flutter_plugin_android_lifecycle - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.7" flutter_riverpod: dependency: transitive description: @@ -387,13 +359,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.3" - mysql_client: - dependency: "direct main" - description: - name: mysql_client - url: "https://pub.dartlang.org" - source: hosted - version: "0.0.27" package_config: dependency: transitive description: @@ -402,33 +367,12 @@ packages: source: hosted version: "2.1.0" path: - dependency: "direct main" + dependency: transitive description: name: path url: "https://pub.dartlang.org" source: hosted version: "1.8.2" - path_provider: - dependency: "direct main" - description: - name: path_provider - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.11" - path_provider_android: - dependency: transitive - description: - name: path_provider_android - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.22" - path_provider_ios: - dependency: transitive - description: - name: path_provider_ios - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.11" path_provider_linux: dependency: transitive description: @@ -436,13 +380,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.7" - path_provider_macos: - dependency: transitive - description: - name: path_provider_macos - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.6" path_provider_platform_interface: dependency: transitive description: @@ -457,13 +394,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.3" - pinenacl: - dependency: transitive - description: - name: pinenacl - url: "https://pub.dartlang.org" - source: hosted - version: "0.5.1" platform: dependency: transitive description: @@ -485,13 +415,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.3" - pointycastle: - dependency: transitive - description: - name: pointycastle - url: "https://pub.dartlang.org" - source: hosted - version: "3.6.2" pool: dependency: transitive description: @@ -686,13 +609,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.0" - tuple: - dependency: transitive - description: - name: tuple - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.1" typed_data: dependency: transitive description: @@ -743,7 +659,7 @@ packages: source: hosted version: "3.4.0" yaml: - dependency: "direct main" + dependency: transitive description: name: yaml url: "https://pub.dartlang.org" diff --git a/pubspec.yaml b/pubspec.yaml index f7137e4..fa1cdb7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,4 +1,4 @@ -name: dev_server_starter +name: steamwar_multitool description: A new Flutter project. authors: - Chaoscaot @@ -14,17 +14,12 @@ dependencies: flutter: sdk: flutter cupertino_icons: ^1.0.2 - dartssh2: ^2.7.3 flutter_hooks: ^0.18.5+1 hooks_riverpod: ^2.0.2 json_annotation: '>=4.7.0 <4.8.0' - path: ^1.8.2 - path_provider: ^2.0.11 shared_preferences: ^2.0.15 - file_picker: ^5.2.2 xterm: ^3.4.0 - mysql_client: ^0.0.27 - yaml: ^3.1.1 + dio: ^4.0.6 dev_dependencies: diff --git a/web/favicon.png b/web/favicon.png new file mode 100644 index 0000000..8aaa46a Binary files /dev/null and b/web/favicon.png differ diff --git a/web/icons/Icon-192.png b/web/icons/Icon-192.png new file mode 100644 index 0000000..b749bfe Binary files /dev/null and b/web/icons/Icon-192.png differ diff --git a/web/icons/Icon-512.png b/web/icons/Icon-512.png new file mode 100644 index 0000000..88cfd48 Binary files /dev/null and b/web/icons/Icon-512.png differ diff --git a/web/icons/Icon-maskable-192.png b/web/icons/Icon-maskable-192.png new file mode 100644 index 0000000..eb9b4d7 Binary files /dev/null and b/web/icons/Icon-maskable-192.png differ diff --git a/web/icons/Icon-maskable-512.png b/web/icons/Icon-maskable-512.png new file mode 100644 index 0000000..d69c566 Binary files /dev/null and b/web/icons/Icon-maskable-512.png differ diff --git a/web/index.html b/web/index.html new file mode 100644 index 0000000..f03efee --- /dev/null +++ b/web/index.html @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + Event-Tool + + + + + + + + + + diff --git a/web/manifest.json b/web/manifest.json new file mode 100644 index 0000000..9587234 --- /dev/null +++ b/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "steamwar_multitool", + "short_name": "steamwar_multitool", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +} diff --git a/windows/runner/main.cpp b/windows/runner/main.cpp index 39f2b96..b7c3582 100644 --- a/windows/runner/main.cpp +++ b/windows/runner/main.cpp @@ -27,7 +27,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, FlutterWindow window(project); Win32Window::Point origin(10, 10); Win32Window::Size size(1280, 720); - if (!window.CreateAndShow(L"SteamWar Dev Server", origin, size)) { + if (!window.CreateAndShow(L"SteamWar Multitool", origin, size)) { return EXIT_FAILURE; } window.SetQuitOnClose(true);