156 lines
4.5 KiB
Dart
156 lines
4.5 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
import '../models.dart';
|
|
import '../services/api_service.dart';
|
|
import '../services/auth_service.dart';
|
|
import '../services/update_service.dart';
|
|
import '../widgets/server_card.dart';
|
|
import 'settings_screen.dart';
|
|
|
|
class ServersScreen extends StatefulWidget {
|
|
const ServersScreen({
|
|
super.key,
|
|
required this.auth,
|
|
required this.api,
|
|
required this.onLoggedOut,
|
|
});
|
|
final AuthService auth;
|
|
final ApiService api;
|
|
final VoidCallback onLoggedOut;
|
|
|
|
@override
|
|
State<ServersScreen> createState() => _ServersScreenState();
|
|
}
|
|
|
|
class _ServersScreenState extends State<ServersScreen> {
|
|
List<GameServer>? _servers;
|
|
String? _error;
|
|
final _busy = <String>{};
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_refresh();
|
|
_checkUpdate();
|
|
}
|
|
|
|
Future<void> _refresh() async {
|
|
try {
|
|
final servers = await widget.api.servers();
|
|
if (mounted) setState(() {
|
|
_servers = servers;
|
|
_error = null;
|
|
});
|
|
} on UnauthorizedException {
|
|
await widget.auth.logout();
|
|
widget.onLoggedOut();
|
|
} catch (e) {
|
|
if (mounted) setState(() => _error = '$e');
|
|
}
|
|
}
|
|
|
|
Future<void> _toggle(GameServer s) async {
|
|
setState(() => _busy.add(s.id));
|
|
try {
|
|
s.isUp ? await widget.api.stop(s.id) : await widget.api.start(s.id);
|
|
await Future.delayed(const Duration(seconds: 2));
|
|
await _refresh();
|
|
} catch (e) {
|
|
if (mounted) {
|
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('$e')));
|
|
}
|
|
} finally {
|
|
if (mounted) setState(() => _busy.remove(s.id));
|
|
}
|
|
}
|
|
|
|
Future<void> _checkUpdate() async {
|
|
final info = await UpdateService(widget.api.baseUrl).check();
|
|
if (info == null || !mounted) return;
|
|
final go = await showDialog<bool>(
|
|
context: context,
|
|
builder: (ctx) => AlertDialog(
|
|
title: Text('Mise à jour ${info.version}'),
|
|
content: Text(info.notes.isEmpty ? 'Une nouvelle version est disponible.' : info.notes),
|
|
actions: [
|
|
TextButton(onPressed: () => Navigator.pop(ctx, false), child: const Text('Plus tard')),
|
|
FilledButton(onPressed: () => Navigator.pop(ctx, true), child: const Text('Installer')),
|
|
],
|
|
),
|
|
);
|
|
if (go == true) _applyUpdate(info);
|
|
}
|
|
|
|
void _applyUpdate(UpdateInfo info) {
|
|
UpdateService(widget.api.baseUrl).apply(info).listen((_) {}, onError: (e) {
|
|
if (mounted) {
|
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('MAJ échouée : $e')));
|
|
}
|
|
});
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(content: Text('Téléchargement de la mise à jour…')),
|
|
);
|
|
}
|
|
|
|
Future<void> _openSettings() async {
|
|
await Navigator.push(context,
|
|
MaterialPageRoute(builder: (_) => SettingsScreen(api: widget.api)));
|
|
_refresh();
|
|
}
|
|
|
|
Future<void> _logout() async {
|
|
await widget.auth.logout();
|
|
widget.onLoggedOut();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
title: const Text('Serveurs'),
|
|
actions: [
|
|
IconButton(onPressed: _openSettings, icon: const Icon(Icons.settings_outlined)),
|
|
IconButton(onPressed: _logout, icon: const Icon(Icons.logout)),
|
|
],
|
|
),
|
|
body: RefreshIndicator(
|
|
onRefresh: _refresh,
|
|
child: _buildBody(),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildBody() {
|
|
if (_error != null && _servers == null) {
|
|
return ListView(children: [
|
|
const SizedBox(height: 120),
|
|
Icon(Icons.cloud_off, size: 60, color: Theme.of(context).colorScheme.outline),
|
|
const SizedBox(height: 12),
|
|
Center(child: Text('Connexion à l\'API impossible.\n$_error', textAlign: TextAlign.center)),
|
|
const SizedBox(height: 12),
|
|
Center(child: FilledButton(onPressed: _refresh, child: const Text('Réessayer'))),
|
|
]);
|
|
}
|
|
if (_servers == null) {
|
|
return const Center(child: CircularProgressIndicator());
|
|
}
|
|
return ListView.separated(
|
|
padding: const EdgeInsets.fromLTRB(16, 8, 16, 24),
|
|
itemCount: _servers!.length,
|
|
separatorBuilder: (_, __) => const SizedBox(height: 14),
|
|
itemBuilder: (_, i) {
|
|
final s = _servers![i];
|
|
return FutureBuilder<String>(
|
|
future: widget.api.iconUrl(s.id),
|
|
builder: (_, snap) => ServerCard(
|
|
server: s,
|
|
iconUrl: snap.data ?? '',
|
|
busy: _busy.contains(s.id),
|
|
onToggle: () => _toggle(s),
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|