Refactoring™
Dieser Commit ist enthalten in:
Ursprung
cc9d9d2f9a
Commit
12d4752782
@ -1,8 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:intl/intl.dart';
|
|
||||||
import 'package:steamwar_multitool/src/app.dart';
|
import 'package:steamwar_multitool/src/app.dart';
|
||||||
|
|
||||||
final kDateFormat = DateFormat("dd.MM.yyyy HH:mm");
|
|
||||||
|
|
||||||
void main() => runApp(const ProviderScope(child: DevServerStarterApp()));
|
void main() => runApp(const ProviderScope(child: DevServerStarterApp()));
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:steamwar_multitool/src/screens/event.dart';
|
import 'package:steamwar_multitool/src/screens/event/event.dart';
|
||||||
import 'package:steamwar_multitool/src/screens/event_fights_graph.dart';
|
import 'package:steamwar_multitool/src/screens/event/event_fights_graph.dart';
|
||||||
|
|
||||||
import 'screens/home.dart';
|
import 'screens/home/home.dart';
|
||||||
import 'screens/login.dart';
|
import 'screens/login/login.dart';
|
||||||
import 'screens/userinfo.dart';
|
import 'screens/settings/userinfo.dart';
|
||||||
|
|
||||||
final _routes = GoRouter(
|
final _routes = GoRouter(
|
||||||
routes: [
|
routes: [
|
||||||
|
2
lib/src/components/components.dart
Normale Datei
2
lib/src/components/components.dart
Normale Datei
@ -0,0 +1,2 @@
|
|||||||
|
export 'date_time_editor.dart';
|
||||||
|
export 'error.dart';
|
66
lib/src/components/date_time_editor.dart
Normale Datei
66
lib/src/components/date_time_editor.dart
Normale Datei
@ -0,0 +1,66 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:steamwar_multitool/src/util/constants.dart';
|
||||||
|
|
||||||
|
class DateTimeEditor extends HookConsumerWidget {
|
||||||
|
final void Function(DateTime) onChanged;
|
||||||
|
final DateTime initialDate;
|
||||||
|
final bool enabled;
|
||||||
|
final MainAxisAlignment mainAxisAlignment;
|
||||||
|
const DateTimeEditor(this.onChanged, this.initialDate, this.enabled,
|
||||||
|
{Key? key, this.mainAxisAlignment = MainAxisAlignment.start})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
return Row(
|
||||||
|
mainAxisAlignment: mainAxisAlignment,
|
||||||
|
children: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: enabled
|
||||||
|
? () async {
|
||||||
|
final date = await showDatePicker(
|
||||||
|
context: context,
|
||||||
|
initialDate: initialDate,
|
||||||
|
firstDate: DateTime(2020),
|
||||||
|
lastDate: DateTime(2030),
|
||||||
|
);
|
||||||
|
if (date != null) {
|
||||||
|
onChanged(date);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
child: Text(kDateFormat.format(initialDate)),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
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),
|
||||||
|
);
|
||||||
|
if (time != null) {
|
||||||
|
final changed = DateTime(
|
||||||
|
initialDate.year,
|
||||||
|
initialDate.month,
|
||||||
|
initialDate.day,
|
||||||
|
time.hour,
|
||||||
|
time.minute);
|
||||||
|
onChanged(changed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
icon: const Icon(Icons.schedule)),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
6
lib/src/delegates/delegates.dart
Normale Datei
6
lib/src/delegates/delegates.dart
Normale Datei
@ -0,0 +1,6 @@
|
|||||||
|
export 'gamemode.dart';
|
||||||
|
export 'groups.dart';
|
||||||
|
export 'map.dart';
|
||||||
|
export 'schematic_type.dart';
|
||||||
|
export 'team.dart';
|
||||||
|
export 'user.dart';
|
58
lib/src/delegates/gamemode.dart
Normale Datei
58
lib/src/delegates/gamemode.dart
Normale Datei
@ -0,0 +1,58 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class GamemodeSearchDelegate extends SearchDelegate<String?> {
|
||||||
|
final List<String> modes;
|
||||||
|
|
||||||
|
GamemodeSearchDelegate(this.modes);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Widget>? buildActions(BuildContext context) {
|
||||||
|
return [
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.clear),
|
||||||
|
onPressed: () {
|
||||||
|
query = "";
|
||||||
|
},
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget? buildLeading(BuildContext context) {
|
||||||
|
return IconButton(
|
||||||
|
icon: const Icon(Icons.arrow_back),
|
||||||
|
onPressed: () {
|
||||||
|
close(context, null);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget buildResults(BuildContext context) {
|
||||||
|
return _buildList(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget buildSuggestions(BuildContext context) {
|
||||||
|
return _buildList(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildList(BuildContext context) {
|
||||||
|
final out = modes
|
||||||
|
.where((element) => element.toLowerCase().contains(query.toLowerCase()))
|
||||||
|
.toList();
|
||||||
|
return ListView.builder(
|
||||||
|
itemCount: out.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final type = out[index];
|
||||||
|
|
||||||
|
return ListTile(
|
||||||
|
title: Text(type),
|
||||||
|
onTap: () {
|
||||||
|
close(context, type);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
67
lib/src/delegates/groups.dart
Normale Datei
67
lib/src/delegates/groups.dart
Normale Datei
@ -0,0 +1,67 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class GroupSearchDelegate extends SearchDelegate<String?> {
|
||||||
|
final List<String> groups;
|
||||||
|
|
||||||
|
GroupSearchDelegate(this.groups);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Widget>? buildActions(BuildContext context) {
|
||||||
|
return [
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.clear),
|
||||||
|
onPressed: () {
|
||||||
|
query = "";
|
||||||
|
},
|
||||||
|
)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget? buildLeading(BuildContext context) {
|
||||||
|
return IconButton(
|
||||||
|
icon: const Icon(Icons.arrow_back),
|
||||||
|
onPressed: () {
|
||||||
|
close(context, null);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget buildResults(BuildContext context) {
|
||||||
|
return buildSuggestions(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget buildSuggestions(BuildContext context) {
|
||||||
|
return ListView(
|
||||||
|
children: [
|
||||||
|
ListTile(
|
||||||
|
title: Text(query),
|
||||||
|
onTap: query.isEmpty
|
||||||
|
? null
|
||||||
|
: () {
|
||||||
|
close(context, query);
|
||||||
|
},
|
||||||
|
subtitle: const Text("Create new group"),
|
||||||
|
leading: const Icon(Icons.add),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(Icons.clear),
|
||||||
|
title: const Text("Reset"),
|
||||||
|
onTap: () {
|
||||||
|
close(context, "");
|
||||||
|
},
|
||||||
|
),
|
||||||
|
for (final group in groups)
|
||||||
|
if (group.toLowerCase().contains(query.toLowerCase()))
|
||||||
|
ListTile(
|
||||||
|
title: Text(group),
|
||||||
|
onTap: () {
|
||||||
|
close(context, group);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
58
lib/src/delegates/map.dart
Normale Datei
58
lib/src/delegates/map.dart
Normale Datei
@ -0,0 +1,58 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class MapSearchDelegate extends SearchDelegate<String?> {
|
||||||
|
final List<String> maps;
|
||||||
|
|
||||||
|
MapSearchDelegate(this.maps);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Widget>? buildActions(BuildContext context) {
|
||||||
|
return [
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.clear),
|
||||||
|
onPressed: () {
|
||||||
|
query = "";
|
||||||
|
},
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget? buildLeading(BuildContext context) {
|
||||||
|
return IconButton(
|
||||||
|
icon: const Icon(Icons.arrow_back),
|
||||||
|
onPressed: () {
|
||||||
|
close(context, null);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget buildResults(BuildContext context) {
|
||||||
|
return _buildList(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget buildSuggestions(BuildContext context) {
|
||||||
|
return _buildList(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildList(BuildContext context) {
|
||||||
|
final out = maps
|
||||||
|
.where((element) => element.toLowerCase().contains(query.toLowerCase()))
|
||||||
|
.toList();
|
||||||
|
return ListView.builder(
|
||||||
|
itemCount: out.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final type = out[index];
|
||||||
|
|
||||||
|
return ListTile(
|
||||||
|
title: Text(type),
|
||||||
|
onTap: () {
|
||||||
|
close(context, type);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
71
lib/src/delegates/schematic_type.dart
Normale Datei
71
lib/src/delegates/schematic_type.dart
Normale Datei
@ -0,0 +1,71 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:steamwar_multitool/src/types/types.dart';
|
||||||
|
|
||||||
|
final RESET_TYPE = SchematicType("RESET", "RESET");
|
||||||
|
|
||||||
|
class SchematicTypeSearchDelegate extends SearchDelegate<SchematicType?> {
|
||||||
|
final List<SchematicType> types;
|
||||||
|
|
||||||
|
SchematicTypeSearchDelegate(this.types);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Widget>? buildActions(BuildContext context) {
|
||||||
|
return [
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.clear),
|
||||||
|
onPressed: () {
|
||||||
|
query = "";
|
||||||
|
},
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget? buildLeading(BuildContext context) {
|
||||||
|
return IconButton(
|
||||||
|
icon: const Icon(Icons.arrow_back),
|
||||||
|
onPressed: () {
|
||||||
|
close(context, null);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget buildResults(BuildContext context) {
|
||||||
|
return _buildList(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget buildSuggestions(BuildContext context) {
|
||||||
|
return _buildList(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildList(BuildContext context) {
|
||||||
|
final out = types
|
||||||
|
.where((element) => element.name.contains(query.toLowerCase()))
|
||||||
|
.toList();
|
||||||
|
return ListView.builder(
|
||||||
|
itemCount: out.length + 1,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
if (index == 0) {
|
||||||
|
return ListTile(
|
||||||
|
title: const Text("Reset"),
|
||||||
|
leading: const Icon(Icons.clear),
|
||||||
|
onTap: () {
|
||||||
|
close(context, RESET_TYPE);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final type = out[index - 1];
|
||||||
|
|
||||||
|
return ListTile(
|
||||||
|
title: Text(type.name),
|
||||||
|
onTap: () {
|
||||||
|
close(context, type);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
74
lib/src/delegates/team.dart
Normale Datei
74
lib/src/delegates/team.dart
Normale Datei
@ -0,0 +1,74 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:steamwar_multitool/src/types/types.dart';
|
||||||
|
|
||||||
|
class TeamSearchDelegate extends SearchDelegate<Team?> {
|
||||||
|
final List<Team> teams;
|
||||||
|
|
||||||
|
TeamSearchDelegate(this.teams);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Widget>? buildActions(BuildContext context) {
|
||||||
|
return [
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.clear),
|
||||||
|
onPressed: () {
|
||||||
|
query = "";
|
||||||
|
},
|
||||||
|
)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget? buildLeading(BuildContext context) {
|
||||||
|
return IconButton(
|
||||||
|
icon: const Icon(Icons.arrow_back),
|
||||||
|
onPressed: () {
|
||||||
|
close(context, null);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget buildResults(BuildContext context) {
|
||||||
|
return ListView(
|
||||||
|
children: [
|
||||||
|
ListTile(
|
||||||
|
title: const Text("?"),
|
||||||
|
onTap: () {
|
||||||
|
close(context, Team(-1, "?", "?", "8"));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
for (final team in teams)
|
||||||
|
if (team.name.toLowerCase().contains(query.toLowerCase()))
|
||||||
|
ListTile(
|
||||||
|
title: Text(team.name),
|
||||||
|
onTap: () {
|
||||||
|
close(context, team);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget buildSuggestions(BuildContext context) {
|
||||||
|
return ListView(
|
||||||
|
children: [
|
||||||
|
ListTile(
|
||||||
|
title: const Text("?"),
|
||||||
|
onTap: () {
|
||||||
|
close(context, Team(-1, "?", "?", "8"));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
for (final team in teams)
|
||||||
|
if (team.name.toLowerCase().contains(query.toLowerCase()))
|
||||||
|
ListTile(
|
||||||
|
title: Text(team.name),
|
||||||
|
onTap: () {
|
||||||
|
close(context, team);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
62
lib/src/delegates/user.dart
Normale Datei
62
lib/src/delegates/user.dart
Normale Datei
@ -0,0 +1,62 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:steamwar_multitool/src/types/types.dart';
|
||||||
|
|
||||||
|
class UserSearchDelegate extends SearchDelegate<User?> {
|
||||||
|
final List<User> users;
|
||||||
|
|
||||||
|
UserSearchDelegate(this.users);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Widget>? buildActions(BuildContext context) {
|
||||||
|
return [
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.clear),
|
||||||
|
onPressed: () {
|
||||||
|
query = "";
|
||||||
|
},
|
||||||
|
)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget? buildLeading(BuildContext context) {
|
||||||
|
return IconButton(
|
||||||
|
icon: const Icon(Icons.arrow_back),
|
||||||
|
onPressed: () {
|
||||||
|
close(context, null);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget buildResults(BuildContext context) {
|
||||||
|
return ListView(
|
||||||
|
children: [
|
||||||
|
for (final team in users)
|
||||||
|
if (team.name.toLowerCase().contains(query.toLowerCase()))
|
||||||
|
ListTile(
|
||||||
|
title: Text(team.name),
|
||||||
|
onTap: () {
|
||||||
|
close(context, team);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget buildSuggestions(BuildContext context) {
|
||||||
|
return ListView(
|
||||||
|
children: [
|
||||||
|
for (final team in users)
|
||||||
|
if (team.name.toLowerCase().contains(query.toLowerCase()))
|
||||||
|
ListTile(
|
||||||
|
title: Text(team.name),
|
||||||
|
onTap: () {
|
||||||
|
close(context, team);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -2,12 +2,11 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:steamwar_multitool/src/components/components.dart';
|
||||||
|
import 'package:steamwar_multitool/src/provider/events.dart';
|
||||||
|
|
||||||
import '../../provider/events.dart';
|
class CreateEventDialog extends HookConsumerWidget {
|
||||||
import '../event.dart';
|
const CreateEventDialog({Key? key}) : super(key: key);
|
||||||
|
|
||||||
class EventDialog extends HookConsumerWidget {
|
|
||||||
const EventDialog({Key? key}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
@ -69,9 +68,6 @@ class EventDialog extends HookConsumerWidget {
|
|||||||
final event = await ref.read(eventRepositoryProvider.future).then(
|
final event = await ref.read(eventRepositoryProvider.future).then(
|
||||||
(value) => value.createEvent(
|
(value) => value.createEvent(
|
||||||
eventName.text, startTime.value, endTime.value));
|
eventName.text, startTime.value, endTime.value));
|
||||||
final eventFull = await ref
|
|
||||||
.read(eventRepositoryProvider.future)
|
|
||||||
.then((value) => value.getEvent(event.id));
|
|
||||||
context.go('/event/${event.id}');
|
context.go('/event/${event.id}');
|
||||||
},
|
},
|
||||||
child: const Text("Create"),
|
child: const Text("Create"),
|
1
lib/src/dialogs/dialogs.dart
Normale Datei
1
lib/src/dialogs/dialogs.dart
Normale Datei
@ -0,0 +1 @@
|
|||||||
|
export 'create_event_dialog.dart';
|
@ -1,7 +1,14 @@
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:steamwar_multitool/src/types/types.dart';
|
||||||
|
|
||||||
import 'http.dart';
|
import 'http.dart';
|
||||||
|
|
||||||
|
final groupsProvider = FutureProvider.autoDispose<List<String>>((ref) async {
|
||||||
|
return (await ref.watch(httpClient.future)).get("/data/groups").then((value) {
|
||||||
|
return (value.data as List).map((e) => e as String).toList();
|
||||||
|
});
|
||||||
|
}, dependencies: [httpClient]);
|
||||||
|
|
||||||
final fightServersProvider = FutureProvider((ref) async {
|
final fightServersProvider = FutureProvider((ref) async {
|
||||||
final client = await ref.watch(httpClient.future);
|
final client = await ref.watch(httpClient.future);
|
||||||
final res = await client.get("/data/gamemodes");
|
final res = await client.get("/data/gamemodes");
|
||||||
@ -33,25 +40,3 @@ final usersProvider = FutureProvider((ref) async {
|
|||||||
final res = await client.get("/data/users");
|
final res = await client.get("/data/users");
|
||||||
return (res.data as List<dynamic>).map((e) => User.fromJson(e)).toList();
|
return (res.data as List<dynamic>).map((e) => User.fromJson(e)).toList();
|
||||||
});
|
});
|
||||||
|
|
||||||
class User {
|
|
||||||
final int id;
|
|
||||||
final String name;
|
|
||||||
|
|
||||||
User(this.id, this.name);
|
|
||||||
|
|
||||||
factory User.fromJson(Map<String, dynamic> 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<String, dynamic> json) {
|
|
||||||
return SchematicType(json["name"], json["db"]);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +1,6 @@
|
|||||||
import 'dart:ui';
|
|
||||||
|
|
||||||
import 'package:dio/dio.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:steamwar_multitool/src/repositories/event.dart';
|
||||||
import 'package:steamwar_multitool/src/provider/types.dart';
|
import 'package:steamwar_multitool/src/types/types.dart';
|
||||||
|
|
||||||
import 'http.dart';
|
import 'http.dart';
|
||||||
|
|
||||||
@ -16,277 +12,3 @@ final eventsListProvider = FutureProvider<List<ShortEvent>>((ref) async {
|
|||||||
final repo = await ref.watch(eventRepositoryProvider.future);
|
final repo = await ref.watch(eventRepositoryProvider.future);
|
||||||
return repo.listEvents();
|
return repo.listEvents();
|
||||||
}, dependencies: [eventRepositoryProvider]);
|
}, dependencies: [eventRepositoryProvider]);
|
||||||
|
|
||||||
final groupsProvider = FutureProvider.autoDispose<List<String>>((ref) async {
|
|
||||||
return (await ref.watch(httpClient.future)).get("/data/groups").then((value) {
|
|
||||||
return (value.data as List).map((e) => e as String).toList();
|
|
||||||
});
|
|
||||||
}, dependencies: [httpClient]);
|
|
||||||
|
|
||||||
class EventRepository {
|
|
||||||
final Dio _client;
|
|
||||||
|
|
||||||
EventRepository(this._client);
|
|
||||||
|
|
||||||
Future<List<ShortEvent>> listEvents() async {
|
|
||||||
final res = await _client.get("/events");
|
|
||||||
return (res.data as List).map((e) => ShortEvent.fromJson(e)).toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<EventExtended> getEvent(int id) async {
|
|
||||||
final res = await _client.get("/events/$id");
|
|
||||||
return EventExtended.fromJson(res.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> updateEvent(
|
|
||||||
int id,
|
|
||||||
String name,
|
|
||||||
DateTime deadline,
|
|
||||||
DateTime start,
|
|
||||||
DateTime end,
|
|
||||||
int maxTeamMembers,
|
|
||||||
String? schemType,
|
|
||||||
bool publicOnly,
|
|
||||||
bool useSpectateSystem) async {
|
|
||||||
await _client.put("/events/$id", data: {
|
|
||||||
"name": name,
|
|
||||||
"deadline": deadline.millisecondsSinceEpoch,
|
|
||||||
"start": start.millisecondsSinceEpoch,
|
|
||||||
"end": end.millisecondsSinceEpoch,
|
|
||||||
"maxTeamMembers": maxTeamMembers,
|
|
||||||
"schemType": schemType ?? "null",
|
|
||||||
"publicSchemsOnly": publicOnly,
|
|
||||||
"spectateSystem": useSpectateSystem,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Event> createEvent(String name, DateTime start, DateTime end) async {
|
|
||||||
final res = await _client.post("/events", data: {
|
|
||||||
"name": name,
|
|
||||||
"start": start.millisecondsSinceEpoch,
|
|
||||||
"end": end.millisecondsSinceEpoch,
|
|
||||||
});
|
|
||||||
return Event.fromJson(res.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> deleteEvent(int id) async {
|
|
||||||
await _client.delete("/events/$id");
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> deleteFight(int id) async {
|
|
||||||
await _client.delete("/fights/$id");
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> createFight(int eventId, DateTime start, String mode, String map,
|
|
||||||
Team blue, Team red, int referee, String? group) {
|
|
||||||
return _client.post("/fights", data: {
|
|
||||||
"event": eventId,
|
|
||||||
"spielmodus": mode,
|
|
||||||
"map": map,
|
|
||||||
"start": start.millisecondsSinceEpoch,
|
|
||||||
"blueTeam": blue.id,
|
|
||||||
"redTeam": red.id,
|
|
||||||
"kampfleiter": referee,
|
|
||||||
"group": group ?? "null",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<List<EventFight>> listFights(int eventId) async {
|
|
||||||
final res = await _client.get("/events/$eventId/fights");
|
|
||||||
return (res.data as List).map((e) => EventFight.fromJson(e)).toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> updateFight(int id, DateTime start, String mode, String map,
|
|
||||||
int referee, Team blue, Team red, String? group) {
|
|
||||||
return _client.put("/fights/$id", data: {
|
|
||||||
"spielmodus": mode,
|
|
||||||
"map": map,
|
|
||||||
"start": start.millisecondsSinceEpoch,
|
|
||||||
"kampfleiter": referee,
|
|
||||||
"blueTeam": blue.id,
|
|
||||||
"redTeam": red.id,
|
|
||||||
"group": group ?? "null",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class EventFight {
|
|
||||||
final int id;
|
|
||||||
final String gameMode;
|
|
||||||
final String map;
|
|
||||||
final DateTime start;
|
|
||||||
final Team redTeam;
|
|
||||||
final Team blueTeam;
|
|
||||||
final User kampfleiter;
|
|
||||||
final int score;
|
|
||||||
final String? group;
|
|
||||||
|
|
||||||
EventFight(this.id, this.gameMode, this.map, this.start, this.redTeam,
|
|
||||||
this.blueTeam, this.kampfleiter, this.score, this.group);
|
|
||||||
|
|
||||||
Team get winner {
|
|
||||||
return getTeamWithContextColor(Colors.white);
|
|
||||||
}
|
|
||||||
|
|
||||||
Team getTeamWithContextColor(Color color) {
|
|
||||||
switch (score) {
|
|
||||||
case 1:
|
|
||||||
return blueTeam;
|
|
||||||
case 2:
|
|
||||||
return redTeam;
|
|
||||||
case 3:
|
|
||||||
return Team(
|
|
||||||
-1, "Tie", "TIE", color.computeLuminance() > 0.5 ? "f" : "0");
|
|
||||||
default:
|
|
||||||
return Team(
|
|
||||||
-1, "Unknown", "UNK", color.computeLuminance() > 0.5 ? "f" : "0");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
factory EventFight.fromJson(Map<String, dynamic> json) {
|
|
||||||
return EventFight(
|
|
||||||
json["id"],
|
|
||||||
json["spielmodus"],
|
|
||||||
json["map"],
|
|
||||||
DateTime.fromMillisecondsSinceEpoch(json["start"]),
|
|
||||||
Team.fromJson(json["redTeam"]),
|
|
||||||
Team.fromJson(json["blueTeam"]),
|
|
||||||
User.fromJson(json["kampfleiter"]),
|
|
||||||
json["ergebnis"],
|
|
||||||
json["group"]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Team {
|
|
||||||
final int id;
|
|
||||||
final String name;
|
|
||||||
final String kuerzel;
|
|
||||||
final String colorCode;
|
|
||||||
|
|
||||||
Team(this.id, this.name, this.kuerzel, this.colorCode);
|
|
||||||
|
|
||||||
Color get color {
|
|
||||||
switch (colorCode) {
|
|
||||||
case "1":
|
|
||||||
return const Color(0xFF0000AA);
|
|
||||||
case "2":
|
|
||||||
return const Color(0xFF00AA00);
|
|
||||||
case "3":
|
|
||||||
return const Color(0xFF00AAAA);
|
|
||||||
case "4":
|
|
||||||
return const Color(0xFFAA0000);
|
|
||||||
case "5":
|
|
||||||
return const Color(0xFFAA00AA);
|
|
||||||
case "6":
|
|
||||||
return const Color(0xFFFFAA00);
|
|
||||||
case "7":
|
|
||||||
return const Color(0xFFAAAAAA);
|
|
||||||
case "8":
|
|
||||||
return const Color(0xFF555555);
|
|
||||||
case "9":
|
|
||||||
return const Color(0xFF5555FF);
|
|
||||||
case "a":
|
|
||||||
return const Color(0xFF55FF55);
|
|
||||||
case "b":
|
|
||||||
return const Color(0xFF55FFFF);
|
|
||||||
case "c":
|
|
||||||
return const Color(0xFFFF5555);
|
|
||||||
case "d":
|
|
||||||
return const Color(0xFFFF55FF);
|
|
||||||
case "e":
|
|
||||||
return const Color(0xFFFFFF55);
|
|
||||||
case "f":
|
|
||||||
return const Color(0xFFFFFFFF);
|
|
||||||
default:
|
|
||||||
return const Color(0xFF000000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
factory Team.fromJson(Map<String, dynamic> 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<EventFight> fights;
|
|
||||||
final List<Team> teams;
|
|
||||||
|
|
||||||
EventExtended(this.event, this.fights, this.teams);
|
|
||||||
|
|
||||||
factory EventExtended.fromJson(Map<String, dynamic> json) {
|
|
||||||
return EventExtended(
|
|
||||||
Event.fromJson(json['event']),
|
|
||||||
(json['fights'] as List<dynamic>)
|
|
||||||
.map((e) => EventFight.fromJson(e))
|
|
||||||
.toList(),
|
|
||||||
(json['teams'] as List<dynamic>).map((e) => Team.fromJson(e)).toList(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Event {
|
|
||||||
final int id;
|
|
||||||
final String name;
|
|
||||||
final DateTime deadline;
|
|
||||||
final DateTime start;
|
|
||||||
final DateTime end;
|
|
||||||
final int maxTeamMembers;
|
|
||||||
final String? schemType;
|
|
||||||
final bool publicSchemsOnly;
|
|
||||||
final bool spectateSystem;
|
|
||||||
|
|
||||||
Event({
|
|
||||||
required this.id,
|
|
||||||
required this.name,
|
|
||||||
required this.deadline,
|
|
||||||
required this.start,
|
|
||||||
required this.end,
|
|
||||||
required this.maxTeamMembers,
|
|
||||||
required this.schemType,
|
|
||||||
required this.publicSchemsOnly,
|
|
||||||
required this.spectateSystem,
|
|
||||||
});
|
|
||||||
|
|
||||||
factory Event.fromJson(Map<String, dynamic> 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;
|
|
||||||
final DateTime start;
|
|
||||||
final DateTime end;
|
|
||||||
|
|
||||||
ShortEvent(this.name, this.id, this.start, this.end);
|
|
||||||
|
|
||||||
get isUpcoming => start.isAfter(DateTime.now());
|
|
||||||
|
|
||||||
get isCurrent =>
|
|
||||||
start.isBefore(DateTime.now()) && end.isAfter(DateTime.now());
|
|
||||||
|
|
||||||
factory ShortEvent.fromJson(Map<String, dynamic> json) {
|
|
||||||
return ShortEvent(
|
|
||||||
json["name"],
|
|
||||||
json["id"],
|
|
||||||
DateTime.fromMillisecondsSinceEpoch(json["start"]),
|
|
||||||
DateTime.fromMillisecondsSinceEpoch(json["end"]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import 'package:dio/dio.dart';
|
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:steamwar_multitool/src/provider/http.dart';
|
import 'package:steamwar_multitool/src/provider/http.dart';
|
||||||
|
import 'package:steamwar_multitool/src/repositories/mod.dart';
|
||||||
|
import 'package:steamwar_multitool/src/types/types.dart';
|
||||||
|
|
||||||
final modRepository = FutureProvider(
|
final modRepository = FutureProvider(
|
||||||
(ref) async => ModRepository(await ref.read(httpClient.future)),
|
(ref) async => ModRepository(await ref.read(httpClient.future)),
|
||||||
@ -15,85 +16,3 @@ final mods = FutureProvider<List<Mod>>((ref) async {
|
|||||||
final repo = await ref.watch(modRepository.future);
|
final repo = await ref.watch(modRepository.future);
|
||||||
return repo.listMods();
|
return repo.listMods();
|
||||||
}, dependencies: [modRepository]);
|
}, dependencies: [modRepository]);
|
||||||
|
|
||||||
class ModRepository {
|
|
||||||
final Dio _client;
|
|
||||||
|
|
||||||
ModRepository(this._client);
|
|
||||||
|
|
||||||
Future<List<Mod>> listMods() async {
|
|
||||||
final res = await _client.get("/mods/all");
|
|
||||||
return (res.data as List).map((e) => Mod.fromJson(e)).toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<List<Mod>> listUnchecked() async {
|
|
||||||
final res = await _client.get("/mods/unchecked");
|
|
||||||
return (res.data as List).map((e) => Mod.fromJson(e)).toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> updateMod(Platform platform, String name, ModType type) {
|
|
||||||
return _client.put("/mods/${platform.name}/$name", data: {
|
|
||||||
"modType": type.name,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Mod {
|
|
||||||
final Platform platform;
|
|
||||||
final String name;
|
|
||||||
final ModType type;
|
|
||||||
|
|
||||||
Mod(this.platform, this.name, this.type);
|
|
||||||
|
|
||||||
factory Mod.fromJson(Map<String, dynamic> json) {
|
|
||||||
return Mod(
|
|
||||||
Platform.fromOrdinal(json["platform"] as int),
|
|
||||||
json["modName"],
|
|
||||||
ModType.fromOrdinal(json["modType"] as int),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Platform {
|
|
||||||
FORGE,
|
|
||||||
LABYMOD,
|
|
||||||
FABRIC;
|
|
||||||
|
|
||||||
static Platform fromOrdinal(int ordinal) {
|
|
||||||
switch (ordinal) {
|
|
||||||
case 0:
|
|
||||||
return FORGE;
|
|
||||||
case 1:
|
|
||||||
return LABYMOD;
|
|
||||||
case 2:
|
|
||||||
return FABRIC;
|
|
||||||
default:
|
|
||||||
throw Exception("Invalid ordinal");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum ModType {
|
|
||||||
UNKLASSIFIED,
|
|
||||||
GREEN,
|
|
||||||
YELLOW,
|
|
||||||
RED,
|
|
||||||
YOUTUBER_ONLY;
|
|
||||||
|
|
||||||
static ModType fromOrdinal(int ordinal) {
|
|
||||||
switch (ordinal) {
|
|
||||||
case 0:
|
|
||||||
return UNKLASSIFIED;
|
|
||||||
case 1:
|
|
||||||
return GREEN;
|
|
||||||
case 2:
|
|
||||||
return YELLOW;
|
|
||||||
case 3:
|
|
||||||
return RED;
|
|
||||||
case 4:
|
|
||||||
return YOUTUBER_ONLY;
|
|
||||||
default:
|
|
||||||
throw Exception("Invalid ordinal");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
5
lib/src/provider/provider.dart
Normale Datei
5
lib/src/provider/provider.dart
Normale Datei
@ -0,0 +1,5 @@
|
|||||||
|
export 'data.dart';
|
||||||
|
export 'events.dart';
|
||||||
|
export 'http.dart';
|
||||||
|
export 'mods.dart';
|
||||||
|
export 'user.dart';
|
@ -1,23 +0,0 @@
|
|||||||
import 'dart:convert';
|
|
||||||
|
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
|
||||||
|
|
||||||
import '../screens/console.dart';
|
|
||||||
|
|
||||||
final serversProvider = FutureProvider<List<String>>((ref) async {
|
|
||||||
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 {
|
|
||||||
final prefs = await SharedPreferences.getInstance();
|
|
||||||
final ret = prefs.getStringList("favorite_servers") ?? [];
|
|
||||||
return ret.map((e) => ServerStartParameters.fromJson(jsonDecode(e))).toList();
|
|
||||||
});
|
|
@ -1,5 +1,6 @@
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
import 'package:steamwar_multitool/src/types/types.dart';
|
||||||
|
|
||||||
final userDataProvider = FutureProvider<UserData>((ref) async {
|
final userDataProvider = FutureProvider<UserData>((ref) async {
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
@ -11,13 +12,3 @@ final userDataProvider = FutureProvider<UserData>((ref) async {
|
|||||||
uneditablePastEvents: uneditablePastEvents,
|
uneditablePastEvents: uneditablePastEvents,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
class UserData {
|
|
||||||
final String key;
|
|
||||||
final bool uneditablePastEvents;
|
|
||||||
|
|
||||||
UserData({
|
|
||||||
required this.key,
|
|
||||||
required this.uneditablePastEvents,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
89
lib/src/repositories/event.dart
Normale Datei
89
lib/src/repositories/event.dart
Normale Datei
@ -0,0 +1,89 @@
|
|||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:steamwar_multitool/src/types/types.dart';
|
||||||
|
|
||||||
|
class EventRepository {
|
||||||
|
final Dio _client;
|
||||||
|
|
||||||
|
EventRepository(this._client);
|
||||||
|
|
||||||
|
Future<List<ShortEvent>> listEvents() async {
|
||||||
|
final res = await _client.get("/events");
|
||||||
|
return (res.data as List).map((e) => ShortEvent.fromJson(e)).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<EventExtended> getEvent(int id) async {
|
||||||
|
final res = await _client.get("/events/$id");
|
||||||
|
return EventExtended.fromJson(res.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> updateEvent(
|
||||||
|
int id,
|
||||||
|
String name,
|
||||||
|
DateTime deadline,
|
||||||
|
DateTime start,
|
||||||
|
DateTime end,
|
||||||
|
int maxTeamMembers,
|
||||||
|
String? schemType,
|
||||||
|
bool publicOnly,
|
||||||
|
bool useSpectateSystem) async {
|
||||||
|
await _client.put("/events/$id", data: {
|
||||||
|
"name": name,
|
||||||
|
"deadline": deadline.millisecondsSinceEpoch,
|
||||||
|
"start": start.millisecondsSinceEpoch,
|
||||||
|
"end": end.millisecondsSinceEpoch,
|
||||||
|
"maxTeamMembers": maxTeamMembers,
|
||||||
|
"schemType": schemType ?? "null",
|
||||||
|
"publicSchemsOnly": publicOnly,
|
||||||
|
"spectateSystem": useSpectateSystem,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Event> createEvent(String name, DateTime start, DateTime end) async {
|
||||||
|
final res = await _client.post("/events", data: {
|
||||||
|
"name": name,
|
||||||
|
"start": start.millisecondsSinceEpoch,
|
||||||
|
"end": end.millisecondsSinceEpoch,
|
||||||
|
});
|
||||||
|
return Event.fromJson(res.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> deleteEvent(int id) async {
|
||||||
|
await _client.delete("/events/$id");
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> deleteFight(int id) async {
|
||||||
|
await _client.delete("/fights/$id");
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> createFight(int eventId, DateTime start, String mode, String map,
|
||||||
|
Team blue, Team red, int referee, String? group) {
|
||||||
|
return _client.post("/fights", data: {
|
||||||
|
"event": eventId,
|
||||||
|
"spielmodus": mode,
|
||||||
|
"map": map,
|
||||||
|
"start": start.millisecondsSinceEpoch,
|
||||||
|
"blueTeam": blue.id,
|
||||||
|
"redTeam": red.id,
|
||||||
|
"kampfleiter": referee,
|
||||||
|
"group": group ?? "null",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<EventFight>> listFights(int eventId) async {
|
||||||
|
final res = await _client.get("/events/$eventId/fights");
|
||||||
|
return (res.data as List).map((e) => EventFight.fromJson(e)).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> updateFight(int id, DateTime start, String mode, String map,
|
||||||
|
int referee, Team blue, Team red, String? group) {
|
||||||
|
return _client.put("/fights/$id", data: {
|
||||||
|
"spielmodus": mode,
|
||||||
|
"map": map,
|
||||||
|
"start": start.millisecondsSinceEpoch,
|
||||||
|
"kampfleiter": referee,
|
||||||
|
"blueTeam": blue.id,
|
||||||
|
"redTeam": red.id,
|
||||||
|
"group": group ?? "null",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
24
lib/src/repositories/mod.dart
Normale Datei
24
lib/src/repositories/mod.dart
Normale Datei
@ -0,0 +1,24 @@
|
|||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:steamwar_multitool/src/types/mods.dart';
|
||||||
|
|
||||||
|
class ModRepository {
|
||||||
|
final Dio _client;
|
||||||
|
|
||||||
|
ModRepository(this._client);
|
||||||
|
|
||||||
|
Future<List<Mod>> listMods() async {
|
||||||
|
final res = await _client.get("/mods/all");
|
||||||
|
return (res.data as List).map((e) => Mod.fromJson(e)).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<Mod>> listUnchecked() async {
|
||||||
|
final res = await _client.get("/mods/unchecked");
|
||||||
|
return (res.data as List).map((e) => Mod.fromJson(e)).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> updateMod(Platform platform, String name, ModType type) {
|
||||||
|
return _client.put("/mods/${platform.name}/$name", data: {
|
||||||
|
"modType": type.name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -1,290 +0,0 @@
|
|||||||
import 'dart:convert';
|
|
||||||
|
|
||||||
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 '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);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
|
||||||
final servers = ref.watch(serversProvider);
|
|
||||||
final favoriteServers = ref.watch(favoriteServersProvider);
|
|
||||||
return servers.when(
|
|
||||||
data: (data) {
|
|
||||||
return ListView(
|
|
||||||
children: [
|
|
||||||
favoriteServers.when(
|
|
||||||
data: (data) {
|
|
||||||
if (data.isEmpty) {
|
|
||||||
return Container();
|
|
||||||
}
|
|
||||||
return Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
const ListTile(
|
|
||||||
title: Text("Favorites"),
|
|
||||||
),
|
|
||||||
SizedBox(
|
|
||||||
height: 200,
|
|
||||||
child: ListView(
|
|
||||||
shrinkWrap: true,
|
|
||||||
scrollDirection: Axis.horizontal,
|
|
||||||
children: [
|
|
||||||
for (final server in data)
|
|
||||||
AspectRatio(
|
|
||||||
aspectRatio: 1,
|
|
||||||
child: Card(
|
|
||||||
child: InkWell(
|
|
||||||
onTap: () {
|
|
||||||
Navigator.of(context).push(
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) {
|
|
||||||
return ConsoleScreen(
|
|
||||||
server,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(8.0),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment:
|
|
||||||
CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(server.name,
|
|
||||||
style: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.headline6),
|
|
||||||
if (server.world != null)
|
|
||||||
Text("World: ${server.world!}"),
|
|
||||||
if (server.plugins != null)
|
|
||||||
Text("Plugins: ${server.plugins!}"),
|
|
||||||
if (server.port != null)
|
|
||||||
Text("Port: ${server.port!}"),
|
|
||||||
const Spacer(),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment:
|
|
||||||
MainAxisAlignment.end,
|
|
||||||
children: [
|
|
||||||
IconButton(
|
|
||||||
onPressed: () async {
|
|
||||||
final remove =
|
|
||||||
await showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (contex) {
|
|
||||||
return AlertDialog(
|
|
||||||
title: const Text(
|
|
||||||
"Remove from favorites?"),
|
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
onPressed:
|
|
||||||
() {
|
|
||||||
Navigator.of(
|
|
||||||
context)
|
|
||||||
.pop(
|
|
||||||
false);
|
|
||||||
},
|
|
||||||
child: const Text(
|
|
||||||
"Cancel")),
|
|
||||||
TextButton(
|
|
||||||
onPressed:
|
|
||||||
() async {
|
|
||||||
Navigator.of(
|
|
||||||
context)
|
|
||||||
.pop(
|
|
||||||
true);
|
|
||||||
},
|
|
||||||
child: const Text(
|
|
||||||
"Remove")),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
});
|
|
||||||
if (remove) {
|
|
||||||
final favs = data.toList();
|
|
||||||
favs.remove(server);
|
|
||||||
final prefs =
|
|
||||||
await SharedPreferences
|
|
||||||
.getInstance();
|
|
||||||
prefs.setStringList(
|
|
||||||
"favorite_servers",
|
|
||||||
favs
|
|
||||||
.map((e) =>
|
|
||||||
jsonEncode(
|
|
||||||
e.toJson()))
|
|
||||||
.toList());
|
|
||||||
ref.invalidate(
|
|
||||||
favoriteServersProvider);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
icon: const Icon(Icons.delete),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
error: (err, stack) => ErrorComponent(err, stack),
|
|
||||||
loading: () => const LinearProgressIndicator()),
|
|
||||||
for (final server in data)
|
|
||||||
ListTile(
|
|
||||||
leading: const Icon(Icons.dns),
|
|
||||||
title: Text(server),
|
|
||||||
onTap: () {
|
|
||||||
showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (context) =>
|
|
||||||
_CustomizeServerStartParameters(server));
|
|
||||||
},
|
|
||||||
trailing: Tooltip(
|
|
||||||
message: "Quick Start",
|
|
||||||
child: IconButton(
|
|
||||||
icon: const Icon(Icons.play_arrow),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) =>
|
|
||||||
ConsoleScreen(ServerStartParameters(server)),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
error: (err, stack) {
|
|
||||||
return ErrorComponent(err, stack);
|
|
||||||
},
|
|
||||||
loading: () {
|
|
||||||
return const Center(
|
|
||||||
child: CircularProgressIndicator(),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _CustomizeServerStartParameters extends HookConsumerWidget {
|
|
||||||
final String server;
|
|
||||||
const _CustomizeServerStartParameters(this.server, {Key? key})
|
|
||||||
: super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
|
||||||
final worldNameController = useTextEditingController();
|
|
||||||
final pluginsController = useTextEditingController();
|
|
||||||
final portController = useTextEditingController();
|
|
||||||
final saved = useState(false);
|
|
||||||
|
|
||||||
ServerStartParameters constructParameters() {
|
|
||||||
return ServerStartParameters(
|
|
||||||
server,
|
|
||||||
world:
|
|
||||||
worldNameController.text.isEmpty ? null : worldNameController.text,
|
|
||||||
plugins: pluginsController.text.isEmpty ? null : pluginsController.text,
|
|
||||||
port:
|
|
||||||
portController.text.isEmpty ? null : int.parse(portController.text),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return AlertDialog(
|
|
||||||
title: Text("Start $server"),
|
|
||||||
content: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
TextField(
|
|
||||||
decoration: const InputDecoration(
|
|
||||||
labelText: "World Name",
|
|
||||||
border: OutlineInputBorder(),
|
|
||||||
),
|
|
||||||
controller: worldNameController,
|
|
||||||
onChanged: (d) => saved.value = false,
|
|
||||||
),
|
|
||||||
const SizedBox(
|
|
||||||
height: 16,
|
|
||||||
),
|
|
||||||
TextField(
|
|
||||||
decoration: const InputDecoration(
|
|
||||||
labelText: "Plugins",
|
|
||||||
border: OutlineInputBorder(),
|
|
||||||
),
|
|
||||||
controller: pluginsController,
|
|
||||||
onChanged: (d) => saved.value = false,
|
|
||||||
),
|
|
||||||
const SizedBox(
|
|
||||||
height: 16,
|
|
||||||
),
|
|
||||||
// Allow only Numbers
|
|
||||||
TextField(
|
|
||||||
decoration: const InputDecoration(
|
|
||||||
labelText: "Port",
|
|
||||||
border: OutlineInputBorder(),
|
|
||||||
),
|
|
||||||
inputFormatters: [
|
|
||||||
FilteringTextInputFormatter.singleLineFormatter,
|
|
||||||
FilteringTextInputFormatter.digitsOnly
|
|
||||||
],
|
|
||||||
keyboardType: TextInputType.number,
|
|
||||||
controller: portController,
|
|
||||||
onChanged: (d) => saved.value = false,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
},
|
|
||||||
child: const Text("Cancel"),
|
|
||||||
),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () async {
|
|
||||||
final favs = await ref.read(favoriteServersProvider.future);
|
|
||||||
favs.add(constructParameters());
|
|
||||||
final prefs = await SharedPreferences.getInstance();
|
|
||||||
await prefs.setStringList(
|
|
||||||
"favorite_servers",
|
|
||||||
favs
|
|
||||||
.map((e) => e.toJson())
|
|
||||||
.map((e) => jsonEncode(e))
|
|
||||||
.toList());
|
|
||||||
ref.invalidate(favoriteServersProvider);
|
|
||||||
saved.value = true;
|
|
||||||
},
|
|
||||||
child: Text(saved.value ? "Saved!" : "Save")),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => ConsoleScreen(constructParameters()),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: const Text("Start"),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,147 +0,0 @@
|
|||||||
import 'dart:typed_data';
|
|
||||||
|
|
||||||
//import 'package:dartssh2/dartssh2.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|
||||||
import 'package:json_annotation/json_annotation.dart';
|
|
||||||
import 'package:xterm/xterm.dart';
|
|
||||||
|
|
||||||
part 'console.g.dart';
|
|
||||||
|
|
||||||
class ConsoleScreen extends StatefulHookConsumerWidget {
|
|
||||||
final ServerStartParameters parameters;
|
|
||||||
const ConsoleScreen(this.parameters, {Key? key}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
ConsumerState<ConsumerStatefulWidget> createState() => _ConsoleScreenState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ConsoleScreenState extends ConsumerState<ConsoleScreen> {
|
|
||||||
//late final SSHSession session;
|
|
||||||
late final Terminal terminal;
|
|
||||||
final _controller = ScrollController(keepScrollOffset: true);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final inputFieldController = useTextEditingController();
|
|
||||||
final inputFieldFocusNode = useFocusNode();
|
|
||||||
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
title: Text(widget.parameters.name),
|
|
||||||
actions: [
|
|
||||||
IconButton(
|
|
||||||
icon: const Icon(Icons.clear),
|
|
||||||
onPressed: () {
|
|
||||||
setState(() {
|
|
||||||
terminal.eraseDisplay();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
body: Padding(
|
|
||||||
padding: const EdgeInsets.all(8.0),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: TerminalView(terminal, alwaysShowCursor: false),
|
|
||||||
),
|
|
||||||
TextField(
|
|
||||||
focusNode: inputFieldFocusNode,
|
|
||||||
onSubmitted: (value) async {
|
|
||||||
inputFieldController.text = "";
|
|
||||||
terminal.write("> $value");
|
|
||||||
terminal.nextLine();
|
|
||||||
//session.write(
|
|
||||||
// Uint8List.fromList(value.codeUnits + '\n'.codeUnits));
|
|
||||||
inputFieldFocusNode.requestFocus();
|
|
||||||
},
|
|
||||||
decoration: const InputDecoration(
|
|
||||||
border: OutlineInputBorder(),
|
|
||||||
labelText: 'Command',
|
|
||||||
),
|
|
||||||
controller: inputFieldController,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
super.dispose();
|
|
||||||
_controller.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void deactivate() {
|
|
||||||
super.deactivate();
|
|
||||||
//session.kill(SSHSignal.KILL);
|
|
||||||
//session.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
terminal = Terminal();
|
|
||||||
terminal.setLineFeedMode(true);
|
|
||||||
/*final clientFut = ref.read(sshClientProvider);
|
|
||||||
final client = clientFut.value!;
|
|
||||||
client
|
|
||||||
.execute(
|
|
||||||
"python3 /binarys/dev.py ${widget.parameters.name} ${widget.parameters.extraArguments}")
|
|
||||||
.then((value) {
|
|
||||||
session = value;
|
|
||||||
session.stdout.listen((event) {
|
|
||||||
setState(() {
|
|
||||||
terminal.write(String.fromCharCodes(event));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
session.done.then((value) {
|
|
||||||
if (mounted) {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
if (session.exitCode != 0) {
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
|
||||||
content: Text("Exited with code ${session.exitCode}")));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});*/
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonSerializable()
|
|
||||||
class ServerStartParameters {
|
|
||||||
final String name;
|
|
||||||
final String? plugins;
|
|
||||||
final String? world;
|
|
||||||
final int? port;
|
|
||||||
|
|
||||||
ServerStartParameters(this.name, {this.plugins, this.world, this.port});
|
|
||||||
|
|
||||||
String get extraArguments {
|
|
||||||
final args = <String>[];
|
|
||||||
if (plugins != null) {
|
|
||||||
args.add("-p");
|
|
||||||
args.add(plugins!);
|
|
||||||
}
|
|
||||||
if (world != null) {
|
|
||||||
args.add("-w");
|
|
||||||
args.add(world!);
|
|
||||||
}
|
|
||||||
if (port != null) {
|
|
||||||
args.add("--port");
|
|
||||||
args.add(port.toString());
|
|
||||||
}
|
|
||||||
return args.join(" ");
|
|
||||||
}
|
|
||||||
|
|
||||||
factory ServerStartParameters.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$ServerStartParametersFromJson(json);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => _$ServerStartParametersToJson(this);
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
|
|
||||||
part of 'console.dart';
|
|
||||||
|
|
||||||
// **************************************************************************
|
|
||||||
// JsonSerializableGenerator
|
|
||||||
// **************************************************************************
|
|
||||||
|
|
||||||
ServerStartParameters _$ServerStartParametersFromJson(
|
|
||||||
Map<String, dynamic> json) =>
|
|
||||||
ServerStartParameters(
|
|
||||||
json['name'] as String,
|
|
||||||
plugins: json['plugins'] as String?,
|
|
||||||
world: json['world'] as String?,
|
|
||||||
port: json['port'] as int?,
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> _$ServerStartParametersToJson(
|
|
||||||
ServerStartParameters instance) =>
|
|
||||||
<String, dynamic>{
|
|
||||||
'name': instance.name,
|
|
||||||
'plugins': instance.plugins,
|
|
||||||
'world': instance.world,
|
|
||||||
'port': instance.port,
|
|
||||||
};
|
|
Datei-Diff unterdrückt, da er zu groß ist
Diff laden
147
lib/src/screens/event/components/dialogs/add_fight.dart
Normale Datei
147
lib/src/screens/event/components/dialogs/add_fight.dart
Normale Datei
@ -0,0 +1,147 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:steamwar_multitool/src/components/date_time_editor.dart';
|
||||||
|
import 'package:steamwar_multitool/src/provider/provider.dart';
|
||||||
|
import 'package:steamwar_multitool/src/screens/event/components/team_selector.dart';
|
||||||
|
import 'package:steamwar_multitool/src/delegates/delegates.dart';
|
||||||
|
import 'package:steamwar_multitool/src/types/types.dart';
|
||||||
|
|
||||||
|
class AddFightDialog extends HookConsumerWidget {
|
||||||
|
final void Function() onAdded;
|
||||||
|
final EventExtended eventData;
|
||||||
|
const AddFightDialog(
|
||||||
|
this.eventData,
|
||||||
|
this.onAdded, {
|
||||||
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final event = eventData.event;
|
||||||
|
final gamemode = useState<String?>(null);
|
||||||
|
final map = useState<String?>(null);
|
||||||
|
final blueTeam = useState<Team?>(null);
|
||||||
|
final redTeam = useState<Team?>(null);
|
||||||
|
final date = useState<DateTime>(event.start);
|
||||||
|
final referrer = useState<User?>(null);
|
||||||
|
final group = useState<String>("");
|
||||||
|
|
||||||
|
final canCreate = useMemoized(() {
|
||||||
|
return gamemode.value != null &&
|
||||||
|
map.value != null &&
|
||||||
|
blueTeam.value != null &&
|
||||||
|
redTeam.value != null;
|
||||||
|
}, [gamemode.value, map.value, blueTeam.value, redTeam.value]);
|
||||||
|
|
||||||
|
return AlertDialog(
|
||||||
|
title: const Text("Add Fight"),
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const Text("Gamemode"),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () async {
|
||||||
|
final modes = await ref.read(fightServersProvider.future);
|
||||||
|
final mode = await showSearch(
|
||||||
|
context: context, delegate: GamemodeSearchDelegate(modes));
|
||||||
|
if (mode != null) {
|
||||||
|
gamemode.value = mode;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Text(gamemode.value ?? "Select"),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
const Text("Map"),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: gamemode.value != null
|
||||||
|
? () async {
|
||||||
|
final maps = await ref.read(mapsProvider.future);
|
||||||
|
final sMap = await showSearch(
|
||||||
|
context: context,
|
||||||
|
delegate: MapSearchDelegate(maps[gamemode.value]!));
|
||||||
|
if (sMap != null) {
|
||||||
|
map.value = sMap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
child: Text(map.value ?? "Select"),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
const Text("Blue Team"),
|
||||||
|
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,
|
||||||
|
eventData.teams),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
DateTimeEditor((p0) => date.value = p0, date.value, true,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
const Text("Kampfleiter"),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () async {
|
||||||
|
final users = await ref.read(usersProvider.future);
|
||||||
|
final user = await showSearch(
|
||||||
|
context: context, delegate: UserSearchDelegate(users));
|
||||||
|
if (user != null) {
|
||||||
|
referrer.value = user;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Text(referrer.value == null || referrer.value!.id == 0
|
||||||
|
? "None"
|
||||||
|
: "${referrer.value!.name} (${referrer.value!.id})"),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
const Text("Group"),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () async {
|
||||||
|
final selGroup = await showSearch(
|
||||||
|
context: context,
|
||||||
|
delegate: GroupSearchDelegate(
|
||||||
|
await ref.read(groupsProvider.future)));
|
||||||
|
if (selGroup != null) {
|
||||||
|
if (selGroup.isEmpty) {
|
||||||
|
group.value = "";
|
||||||
|
} else {
|
||||||
|
group.value = selGroup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Text(group.value.isEmpty ? "None" : group.value)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
child: const Text("Cancel")),
|
||||||
|
TextButton(
|
||||||
|
onPressed: canCreate
|
||||||
|
? () async {
|
||||||
|
final nav = Navigator.of(context);
|
||||||
|
final repo = await ref.read(eventRepositoryProvider.future);
|
||||||
|
await repo.createFight(
|
||||||
|
event.id,
|
||||||
|
date.value,
|
||||||
|
gamemode.value!,
|
||||||
|
map.value!,
|
||||||
|
blueTeam.value!,
|
||||||
|
redTeam.value!,
|
||||||
|
referrer.value?.id ?? 0,
|
||||||
|
group.value.isEmpty ? null : group.value);
|
||||||
|
onAdded.call();
|
||||||
|
nav.pop();
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
child: const Text("Add")),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
2
lib/src/screens/event/components/dialogs/dialogs.dart
Normale Datei
2
lib/src/screens/event/components/dialogs/dialogs.dart
Normale Datei
@ -0,0 +1,2 @@
|
|||||||
|
export 'add_fight.dart';
|
||||||
|
export 'edit_fight.dart';
|
184
lib/src/screens/event/components/dialogs/edit_fight.dart
Normale Datei
184
lib/src/screens/event/components/dialogs/edit_fight.dart
Normale Datei
@ -0,0 +1,184 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:steamwar_multitool/src/components/date_time_editor.dart';
|
||||||
|
import 'package:steamwar_multitool/src/provider/provider.dart';
|
||||||
|
import 'package:steamwar_multitool/src/screens/event/components/team_selector.dart';
|
||||||
|
import 'package:steamwar_multitool/src/delegates/delegates.dart';
|
||||||
|
import 'package:steamwar_multitool/src/types/types.dart';
|
||||||
|
|
||||||
|
class EditEventFightDialog extends HookConsumerWidget {
|
||||||
|
const EditEventFightDialog(
|
||||||
|
{Key? key,
|
||||||
|
required this.fight,
|
||||||
|
required this.fightsRefresher,
|
||||||
|
required this.event})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
final EventFight fight;
|
||||||
|
final EventExtended event;
|
||||||
|
final void Function() fightsRefresher;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final blueTeam = useState(fight.blueTeam);
|
||||||
|
final redTeam = useState(fight.redTeam);
|
||||||
|
final start = useState(fight.start);
|
||||||
|
final mode = useState(fight.gameMode);
|
||||||
|
final map = useState(fight.map);
|
||||||
|
final referrer = useState(fight.kampfleiter);
|
||||||
|
final group = useState(fight.group ?? "");
|
||||||
|
|
||||||
|
return AlertDialog(
|
||||||
|
title: Text("${fight.blueTeam.name} vs ${fight.redTeam.name}"),
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
const Text("Blue Team"),
|
||||||
|
TeamSelector(event.event, blueTeam.value, (p0) {
|
||||||
|
blueTeam.value = p0;
|
||||||
|
}, event.teams),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
const Text("Red Team"),
|
||||||
|
TeamSelector(event.event, redTeam.value, (p0) {
|
||||||
|
redTeam.value = p0;
|
||||||
|
}, event.teams),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
const Text("Start"),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
DateTimeEditor((p0) {
|
||||||
|
start.value = p0;
|
||||||
|
}, start.value, true, mainAxisAlignment: MainAxisAlignment.center),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
start.value = start.value.add(const Duration(seconds: 30));
|
||||||
|
},
|
||||||
|
child: const Text("Add 30 Seconds"),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
const Text("Game Mode"),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () async {
|
||||||
|
final modes = await ref.read(fightServersProvider.future);
|
||||||
|
final out = await showSearch(
|
||||||
|
context: context, delegate: GamemodeSearchDelegate(modes));
|
||||||
|
if (out != null) {
|
||||||
|
mode.value = out;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Text(mode.value),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
const Text("Map"),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () async {
|
||||||
|
final maps = await ref.read(mapsProvider.future);
|
||||||
|
final out = await showSearch(
|
||||||
|
context: context,
|
||||||
|
delegate: MapSearchDelegate(maps[mode.value] ?? []));
|
||||||
|
if (out != null) {
|
||||||
|
map.value = out;
|
||||||
|
fightsRefresher();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Text(map.value),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
const Text("Kampfleiter"),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () async {
|
||||||
|
final users = await ref.read(usersProvider.future);
|
||||||
|
final user = await showSearch(
|
||||||
|
context: context, delegate: UserSearchDelegate(users));
|
||||||
|
if (user != null) {
|
||||||
|
referrer.value = user;
|
||||||
|
fightsRefresher();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Text(referrer.value.id == 0
|
||||||
|
? "None"
|
||||||
|
: "${referrer.value.name} (${referrer.value.id})"),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
const Text("Group"),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () async {
|
||||||
|
final selGroup = await showSearch(
|
||||||
|
context: context,
|
||||||
|
delegate: GroupSearchDelegate(
|
||||||
|
await ref.read(groupsProvider.future)));
|
||||||
|
if (selGroup != null) {
|
||||||
|
if (selGroup.isEmpty) {
|
||||||
|
group.value = "";
|
||||||
|
} else {
|
||||||
|
group.value = selGroup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Text(group.value.isEmpty ? "None" : group.value)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () async {
|
||||||
|
final delete = await showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => AlertDialog(
|
||||||
|
title: const Text("Delete Fight?"),
|
||||||
|
content: const Text(
|
||||||
|
"Do you really want to delete this fight?"),
|
||||||
|
icon: const Icon(Icons.warning),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(false),
|
||||||
|
child: const Text("No")),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(true),
|
||||||
|
child: const Text("Yes",
|
||||||
|
style: TextStyle(color: Colors.red))),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
if (delete) {
|
||||||
|
ref.read(eventRepositoryProvider.future).then(
|
||||||
|
(value) => value.deleteFight(fight.id).then(
|
||||||
|
(value) {
|
||||||
|
fightsRefresher();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: const Text("Delete", style: TextStyle(color: Colors.red))),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
child: const Text("Cancel")),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () async {
|
||||||
|
var nav = Navigator.of(context);
|
||||||
|
final repo = await ref.read(eventRepositoryProvider.future);
|
||||||
|
await repo.updateFight(
|
||||||
|
fight.id,
|
||||||
|
start.value,
|
||||||
|
mode.value,
|
||||||
|
map.value,
|
||||||
|
referrer.value.id,
|
||||||
|
blueTeam.value,
|
||||||
|
redTeam.value,
|
||||||
|
group.value.isEmpty ? null : group.value);
|
||||||
|
fightsRefresher();
|
||||||
|
nav.pop();
|
||||||
|
},
|
||||||
|
child: const Text("Save")),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
408
lib/src/screens/event/components/fight_list.dart
Normale Datei
408
lib/src/screens/event/components/fight_list.dart
Normale Datei
@ -0,0 +1,408 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:grouped_list/grouped_list.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:steamwar_multitool/src/components/date_time_editor.dart';
|
||||||
|
import 'package:steamwar_multitool/src/provider/provider.dart';
|
||||||
|
import 'package:steamwar_multitool/src/delegates/delegates.dart';
|
||||||
|
import 'package:steamwar_multitool/src/screens/event/components/dialogs/dialogs.dart';
|
||||||
|
import 'package:steamwar_multitool/src/types/types.dart';
|
||||||
|
import 'package:steamwar_multitool/src/util/constants.dart';
|
||||||
|
import 'package:steamwar_multitool/src/util/event_fight_exporter.dart';
|
||||||
|
|
||||||
|
class EventFightList extends HookConsumerWidget {
|
||||||
|
final EventExtended eventData;
|
||||||
|
|
||||||
|
const EventFightList({Key? key, required this.eventData}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final event = eventData.event;
|
||||||
|
final fights = useState<List<EventFight>>(eventData.fights);
|
||||||
|
final selected = useState<List<int>>([]);
|
||||||
|
|
||||||
|
final currentCheckBoxState = useMemoized<bool?>(() {
|
||||||
|
if (selected.value.isEmpty) {
|
||||||
|
return false;
|
||||||
|
} else if (selected.value.length == fights.value.length) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}, [selected.value, fights.value]);
|
||||||
|
|
||||||
|
void update() async {
|
||||||
|
final repo = await ref.read(eventRepositoryProvider.future);
|
||||||
|
fights.value = await repo.listFights(event.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
const SizedBox(width: 16),
|
||||||
|
Checkbox(
|
||||||
|
value: currentCheckBoxState,
|
||||||
|
onChanged: (v) {
|
||||||
|
if (v == null) {
|
||||||
|
selected.value = [];
|
||||||
|
} else if (v) {
|
||||||
|
selected.value = fights.value
|
||||||
|
.where((element) => element.start.isAfter(DateTime.now()))
|
||||||
|
.map((e) => e.id)
|
||||||
|
.toList();
|
||||||
|
if (selected.value.isEmpty) {
|
||||||
|
selected.value = fights.value.map((e) => e.id).toList();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
selected.value = fights.value.map((e) => e.id).toList();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tristate: true,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
ButtonBar(
|
||||||
|
children: [
|
||||||
|
Tooltip(
|
||||||
|
message: "Reschedule Fights",
|
||||||
|
child: IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
if (selected.value.isEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return HookBuilder(builder: (context) {
|
||||||
|
final lowest = useMemoized<DateTime>(
|
||||||
|
() => DateTime.fromMillisecondsSinceEpoch(
|
||||||
|
fights.value
|
||||||
|
.where((element) => selected.value
|
||||||
|
.contains(element.id))
|
||||||
|
.map((e) =>
|
||||||
|
e.start.millisecondsSinceEpoch)
|
||||||
|
.reduce(min)),
|
||||||
|
[fights.value]);
|
||||||
|
final date = useState<DateTime>(lowest);
|
||||||
|
|
||||||
|
final offset = useMemoized(
|
||||||
|
() => date.value.difference(lowest),
|
||||||
|
[date.value, lowest]);
|
||||||
|
|
||||||
|
return AlertDialog(
|
||||||
|
title: const Text("Reschedule Fights"),
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
DateTimeEditor(
|
||||||
|
mainAxisAlignment:
|
||||||
|
MainAxisAlignment.center,
|
||||||
|
(p0) {
|
||||||
|
date.value = p0;
|
||||||
|
},
|
||||||
|
date.value,
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
"${offset.isNegative ? "-" : ""}${offset.inHours.abs().toString().padLeft(2, "0")}:${offset.inMinutes.remainder(60).abs().toInt().toString().padLeft(2, "0")}:${offset.inSeconds.remainder(60).abs().toInt().toString().padLeft(2, "0")}"),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
child: const Text("Cancel"),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () async {
|
||||||
|
final selectedFights = fights.value
|
||||||
|
.where((element) => selected.value
|
||||||
|
.contains(element.id))
|
||||||
|
.toList();
|
||||||
|
final repo = await ref
|
||||||
|
.read(eventRepositoryProvider.future);
|
||||||
|
for (final fight in selectedFights) {
|
||||||
|
await repo.updateFight(
|
||||||
|
fight.id,
|
||||||
|
fight.start.add(offset),
|
||||||
|
fight.gameMode,
|
||||||
|
fight.map,
|
||||||
|
fight.kampfleiter.id,
|
||||||
|
fight.blueTeam,
|
||||||
|
fight.redTeam,
|
||||||
|
fight.group);
|
||||||
|
}
|
||||||
|
update();
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
child: const Text("Reshedule"),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.event_note),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Tooltip(
|
||||||
|
message: "Change Kampfleiter",
|
||||||
|
child: IconButton(
|
||||||
|
onPressed: () async {
|
||||||
|
if (selected.value.isEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final kampfleiter = await showSearch(
|
||||||
|
context: context,
|
||||||
|
delegate: UserSearchDelegate(
|
||||||
|
await ref.read(usersProvider.future)));
|
||||||
|
|
||||||
|
if (kampfleiter == null) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
final repo =
|
||||||
|
await ref.read(eventRepositoryProvider.future);
|
||||||
|
for (final fight in fights.value) {
|
||||||
|
if (selected.value.contains(fight.id)) {
|
||||||
|
await repo.updateFight(
|
||||||
|
fight.id,
|
||||||
|
fight.start,
|
||||||
|
fight.gameMode,
|
||||||
|
fight.map,
|
||||||
|
kampfleiter.id,
|
||||||
|
fight.blueTeam,
|
||||||
|
fight.redTeam,
|
||||||
|
fight.group);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.person)),
|
||||||
|
),
|
||||||
|
Tooltip(
|
||||||
|
message: "Change Group",
|
||||||
|
child: IconButton(
|
||||||
|
onPressed: () async {
|
||||||
|
if (selected.value.isEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final group = await showSearch(
|
||||||
|
context: context,
|
||||||
|
delegate: GroupSearchDelegate(
|
||||||
|
await ref.read(groupsProvider.future)));
|
||||||
|
|
||||||
|
if (group == null) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
final repo =
|
||||||
|
await ref.read(eventRepositoryProvider.future);
|
||||||
|
for (final fight in fights.value) {
|
||||||
|
if (selected.value.contains(fight.id)) {
|
||||||
|
await repo.updateFight(
|
||||||
|
fight.id,
|
||||||
|
fight.start,
|
||||||
|
fight.gameMode,
|
||||||
|
fight.map,
|
||||||
|
fight.kampfleiter.id,
|
||||||
|
fight.blueTeam,
|
||||||
|
fight.redTeam,
|
||||||
|
group.isEmpty ? null : group);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.group)),
|
||||||
|
),
|
||||||
|
Tooltip(
|
||||||
|
message: "Delete Fights",
|
||||||
|
child: IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
if (selected.value.isEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: const Text("Delete Fights"),
|
||||||
|
content: const Text(
|
||||||
|
"Do you really want to delete the selected fights?"),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
child: const Text("Cancel"),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () async {
|
||||||
|
final repo = await ref
|
||||||
|
.read(eventRepositoryProvider.future);
|
||||||
|
for (final fight in selected.value) {
|
||||||
|
await repo.deleteFight(fight);
|
||||||
|
}
|
||||||
|
update();
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
child: const Text("Delete",
|
||||||
|
style: TextStyle(color: Colors.red)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.delete, color: Colors.red),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
FloatingActionButton.extended(
|
||||||
|
onPressed: () {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return AddFightDialog(
|
||||||
|
eventData,
|
||||||
|
() => update(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
label: const Text("Add Fight"),
|
||||||
|
icon: const Icon(Icons.add),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
PopupMenuButton(
|
||||||
|
itemBuilder: (context) {
|
||||||
|
return const [
|
||||||
|
PopupMenuItem(
|
||||||
|
value: "export",
|
||||||
|
child: Text("Export Data"),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
},
|
||||||
|
onSelected: (value) {
|
||||||
|
if (value == "export") {
|
||||||
|
openExportedFights(fights.value, event.name);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
if (fights.value.isEmpty)
|
||||||
|
const Center(
|
||||||
|
child: Text("No fights yet"),
|
||||||
|
),
|
||||||
|
GroupedListView(
|
||||||
|
elements: fights.value,
|
||||||
|
groupBy: (fight) => fight.group ?? "Ungrouped",
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemComparator: (fight1, fight2) =>
|
||||||
|
fight1.start.compareTo(fight2.start),
|
||||||
|
groupComparator: (group1, group2) => group1.compareTo(group2),
|
||||||
|
floatingHeader: true,
|
||||||
|
groupSeparatorBuilder: (group) => InkWell(
|
||||||
|
onTap: () {
|
||||||
|
final g = group == "Ungrouped" ? null : group;
|
||||||
|
final isAllSelected = selected.value
|
||||||
|
.where((element) =>
|
||||||
|
fights.value
|
||||||
|
.firstWhere((element2) => element2.id == element)
|
||||||
|
.group ==
|
||||||
|
g)
|
||||||
|
.length ==
|
||||||
|
fights.value.where((element) => element.group == g).length;
|
||||||
|
if (isAllSelected) {
|
||||||
|
selected.value = selected.value
|
||||||
|
.where((element) =>
|
||||||
|
fights.value
|
||||||
|
.firstWhere((element2) => element2.id == element)
|
||||||
|
.group !=
|
||||||
|
g)
|
||||||
|
.toList();
|
||||||
|
} else {
|
||||||
|
selected.value = [
|
||||||
|
...selected.value,
|
||||||
|
...fights.value
|
||||||
|
.where((element) => element.group == g)
|
||||||
|
.map((e) => e.id)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Text(
|
||||||
|
group,
|
||||||
|
style: Theme.of(context).textTheme.headline6,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
itemBuilder: (context, fight) => ListTile(
|
||||||
|
title: Text(
|
||||||
|
"${fight.blueTeam.name} vs ${fight.redTeam.name}",
|
||||||
|
style: TextStyle(
|
||||||
|
color: fight
|
||||||
|
.getTeamWithContextColor(
|
||||||
|
Theme.of(context).scaffoldBackgroundColor)
|
||||||
|
.color
|
||||||
|
.computeLuminance() >
|
||||||
|
0.5
|
||||||
|
? Colors.black
|
||||||
|
: Colors.white),
|
||||||
|
),
|
||||||
|
subtitle: Text(
|
||||||
|
fight.score == 0
|
||||||
|
? kDateFormat.format(fight.start)
|
||||||
|
: "Winner: ${fight.winner.name}",
|
||||||
|
style: TextStyle(
|
||||||
|
color: fight
|
||||||
|
.getTeamWithContextColor(
|
||||||
|
Theme.of(context).scaffoldBackgroundColor)
|
||||||
|
.color
|
||||||
|
.computeLuminance() >
|
||||||
|
0.5
|
||||||
|
? Colors.black
|
||||||
|
: Colors.white)),
|
||||||
|
onTap: () {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return EditEventFightDialog(
|
||||||
|
fight: fight,
|
||||||
|
fightsRefresher: () => update(),
|
||||||
|
event: eventData,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
tileColor:
|
||||||
|
fight.score != 0 ? fight.winner.color.withOpacity(0.5) : null,
|
||||||
|
leading: Checkbox(
|
||||||
|
value: selected.value.contains(fight.id),
|
||||||
|
onChanged: (value) {
|
||||||
|
if (value ?? false) {
|
||||||
|
selected.value = [...selected.value, fight.id];
|
||||||
|
} else {
|
||||||
|
selected.value = selected.value
|
||||||
|
.where((element) => element != fight.id)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
283
lib/src/screens/event/components/loaded_event.dart
Normale Datei
283
lib/src/screens/event/components/loaded_event.dart
Normale Datei
@ -0,0 +1,283 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:steamwar_multitool/src/components/date_time_editor.dart';
|
||||||
|
import 'package:steamwar_multitool/src/provider/provider.dart';
|
||||||
|
import 'package:steamwar_multitool/src/screens/event/components/fight_list.dart';
|
||||||
|
import 'package:steamwar_multitool/src/screens/event/event.dart';
|
||||||
|
import 'package:steamwar_multitool/src/delegates/delegates.dart';
|
||||||
|
import 'package:steamwar_multitool/src/types/types.dart';
|
||||||
|
|
||||||
|
class LoadedEventScreen extends HookConsumerWidget {
|
||||||
|
const LoadedEventScreen({Key? key, required this.eventData})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
final EventExtended eventData;
|
||||||
|
|
||||||
|
@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);
|
||||||
|
final endDateState = useState(event.end);
|
||||||
|
final maxTeamMembersState = useState(event.maxTeamMembers);
|
||||||
|
final maxTeamMembersController =
|
||||||
|
useTextEditingController(text: event.maxTeamMembers.toString());
|
||||||
|
final invalidMaxTeamMembers = useState(false);
|
||||||
|
final schematicTypeState = useState<SchematicType?>(null);
|
||||||
|
final publicOnlyState = useState(event.publicSchemsOnly);
|
||||||
|
final spectateSystemState = useState(event.spectateSystem);
|
||||||
|
|
||||||
|
useMemoized(() {
|
||||||
|
ref.read(schematicTypesProvider.future).then((value) {
|
||||||
|
schematicTypeState.value = catchToNull(
|
||||||
|
() => value.firstWhere((element) => element.db == event.schemType));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
final changed = useState(false);
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text("${"Edit"} ${event.name}"),
|
||||||
|
actions: [
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => AlertDialog(
|
||||||
|
title: const Text("Delete Event"),
|
||||||
|
content: const Text(
|
||||||
|
"Are you sure you want to delete this event?"),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
child: const Text("Cancel")),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
ref.read(eventRepositoryProvider.future).then(
|
||||||
|
(value) => value.deleteEvent(event.id));
|
||||||
|
ref.invalidate(eventsListProvider);
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
child: const Text(
|
||||||
|
"Delete",
|
||||||
|
style: TextStyle(color: Colors.red),
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.delete_outline, color: Colors.red),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: changed.value
|
||||||
|
? () {
|
||||||
|
ref
|
||||||
|
.read(eventRepositoryProvider.future)
|
||||||
|
.then((value) => value.updateEvent(
|
||||||
|
event.id,
|
||||||
|
nameController.text,
|
||||||
|
deadlineState.value,
|
||||||
|
startDateState.value,
|
||||||
|
endDateState.value,
|
||||||
|
maxTeamMembersState.value,
|
||||||
|
schematicTypeState.value?.db,
|
||||||
|
publicOnlyState.value,
|
||||||
|
spectateSystemState.value,
|
||||||
|
))
|
||||||
|
.whenComplete(
|
||||||
|
() => ref.invalidate(eventsListProvider));
|
||||||
|
context.go('/');
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
icon: const Icon(Icons.save)),
|
||||||
|
],
|
||||||
|
leading: IconButton(
|
||||||
|
onPressed: () async {
|
||||||
|
var canPop = false;
|
||||||
|
if (changed.value) {
|
||||||
|
final accepted = await showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => AlertDialog(
|
||||||
|
title: const Text("Unsaved changes"),
|
||||||
|
content: const Text(
|
||||||
|
"You have unsaved changes. Do you want to discard them?"),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(false),
|
||||||
|
child: const Text("No")),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(true),
|
||||||
|
child: const Text("Yes",
|
||||||
|
style: TextStyle(color: Colors.red))),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
if (accepted != null && accepted) {
|
||||||
|
canPop = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
canPop = true;
|
||||||
|
}
|
||||||
|
if (canPop) context.go("/");
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.arrow_back),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
body: Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: ListView(
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
TextField(
|
||||||
|
controller: nameController,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
labelText: "Name",
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
),
|
||||||
|
onChanged: (value) {
|
||||||
|
changed.value = true;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
const Text("Deadline: "),
|
||||||
|
DateTimeEditor((p0) {
|
||||||
|
deadlineState.value = p0;
|
||||||
|
changed.value = true;
|
||||||
|
}, deadlineState.value, true),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
const Text("Start: "),
|
||||||
|
DateTimeEditor((p0) {
|
||||||
|
startDateState.value = p0;
|
||||||
|
changed.value = true;
|
||||||
|
}, startDateState.value, true),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
const Text("End: "),
|
||||||
|
DateTimeEditor((p0) {
|
||||||
|
endDateState.value = p0;
|
||||||
|
changed.value = true;
|
||||||
|
}, endDateState.value, true),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
TextField(
|
||||||
|
controller: maxTeamMembersController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: "Max Team Members",
|
||||||
|
border: const OutlineInputBorder(),
|
||||||
|
errorText: invalidMaxTeamMembers.value
|
||||||
|
? "Must be a number above 0"
|
||||||
|
: null),
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
onChanged: (value) {
|
||||||
|
if (value.isEmpty ||
|
||||||
|
int.tryParse(value) == null ||
|
||||||
|
int.parse(value) <= 0) {
|
||||||
|
invalidMaxTeamMembers.value = true;
|
||||||
|
} else {
|
||||||
|
invalidMaxTeamMembers.value = false;
|
||||||
|
maxTeamMembersState.value = int.parse(value);
|
||||||
|
changed.value = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Slider(
|
||||||
|
value: maxTeamMembersState.value.toDouble(),
|
||||||
|
onChanged: (p0) {
|
||||||
|
maxTeamMembersState.value = p0.toInt();
|
||||||
|
maxTeamMembersController.text = p0.toInt().toString();
|
||||||
|
changed.value = true;
|
||||||
|
},
|
||||||
|
min: min(1, maxTeamMembersState.value.toDouble()),
|
||||||
|
max: max(30, maxTeamMembersState.value.toDouble()),
|
||||||
|
divisions:
|
||||||
|
max(30, maxTeamMembersState.value.toDouble()).toInt() - 1,
|
||||||
|
label: maxTeamMembersState.value.toString(),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
const Text("Schematic Type: "),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () async {
|
||||||
|
final types =
|
||||||
|
await ref.read(schematicTypesProvider.future);
|
||||||
|
final out = await showSearch(
|
||||||
|
context: context,
|
||||||
|
delegate: SchematicTypeSearchDelegate(types));
|
||||||
|
if (out == RESET_TYPE) {
|
||||||
|
schematicTypeState.value = null;
|
||||||
|
changed.value = true;
|
||||||
|
} else {
|
||||||
|
schematicTypeState.value =
|
||||||
|
out ?? schematicTypeState.value;
|
||||||
|
changed.value = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Text(schematicTypeState.value?.name ?? "Select")),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
CheckboxListTile(
|
||||||
|
value: publicOnlyState.value,
|
||||||
|
onChanged: (value) {
|
||||||
|
publicOnlyState.value = value ?? publicOnlyState.value;
|
||||||
|
changed.value = true;
|
||||||
|
},
|
||||||
|
title: const Text("Public Only"),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
CheckboxListTile(
|
||||||
|
value: spectateSystemState.value,
|
||||||
|
onChanged: (value) {
|
||||||
|
spectateSystemState.value = value ?? spectateSystemState.value;
|
||||||
|
changed.value = true;
|
||||||
|
},
|
||||||
|
title: const Text("Use Spectate System"),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text("Teams (${eventData.teams.length})",
|
||||||
|
style: Theme.of(context).textTheme.headline6),
|
||||||
|
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(
|
||||||
|
eventData: eventData,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
33
lib/src/screens/event/components/team_selector.dart
Normale Datei
33
lib/src/screens/event/components/team_selector.dart
Normale Datei
@ -0,0 +1,33 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:steamwar_multitool/src/delegates/delegates.dart';
|
||||||
|
import 'package:steamwar_multitool/src/types/types.dart';
|
||||||
|
|
||||||
|
class TeamSelector extends HookConsumerWidget {
|
||||||
|
final Event event;
|
||||||
|
final Team? selectedTeam;
|
||||||
|
final void Function(Team) onSelected;
|
||||||
|
final List<Team> teams;
|
||||||
|
|
||||||
|
const TeamSelector(
|
||||||
|
this.event,
|
||||||
|
this.selectedTeam,
|
||||||
|
this.onSelected,
|
||||||
|
this.teams, {
|
||||||
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
return ElevatedButton(
|
||||||
|
onPressed: () async {
|
||||||
|
final team = await showSearch(
|
||||||
|
context: context, delegate: TeamSearchDelegate(teams));
|
||||||
|
if (team != null) {
|
||||||
|
onSelected(team);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Text(selectedTeam?.name ?? "Select Team"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
57
lib/src/screens/event/event.dart
Normale Datei
57
lib/src/screens/event/event.dart
Normale Datei
@ -0,0 +1,57 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:steamwar_multitool/src/provider/events.dart';
|
||||||
|
import 'package:steamwar_multitool/src/screens/event/components/loaded_event.dart';
|
||||||
|
import 'package:steamwar_multitool/src/types/types.dart';
|
||||||
|
|
||||||
|
T? catchToNull<T>(T Function() f) {
|
||||||
|
try {
|
||||||
|
return f();
|
||||||
|
} catch (e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class EditEventScreen extends HookConsumerWidget {
|
||||||
|
final int eventId;
|
||||||
|
const EditEventScreen(this.eventId, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final eventFuture = useMemoized(() => ref
|
||||||
|
.watch(eventRepositoryProvider.future)
|
||||||
|
.then((value) => value.getEvent(eventId)));
|
||||||
|
|
||||||
|
return FutureBuilder(
|
||||||
|
future: eventFuture,
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.hasError) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text('Error'),
|
||||||
|
),
|
||||||
|
body: Center(
|
||||||
|
child: Text('Error: ${snapshot.error}'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!snapshot.hasData) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text('Loading'),
|
||||||
|
),
|
||||||
|
body: const Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
final eventData = snapshot.data as EventExtended;
|
||||||
|
|
||||||
|
return LoadedEventScreen(
|
||||||
|
eventData: eventData,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:steamwar_multitool/src/provider/events.dart';
|
import 'package:steamwar_multitool/src/provider/events.dart';
|
||||||
|
import 'package:steamwar_multitool/src/types/types.dart';
|
||||||
|
|
||||||
class EventfightsGraph extends HookConsumerWidget {
|
class EventfightsGraph extends HookConsumerWidget {
|
||||||
final int eventId;
|
final int eventId;
|
@ -6,20 +6,14 @@ import 'package:go_router/go_router.dart';
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:steamwar_multitool/src/provider/mods.dart';
|
import 'package:steamwar_multitool/src/provider/mods.dart';
|
||||||
|
import 'package:steamwar_multitool/src/provider/provider.dart';
|
||||||
import '../provider/events.dart';
|
import 'package:steamwar_multitool/src/screens/home/lists/events_list.dart';
|
||||||
import '../provider/server.dart';
|
|
||||||
import '../provider/user.dart';
|
|
||||||
import 'components/events_list.dart';
|
|
||||||
import 'components/mod_list.dart';
|
|
||||||
|
|
||||||
class HomeScreen extends HookConsumerWidget {
|
class HomeScreen extends HookConsumerWidget {
|
||||||
const HomeScreen({Key? key}) : super(key: key);
|
const HomeScreen({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final servers = ref.watch(serversProvider);
|
|
||||||
|
|
||||||
final userData = ref.watch(userDataProvider);
|
final userData = ref.watch(userDataProvider);
|
||||||
|
|
||||||
if (userData.hasError) {
|
if (userData.hasError) {
|
||||||
@ -44,9 +38,7 @@ class HomeScreen extends HookConsumerWidget {
|
|||||||
automaticallyImplyLeading: false,
|
automaticallyImplyLeading: false,
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: servers.isLoading
|
onPressed: () {
|
||||||
? null
|
|
||||||
: () {
|
|
||||||
ref.invalidate(eventsListProvider);
|
ref.invalidate(eventsListProvider);
|
||||||
ref.invalidate(uncheckedMods);
|
ref.invalidate(uncheckedMods);
|
||||||
},
|
},
|
@ -1,11 +1,11 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:steamwar_multitool/main.dart';
|
import 'package:steamwar_multitool/src/components/components.dart';
|
||||||
|
import 'package:steamwar_multitool/src/dialogs/dialogs.dart';
|
||||||
|
import 'package:steamwar_multitool/src/util/constants.dart';
|
||||||
|
|
||||||
import '../../provider/events.dart';
|
import '../../../provider/events.dart';
|
||||||
import 'error.dart';
|
|
||||||
import 'event_dialog.dart';
|
|
||||||
|
|
||||||
class EventListComponent extends HookConsumerWidget {
|
class EventListComponent extends HookConsumerWidget {
|
||||||
const EventListComponent({Key? key}) : super(key: key);
|
const EventListComponent({Key? key}) : super(key: key);
|
||||||
@ -23,7 +23,8 @@ class EventListComponent extends HookConsumerWidget {
|
|||||||
FloatingActionButton.extended(
|
FloatingActionButton.extended(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context, builder: (context) => const EventDialog());
|
context: context,
|
||||||
|
builder: (context) => const CreateEventDialog());
|
||||||
},
|
},
|
||||||
label: const Text("Create Event"),
|
label: const Text("Create Event"),
|
||||||
icon: const Icon(Icons.add),
|
icon: const Icon(Icons.add),
|
@ -1,9 +1,8 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:steamwar_multitool/src/components/error.dart';
|
||||||
import 'package:steamwar_multitool/src/provider/mods.dart';
|
import 'package:steamwar_multitool/src/provider/mods.dart';
|
||||||
|
|
||||||
import 'error.dart';
|
|
||||||
|
|
||||||
class ModListComponent extends HookConsumerWidget {
|
class ModListComponent extends HookConsumerWidget {
|
||||||
const ModListComponent({Key? key}) : super(key: key);
|
const ModListComponent({Key? key}) : super(key: key);
|
||||||
|
|
@ -7,8 +7,7 @@ import 'package:go_router/go_router.dart';
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:steamwar_multitool/src/provider/http.dart';
|
import 'package:steamwar_multitool/src/provider/http.dart';
|
||||||
|
import 'package:steamwar_multitool/src/provider/provider.dart';
|
||||||
import '../provider/user.dart';
|
|
||||||
|
|
||||||
class LoginScreenWidget extends HookConsumerWidget {
|
class LoginScreenWidget extends HookConsumerWidget {
|
||||||
const LoginScreenWidget({
|
const LoginScreenWidget({
|
@ -1,385 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
import '../provider/events.dart';
|
|
||||||
import '../provider/types.dart';
|
|
||||||
|
|
||||||
final RESET_TYPE = SchematicType("RESET", "RESET");
|
|
||||||
|
|
||||||
class SchematicTypeSearchDelegate extends SearchDelegate<SchematicType?> {
|
|
||||||
final List<SchematicType> types;
|
|
||||||
|
|
||||||
SchematicTypeSearchDelegate(this.types);
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Widget>? buildActions(BuildContext context) {
|
|
||||||
return [
|
|
||||||
IconButton(
|
|
||||||
icon: const Icon(Icons.clear),
|
|
||||||
onPressed: () {
|
|
||||||
query = "";
|
|
||||||
},
|
|
||||||
),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget? buildLeading(BuildContext context) {
|
|
||||||
return IconButton(
|
|
||||||
icon: const Icon(Icons.arrow_back),
|
|
||||||
onPressed: () {
|
|
||||||
close(context, null);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget buildResults(BuildContext context) {
|
|
||||||
return _buildList(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget buildSuggestions(BuildContext context) {
|
|
||||||
return _buildList(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildList(BuildContext context) {
|
|
||||||
final out = types
|
|
||||||
.where((element) => element.name.contains(query.toLowerCase()))
|
|
||||||
.toList();
|
|
||||||
return ListView.builder(
|
|
||||||
itemCount: out.length + 1,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
if (index == 0) {
|
|
||||||
return ListTile(
|
|
||||||
title: const Text("Reset"),
|
|
||||||
leading: const Icon(Icons.clear),
|
|
||||||
onTap: () {
|
|
||||||
close(context, RESET_TYPE);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final type = out[index - 1];
|
|
||||||
|
|
||||||
return ListTile(
|
|
||||||
title: Text(type.name),
|
|
||||||
onTap: () {
|
|
||||||
close(context, type);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class GamemodeSearchDelegate extends SearchDelegate<String?> {
|
|
||||||
final List<String> modes;
|
|
||||||
|
|
||||||
GamemodeSearchDelegate(this.modes);
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Widget>? buildActions(BuildContext context) {
|
|
||||||
return [
|
|
||||||
IconButton(
|
|
||||||
icon: const Icon(Icons.clear),
|
|
||||||
onPressed: () {
|
|
||||||
query = "";
|
|
||||||
},
|
|
||||||
),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget? buildLeading(BuildContext context) {
|
|
||||||
return IconButton(
|
|
||||||
icon: const Icon(Icons.arrow_back),
|
|
||||||
onPressed: () {
|
|
||||||
close(context, null);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget buildResults(BuildContext context) {
|
|
||||||
return _buildList(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget buildSuggestions(BuildContext context) {
|
|
||||||
return _buildList(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildList(BuildContext context) {
|
|
||||||
final out = modes
|
|
||||||
.where((element) => element.toLowerCase().contains(query.toLowerCase()))
|
|
||||||
.toList();
|
|
||||||
return ListView.builder(
|
|
||||||
itemCount: out.length,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
final type = out[index];
|
|
||||||
|
|
||||||
return ListTile(
|
|
||||||
title: Text(type),
|
|
||||||
onTap: () {
|
|
||||||
close(context, type);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class MapSearchDelegate extends SearchDelegate<String?> {
|
|
||||||
final List<String> maps;
|
|
||||||
|
|
||||||
MapSearchDelegate(this.maps);
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Widget>? buildActions(BuildContext context) {
|
|
||||||
return [
|
|
||||||
IconButton(
|
|
||||||
icon: const Icon(Icons.clear),
|
|
||||||
onPressed: () {
|
|
||||||
query = "";
|
|
||||||
},
|
|
||||||
),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget? buildLeading(BuildContext context) {
|
|
||||||
return IconButton(
|
|
||||||
icon: const Icon(Icons.arrow_back),
|
|
||||||
onPressed: () {
|
|
||||||
close(context, null);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget buildResults(BuildContext context) {
|
|
||||||
return _buildList(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget buildSuggestions(BuildContext context) {
|
|
||||||
return _buildList(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildList(BuildContext context) {
|
|
||||||
final out = maps
|
|
||||||
.where((element) => element.toLowerCase().contains(query.toLowerCase()))
|
|
||||||
.toList();
|
|
||||||
return ListView.builder(
|
|
||||||
itemCount: out.length,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
final type = out[index];
|
|
||||||
|
|
||||||
return ListTile(
|
|
||||||
title: Text(type),
|
|
||||||
onTap: () {
|
|
||||||
close(context, type);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class UserSearchDelegate extends SearchDelegate<User?> {
|
|
||||||
final List<User> users;
|
|
||||||
|
|
||||||
UserSearchDelegate(this.users);
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Widget>? buildActions(BuildContext context) {
|
|
||||||
return [
|
|
||||||
IconButton(
|
|
||||||
icon: const Icon(Icons.clear),
|
|
||||||
onPressed: () {
|
|
||||||
query = "";
|
|
||||||
},
|
|
||||||
)
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget? buildLeading(BuildContext context) {
|
|
||||||
return IconButton(
|
|
||||||
icon: const Icon(Icons.arrow_back),
|
|
||||||
onPressed: () {
|
|
||||||
close(context, null);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget buildResults(BuildContext context) {
|
|
||||||
return ListView(
|
|
||||||
children: [
|
|
||||||
for (final team in users)
|
|
||||||
if (team.name.toLowerCase().contains(query.toLowerCase()))
|
|
||||||
ListTile(
|
|
||||||
title: Text(team.name),
|
|
||||||
onTap: () {
|
|
||||||
close(context, team);
|
|
||||||
},
|
|
||||||
)
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget buildSuggestions(BuildContext context) {
|
|
||||||
return ListView(
|
|
||||||
children: [
|
|
||||||
for (final team in users)
|
|
||||||
if (team.name.toLowerCase().contains(query.toLowerCase()))
|
|
||||||
ListTile(
|
|
||||||
title: Text(team.name),
|
|
||||||
onTap: () {
|
|
||||||
close(context, team);
|
|
||||||
},
|
|
||||||
)
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class TeamSearchDelegate extends SearchDelegate<Team?> {
|
|
||||||
final List<Team> teams;
|
|
||||||
|
|
||||||
TeamSearchDelegate(this.teams);
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Widget>? buildActions(BuildContext context) {
|
|
||||||
return [
|
|
||||||
IconButton(
|
|
||||||
icon: const Icon(Icons.clear),
|
|
||||||
onPressed: () {
|
|
||||||
query = "";
|
|
||||||
},
|
|
||||||
)
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget? buildLeading(BuildContext context) {
|
|
||||||
return IconButton(
|
|
||||||
icon: const Icon(Icons.arrow_back),
|
|
||||||
onPressed: () {
|
|
||||||
close(context, null);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget buildResults(BuildContext context) {
|
|
||||||
return ListView(
|
|
||||||
children: [
|
|
||||||
ListTile(
|
|
||||||
title: const Text("?"),
|
|
||||||
onTap: () {
|
|
||||||
close(context, Team(-1, "?", "?", "8"));
|
|
||||||
},
|
|
||||||
),
|
|
||||||
for (final team in teams)
|
|
||||||
if (team.name.toLowerCase().contains(query.toLowerCase()))
|
|
||||||
ListTile(
|
|
||||||
title: Text(team.name),
|
|
||||||
onTap: () {
|
|
||||||
close(context, team);
|
|
||||||
},
|
|
||||||
)
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget buildSuggestions(BuildContext context) {
|
|
||||||
return ListView(
|
|
||||||
children: [
|
|
||||||
ListTile(
|
|
||||||
title: const Text("?"),
|
|
||||||
onTap: () {
|
|
||||||
close(context, Team(-1, "?", "?", "8"));
|
|
||||||
},
|
|
||||||
),
|
|
||||||
for (final team in teams)
|
|
||||||
if (team.name.toLowerCase().contains(query.toLowerCase()))
|
|
||||||
ListTile(
|
|
||||||
title: Text(team.name),
|
|
||||||
onTap: () {
|
|
||||||
close(context, team);
|
|
||||||
},
|
|
||||||
)
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class GroupSearchDelegate extends SearchDelegate<String?> {
|
|
||||||
final List<String> groups;
|
|
||||||
|
|
||||||
GroupSearchDelegate(this.groups);
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Widget>? buildActions(BuildContext context) {
|
|
||||||
return [
|
|
||||||
IconButton(
|
|
||||||
icon: const Icon(Icons.clear),
|
|
||||||
onPressed: () {
|
|
||||||
query = "";
|
|
||||||
},
|
|
||||||
)
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget? buildLeading(BuildContext context) {
|
|
||||||
return IconButton(
|
|
||||||
icon: const Icon(Icons.arrow_back),
|
|
||||||
onPressed: () {
|
|
||||||
close(context, null);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget buildResults(BuildContext context) {
|
|
||||||
return buildSuggestions(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget buildSuggestions(BuildContext context) {
|
|
||||||
return ListView(
|
|
||||||
children: [
|
|
||||||
ListTile(
|
|
||||||
title: Text(query),
|
|
||||||
onTap: query.isEmpty
|
|
||||||
? null
|
|
||||||
: () {
|
|
||||||
close(context, query);
|
|
||||||
},
|
|
||||||
subtitle: const Text("Create new group"),
|
|
||||||
leading: const Icon(Icons.add),
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
leading: const Icon(Icons.clear),
|
|
||||||
title: const Text("Reset"),
|
|
||||||
onTap: () {
|
|
||||||
close(context, "");
|
|
||||||
},
|
|
||||||
),
|
|
||||||
for (final group in groups)
|
|
||||||
if (group.toLowerCase().contains(query.toLowerCase()))
|
|
||||||
ListTile(
|
|
||||||
title: Text(group),
|
|
||||||
onTap: () {
|
|
||||||
close(context, group);
|
|
||||||
},
|
|
||||||
)
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,9 +3,8 @@ import 'package:flutter_hooks/flutter_hooks.dart';
|
|||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
import 'package:steamwar_multitool/src/components/error.dart';
|
||||||
import '../provider/user.dart';
|
import 'package:steamwar_multitool/src/provider/provider.dart';
|
||||||
import 'components/error.dart';
|
|
||||||
|
|
||||||
class SettingsScreen extends HookConsumerWidget {
|
class SettingsScreen extends HookConsumerWidget {
|
||||||
const SettingsScreen({Key? key}) : super(key: key);
|
const SettingsScreen({Key? key}) : super(key: key);
|
182
lib/src/types/event.dart
Normale Datei
182
lib/src/types/event.dart
Normale Datei
@ -0,0 +1,182 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:steamwar_multitool/src/types/types.dart';
|
||||||
|
|
||||||
|
class EventFight {
|
||||||
|
final int id;
|
||||||
|
final String gameMode;
|
||||||
|
final String map;
|
||||||
|
final DateTime start;
|
||||||
|
final Team redTeam;
|
||||||
|
final Team blueTeam;
|
||||||
|
final User kampfleiter;
|
||||||
|
final int score;
|
||||||
|
final String? group;
|
||||||
|
|
||||||
|
EventFight(this.id, this.gameMode, this.map, this.start, this.redTeam,
|
||||||
|
this.blueTeam, this.kampfleiter, this.score, this.group);
|
||||||
|
|
||||||
|
Team get winner {
|
||||||
|
return getTeamWithContextColor(Colors.white);
|
||||||
|
}
|
||||||
|
|
||||||
|
Team getTeamWithContextColor(Color color) {
|
||||||
|
switch (score) {
|
||||||
|
case 1:
|
||||||
|
return blueTeam;
|
||||||
|
case 2:
|
||||||
|
return redTeam;
|
||||||
|
case 3:
|
||||||
|
return Team(
|
||||||
|
-1, "Tie", "TIE", color.computeLuminance() > 0.5 ? "f" : "0");
|
||||||
|
default:
|
||||||
|
return Team(
|
||||||
|
-1, "Unknown", "UNK", color.computeLuminance() > 0.5 ? "f" : "0");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
factory EventFight.fromJson(Map<String, dynamic> json) {
|
||||||
|
return EventFight(
|
||||||
|
json["id"],
|
||||||
|
json["spielmodus"],
|
||||||
|
json["map"],
|
||||||
|
DateTime.fromMillisecondsSinceEpoch(json["start"]),
|
||||||
|
Team.fromJson(json["redTeam"]),
|
||||||
|
Team.fromJson(json["blueTeam"]),
|
||||||
|
User.fromJson(json["kampfleiter"]),
|
||||||
|
json["ergebnis"],
|
||||||
|
json["group"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Team {
|
||||||
|
final int id;
|
||||||
|
final String name;
|
||||||
|
final String kuerzel;
|
||||||
|
final String colorCode;
|
||||||
|
|
||||||
|
Team(this.id, this.name, this.kuerzel, this.colorCode);
|
||||||
|
|
||||||
|
Color get color {
|
||||||
|
switch (colorCode) {
|
||||||
|
case "1":
|
||||||
|
return const Color(0xFF0000AA);
|
||||||
|
case "2":
|
||||||
|
return const Color(0xFF00AA00);
|
||||||
|
case "3":
|
||||||
|
return const Color(0xFF00AAAA);
|
||||||
|
case "4":
|
||||||
|
return const Color(0xFFAA0000);
|
||||||
|
case "5":
|
||||||
|
return const Color(0xFFAA00AA);
|
||||||
|
case "6":
|
||||||
|
return const Color(0xFFFFAA00);
|
||||||
|
case "7":
|
||||||
|
return const Color(0xFFAAAAAA);
|
||||||
|
case "8":
|
||||||
|
return const Color(0xFF555555);
|
||||||
|
case "9":
|
||||||
|
return const Color(0xFF5555FF);
|
||||||
|
case "a":
|
||||||
|
return const Color(0xFF55FF55);
|
||||||
|
case "b":
|
||||||
|
return const Color(0xFF55FFFF);
|
||||||
|
case "c":
|
||||||
|
return const Color(0xFFFF5555);
|
||||||
|
case "d":
|
||||||
|
return const Color(0xFFFF55FF);
|
||||||
|
case "e":
|
||||||
|
return const Color(0xFFFFFF55);
|
||||||
|
case "f":
|
||||||
|
return const Color(0xFFFFFFFF);
|
||||||
|
default:
|
||||||
|
return const Color(0xFF000000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
factory Team.fromJson(Map<String, dynamic> 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<EventFight> fights;
|
||||||
|
final List<Team> teams;
|
||||||
|
|
||||||
|
EventExtended(this.event, this.fights, this.teams);
|
||||||
|
|
||||||
|
factory EventExtended.fromJson(Map<String, dynamic> json) {
|
||||||
|
return EventExtended(
|
||||||
|
Event.fromJson(json['event']),
|
||||||
|
(json['fights'] as List<dynamic>)
|
||||||
|
.map((e) => EventFight.fromJson(e))
|
||||||
|
.toList(),
|
||||||
|
(json['teams'] as List<dynamic>).map((e) => Team.fromJson(e)).toList(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Event {
|
||||||
|
final int id;
|
||||||
|
final String name;
|
||||||
|
final DateTime deadline;
|
||||||
|
final DateTime start;
|
||||||
|
final DateTime end;
|
||||||
|
final int maxTeamMembers;
|
||||||
|
final String? schemType;
|
||||||
|
final bool publicSchemsOnly;
|
||||||
|
final bool spectateSystem;
|
||||||
|
|
||||||
|
Event({
|
||||||
|
required this.id,
|
||||||
|
required this.name,
|
||||||
|
required this.deadline,
|
||||||
|
required this.start,
|
||||||
|
required this.end,
|
||||||
|
required this.maxTeamMembers,
|
||||||
|
required this.schemType,
|
||||||
|
required this.publicSchemsOnly,
|
||||||
|
required this.spectateSystem,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory Event.fromJson(Map<String, dynamic> 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'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ShortEvent {
|
||||||
|
final String name;
|
||||||
|
final int id;
|
||||||
|
final DateTime start;
|
||||||
|
final DateTime end;
|
||||||
|
|
||||||
|
ShortEvent(this.name, this.id, this.start, this.end);
|
||||||
|
|
||||||
|
get isUpcoming => start.isAfter(DateTime.now());
|
||||||
|
|
||||||
|
get isCurrent =>
|
||||||
|
start.isBefore(DateTime.now()) && end.isAfter(DateTime.now());
|
||||||
|
|
||||||
|
factory ShortEvent.fromJson(Map<String, dynamic> json) {
|
||||||
|
return ShortEvent(
|
||||||
|
json["name"],
|
||||||
|
json["id"],
|
||||||
|
DateTime.fromMillisecondsSinceEpoch(json["start"]),
|
||||||
|
DateTime.fromMillisecondsSinceEpoch(json["end"]));
|
||||||
|
}
|
||||||
|
}
|
59
lib/src/types/mods.dart
Normale Datei
59
lib/src/types/mods.dart
Normale Datei
@ -0,0 +1,59 @@
|
|||||||
|
class Mod {
|
||||||
|
final Platform platform;
|
||||||
|
final String name;
|
||||||
|
final ModType type;
|
||||||
|
|
||||||
|
Mod(this.platform, this.name, this.type);
|
||||||
|
|
||||||
|
factory Mod.fromJson(Map<String, dynamic> json) {
|
||||||
|
return Mod(
|
||||||
|
Platform.fromOrdinal(json["platform"] as int),
|
||||||
|
json["modName"],
|
||||||
|
ModType.fromOrdinal(json["modType"] as int),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Platform {
|
||||||
|
FORGE,
|
||||||
|
LABYMOD,
|
||||||
|
FABRIC;
|
||||||
|
|
||||||
|
static Platform fromOrdinal(int ordinal) {
|
||||||
|
switch (ordinal) {
|
||||||
|
case 0:
|
||||||
|
return FORGE;
|
||||||
|
case 1:
|
||||||
|
return LABYMOD;
|
||||||
|
case 2:
|
||||||
|
return FABRIC;
|
||||||
|
default:
|
||||||
|
throw Exception("Invalid ordinal");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ModType {
|
||||||
|
UNKLASSIFIED,
|
||||||
|
GREEN,
|
||||||
|
YELLOW,
|
||||||
|
RED,
|
||||||
|
YOUTUBER_ONLY;
|
||||||
|
|
||||||
|
static ModType fromOrdinal(int ordinal) {
|
||||||
|
switch (ordinal) {
|
||||||
|
case 0:
|
||||||
|
return UNKLASSIFIED;
|
||||||
|
case 1:
|
||||||
|
return GREEN;
|
||||||
|
case 2:
|
||||||
|
return YELLOW;
|
||||||
|
case 3:
|
||||||
|
return RED;
|
||||||
|
case 4:
|
||||||
|
return YOUTUBER_ONLY;
|
||||||
|
default:
|
||||||
|
throw Exception("Invalid ordinal");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
lib/src/types/schematic.dart
Normale Datei
10
lib/src/types/schematic.dart
Normale Datei
@ -0,0 +1,10 @@
|
|||||||
|
class SchematicType {
|
||||||
|
final String name;
|
||||||
|
final String db;
|
||||||
|
|
||||||
|
SchematicType(this.name, this.db);
|
||||||
|
|
||||||
|
factory SchematicType.fromJson(Map<String, dynamic> json) {
|
||||||
|
return SchematicType(json["name"], json["db"]);
|
||||||
|
}
|
||||||
|
}
|
5
lib/src/types/types.dart
Normale Datei
5
lib/src/types/types.dart
Normale Datei
@ -0,0 +1,5 @@
|
|||||||
|
export 'event.dart';
|
||||||
|
export 'user_data.dart';
|
||||||
|
export 'mods.dart';
|
||||||
|
export 'user.dart';
|
||||||
|
export 'schematic.dart';
|
10
lib/src/types/user.dart
Normale Datei
10
lib/src/types/user.dart
Normale Datei
@ -0,0 +1,10 @@
|
|||||||
|
class User {
|
||||||
|
final int id;
|
||||||
|
final String name;
|
||||||
|
|
||||||
|
User(this.id, this.name);
|
||||||
|
|
||||||
|
factory User.fromJson(Map<String, dynamic> json) {
|
||||||
|
return User(json["id"] as int, json["name"] as String);
|
||||||
|
}
|
||||||
|
}
|
9
lib/src/types/user_data.dart
Normale Datei
9
lib/src/types/user_data.dart
Normale Datei
@ -0,0 +1,9 @@
|
|||||||
|
class UserData {
|
||||||
|
final String key;
|
||||||
|
final bool uneditablePastEvents;
|
||||||
|
|
||||||
|
UserData({
|
||||||
|
required this.key,
|
||||||
|
required this.uneditablePastEvents,
|
||||||
|
});
|
||||||
|
}
|
3
lib/src/util/constants.dart
Normale Datei
3
lib/src/util/constants.dart
Normale Datei
@ -0,0 +1,3 @@
|
|||||||
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
|
final kDateFormat = DateFormat("dd.MM.yyyy HH:mm");
|
@ -2,7 +2,7 @@ import 'dart:convert';
|
|||||||
import 'dart:html';
|
import 'dart:html';
|
||||||
|
|
||||||
import 'package:csv/csv.dart';
|
import 'package:csv/csv.dart';
|
||||||
import 'package:steamwar_multitool/src/provider/events.dart';
|
import 'package:steamwar_multitool/src/types/types.dart';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Start,BlueTeam,RedTeam,WinnerTeam
|
Start,BlueTeam,RedTeam,WinnerTeam
|
||||||
|
In neuem Issue referenzieren
Einen Benutzer sperren