Fixes and new Router
@ -1,20 +1,39 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:steamwar_multitool/src/screens/event.dart';
|
||||
|
||||
import 'screens/home.dart';
|
||||
import 'screens/login.dart';
|
||||
import 'screens/userinfo.dart';
|
||||
|
||||
final _routes = GoRouter(
|
||||
routes: [
|
||||
GoRoute(path: "/", builder: (context, state) => const HomeScreen()),
|
||||
GoRoute(
|
||||
path: "/login", builder: (context, state) => const LoginScreenWidget()),
|
||||
GoRoute(
|
||||
path: "/settings", builder: (context, state) => const SettingsScreen()),
|
||||
GoRoute(
|
||||
path: "/event/:eventId",
|
||||
builder: (context, state) {
|
||||
final eventId = int.tryParse(state.params['eventId']!);
|
||||
if (eventId == null) {
|
||||
context.go("/");
|
||||
return const SizedBox();
|
||||
}
|
||||
return EditEventScreen(eventId);
|
||||
}),
|
||||
],
|
||||
initialLocation: "/login",
|
||||
);
|
||||
|
||||
class DevServerStarterApp extends StatelessWidget {
|
||||
const DevServerStarterApp({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
routes: {
|
||||
"/": (context) => const LoginScreenWidget(),
|
||||
"/home": (context) => const HomeScreen(),
|
||||
"/settings": (context) => const SettingsScreen(),
|
||||
},
|
||||
return MaterialApp.router(
|
||||
routerConfig: _routes,
|
||||
title: 'Event-Tool',
|
||||
theme: ThemeData(
|
||||
useMaterial3: true,
|
||||
|
@ -7,7 +7,7 @@ final serverUrlProvider = Provider<String>((ref) {
|
||||
if (kDebugMode) {
|
||||
return "http://localhost:8000";
|
||||
} else {
|
||||
return "https://steamwar.de";
|
||||
return "http://localhost:8000";
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
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 '../../provider/events.dart';
|
||||
@ -65,21 +66,13 @@ class EventDialog extends HookConsumerWidget {
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
final nav = Navigator.of(context);
|
||||
final event = await ref.read(eventRepositoryProvider.future).then(
|
||||
(value) => value.createEvent(
|
||||
eventName.text, startTime.value, endTime.value));
|
||||
final eventFull = await ref
|
||||
.read(eventRepositoryProvider.future)
|
||||
.then((value) => value.getEvent(event.id));
|
||||
nav.pop();
|
||||
nav.push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) {
|
||||
return EditEventScreen(true, eventFull);
|
||||
},
|
||||
),
|
||||
);
|
||||
context.go('/event/${event.id}');
|
||||
},
|
||||
child: const Text("Create"),
|
||||
),
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
|
||||
import '../../provider/events.dart';
|
||||
@ -40,17 +41,7 @@ class EventListComponent extends HookConsumerWidget {
|
||||
subtitle: Text("${e.start} - ${e.end}"),
|
||||
leading: const Icon(Icons.event),
|
||||
onTap: () async {
|
||||
final nav = Navigator.of(context);
|
||||
final event = await ref
|
||||
.read(eventRepositoryProvider.future)
|
||||
.then((value) => value.getEvent(e.id));
|
||||
nav.push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) {
|
||||
return EditEventScreen(true, event);
|
||||
},
|
||||
),
|
||||
);
|
||||
context.go("/event/${e.id}");
|
||||
},
|
||||
),
|
||||
),
|
||||
@ -64,21 +55,7 @@ class EventListComponent extends HookConsumerWidget {
|
||||
leading: const Icon(Icons.check),
|
||||
subtitle: Text("${e.start} - ${e.end}"),
|
||||
onTap: () async {
|
||||
final nav = Navigator.of(context);
|
||||
final event = await ref
|
||||
.read(eventRepositoryProvider.future)
|
||||
.then((value) => value.getEvent(e.id));
|
||||
nav.push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) {
|
||||
return EditEventScreen(
|
||||
false ||
|
||||
(userData.valueOrNull?.uneditablePastEvents ??
|
||||
false),
|
||||
event);
|
||||
},
|
||||
),
|
||||
);
|
||||
context.go("/event/${e.id}");
|
||||
},
|
||||
)),
|
||||
],
|
||||
|
@ -2,6 +2,7 @@ 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 '../provider/events.dart';
|
||||
@ -17,14 +18,57 @@ T? catchToNull<T>(T Function() f) {
|
||||
}
|
||||
|
||||
class EditEventScreen extends HookConsumerWidget {
|
||||
final bool editable;
|
||||
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 _EventScreen(
|
||||
eventData: eventData,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _EventScreen extends HookConsumerWidget {
|
||||
const _EventScreen({Key? key, required this.eventData}) : super(key: key);
|
||||
|
||||
final EventExtended eventData;
|
||||
const EditEventScreen(this.editable, this.eventData, {Key? key})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final event = eventData.event;
|
||||
|
||||
final nameController = useTextEditingController(text: event.name);
|
||||
final deadlineState = useState(event.deadline);
|
||||
final startDateState = useState(event.start);
|
||||
@ -41,10 +85,9 @@ class EditEventScreen extends HookConsumerWidget {
|
||||
final spectateSystemState = useState(event.spectateSystem);
|
||||
|
||||
final changed = useState(false);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text("${editable ? "Edit" : "View"} ${event.name}"),
|
||||
title: Text("${"Edit"} ${event.name}"),
|
||||
actions: [
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
@ -95,7 +138,7 @@ class EditEventScreen extends HookConsumerWidget {
|
||||
))
|
||||
.whenComplete(
|
||||
() => ref.invalidate(eventsListProvider));
|
||||
Navigator.of(context).pop();
|
||||
context.go('/');
|
||||
}
|
||||
: null,
|
||||
icon: const Icon(Icons.save)),
|
||||
@ -126,7 +169,7 @@ class EditEventScreen extends HookConsumerWidget {
|
||||
} else {
|
||||
canPop = true;
|
||||
}
|
||||
if (canPop) Navigator.of(context).pop();
|
||||
if (canPop) context.go("/");
|
||||
},
|
||||
icon: const Icon(Icons.arrow_back),
|
||||
),
|
||||
@ -141,7 +184,6 @@ class EditEventScreen extends HookConsumerWidget {
|
||||
labelText: "Name",
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
enabled: editable,
|
||||
onChanged: (value) {
|
||||
changed.value = true;
|
||||
},
|
||||
@ -153,7 +195,7 @@ class EditEventScreen extends HookConsumerWidget {
|
||||
DateTimeEditor((p0) {
|
||||
deadlineState.value = p0;
|
||||
changed.value = true;
|
||||
}, deadlineState.value, editable),
|
||||
}, deadlineState.value, true),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
@ -163,7 +205,7 @@ class EditEventScreen extends HookConsumerWidget {
|
||||
DateTimeEditor((p0) {
|
||||
startDateState.value = p0;
|
||||
changed.value = true;
|
||||
}, startDateState.value, editable),
|
||||
}, startDateState.value, true),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
@ -173,7 +215,7 @@ class EditEventScreen extends HookConsumerWidget {
|
||||
DateTimeEditor((p0) {
|
||||
endDateState.value = p0;
|
||||
changed.value = true;
|
||||
}, endDateState.value, editable),
|
||||
}, endDateState.value, true),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
@ -185,7 +227,6 @@ class EditEventScreen extends HookConsumerWidget {
|
||||
errorText: invalidMaxTeamMembers.value
|
||||
? "Must be a number above 0"
|
||||
: null),
|
||||
enabled: editable,
|
||||
keyboardType: TextInputType.number,
|
||||
onChanged: (value) {
|
||||
if (value.isEmpty ||
|
||||
@ -199,44 +240,39 @@ class EditEventScreen extends HookConsumerWidget {
|
||||
}
|
||||
},
|
||||
),
|
||||
if (editable)
|
||||
Slider(
|
||||
value: maxTeamMembersState.value.toDouble(),
|
||||
onChanged: editable
|
||||
? (p0) {
|
||||
maxTeamMembersState.value = p0.toInt();
|
||||
maxTeamMembersController.text = p0.toInt().toString();
|
||||
changed.value = true;
|
||||
}
|
||||
: null,
|
||||
min: min(1, maxTeamMembersState.value.toDouble()),
|
||||
max: max(30, maxTeamMembersState.value.toDouble()),
|
||||
divisions:
|
||||
max(30, maxTeamMembersState.value.toDouble()).toInt() - 1,
|
||||
label: maxTeamMembersState.value.toString(),
|
||||
),
|
||||
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: editable
|
||||
? () 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;
|
||||
}
|
||||
}
|
||||
: null,
|
||||
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")),
|
||||
],
|
||||
),
|
||||
@ -248,7 +284,6 @@ class EditEventScreen extends HookConsumerWidget {
|
||||
changed.value = true;
|
||||
},
|
||||
title: const Text("Public Only"),
|
||||
enabled: editable,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
CheckboxListTile(
|
||||
@ -258,7 +293,6 @@ class EditEventScreen extends HookConsumerWidget {
|
||||
changed.value = true;
|
||||
},
|
||||
title: const Text("Use Spectate System"),
|
||||
enabled: editable,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text("Teams", style: Theme.of(context).textTheme.headline6),
|
||||
|
@ -1,5 +1,8 @@
|
||||
import 'dart:async';
|
||||
|
||||
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:shared_preferences/shared_preferences.dart';
|
||||
|
||||
@ -15,6 +18,23 @@ class HomeScreen extends HookConsumerWidget {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final servers = ref.watch(serversProvider);
|
||||
|
||||
final userData = ref.watch(userDataProvider);
|
||||
|
||||
if (userData.hasError) {
|
||||
Timer.run(() {
|
||||
context.go('/login');
|
||||
});
|
||||
return Scaffold(
|
||||
body: Center(
|
||||
child: FloatingActionButton.large(
|
||||
onPressed: () {
|
||||
context.go('/login');
|
||||
},
|
||||
child: const Icon(Icons.login)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final navRailIndex = useState(0);
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
@ -49,7 +69,7 @@ class HomeScreen extends HookConsumerWidget {
|
||||
await prefs.remove("key");
|
||||
ref.refresh(userDataProvider);
|
||||
nav.pop();
|
||||
nav.pushReplacementNamed("/");
|
||||
context.go('/login');
|
||||
},
|
||||
child: const Text('Logout',
|
||||
style: TextStyle(color: Colors.red)),
|
||||
|
@ -1,7 +1,12 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
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:shared_preferences/shared_preferences.dart';
|
||||
import 'package:steamwar_multitool/src/provider/http.dart';
|
||||
|
||||
import '../provider/user.dart';
|
||||
|
||||
@ -15,14 +20,22 @@ class LoginScreenWidget extends HookConsumerWidget {
|
||||
final keyController = useTextEditingController();
|
||||
final updater = useState(0);
|
||||
|
||||
ref.read(userDataProvider).when(
|
||||
data: (data) {
|
||||
keyController.text = data.key;
|
||||
updater.value++;
|
||||
},
|
||||
loading: () {},
|
||||
error: (error, stack) {},
|
||||
);
|
||||
final userData = ref.watch(userDataProvider);
|
||||
|
||||
if (userData.hasValue) {
|
||||
Timer.run(() {
|
||||
context.go('/');
|
||||
});
|
||||
return Scaffold(
|
||||
body: Center(
|
||||
child: FloatingActionButton.large(
|
||||
onPressed: () {
|
||||
context.go('/');
|
||||
},
|
||||
child: const Icon(Icons.home)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
@ -53,11 +66,28 @@ class LoginScreenWidget extends HookConsumerWidget {
|
||||
FloatingActionButton.large(
|
||||
onPressed: keyController.text.isNotEmpty
|
||||
? () async {
|
||||
final nav = Navigator.of(context);
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setString("key", keyController.text);
|
||||
ref.invalidate(userDataProvider);
|
||||
nav.pushReplacementNamed("/home");
|
||||
final dio = Dio(
|
||||
BaseOptions(
|
||||
baseUrl: ref.read(serverUrlProvider),
|
||||
headers: {
|
||||
'X-SW-Auth': keyController.text,
|
||||
},
|
||||
contentType: 'application/json',
|
||||
),
|
||||
);
|
||||
try {
|
||||
await dio.get('/data');
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setString("key", keyController.text);
|
||||
ref.invalidate(userDataProvider);
|
||||
context.go('/');
|
||||
} catch (e) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Invalid Key'),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
: null,
|
||||
child: const Icon(Icons.forward),
|
||||
|
@ -261,6 +261,13 @@ packages:
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
go_router:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: go_router
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "6.0.0"
|
||||
graphs:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -667,4 +674,4 @@ packages:
|
||||
version: "3.1.1"
|
||||
sdks:
|
||||
dart: ">=2.18.1 <3.0.0"
|
||||
flutter: ">=3.0.0"
|
||||
flutter: ">=3.3.0"
|
||||
|
@ -20,6 +20,7 @@ dependencies:
|
||||
shared_preferences: ^2.0.15
|
||||
xterm: ^3.4.0
|
||||
dio: ^4.0.6
|
||||
go_router: ^6.0.0
|
||||
|
||||
|
||||
dev_dependencies:
|
||||
|
BIN
web/favicon.png
Vorher Breite: | Höhe: | Größe: 917 B Nachher Breite: | Höhe: | Größe: 7.9 KiB |
Vorher Breite: | Höhe: | Größe: 5.2 KiB Nachher Breite: | Höhe: | Größe: 37 KiB |
Vorher Breite: | Höhe: | Größe: 8.1 KiB Nachher Breite: | Höhe: | Größe: 101 KiB |
Vorher Breite: | Höhe: | Größe: 5.5 KiB Nachher Breite: | Höhe: | Größe: 37 KiB |
Vorher Breite: | Höhe: | Größe: 20 KiB Nachher Breite: | Höhe: | Größe: 95 KiB |