feat: check for correct version

This commit is contained in:
alzalia1 2025-08-13 14:16:34 +02:00
parent b751d93be6
commit 8e379241ee
6 changed files with 169 additions and 92 deletions

View file

@ -1 +1,2 @@
const apiBasePath = "bal.ueauvergne.fr/api";
const apiVersion = 1;

View file

@ -39,4 +39,8 @@ class AuthRepository extends ChangeNotifier {
return Result.error(Exception(e));
}
}
Future<Result<int>> getRemoteApiVersion() async {
return await _authClient.getRemoteApiVersion();
}
}

View file

@ -66,4 +66,23 @@ class AuthClient {
client.close();
}
}
Future<Result<int>> getRemoteApiVersion() async {
final client = http.Client();
try {
final response = await client.get(
Uri.parse("https://$apiBasePath/version"),
);
if (response.statusCode == 200) {
final json = jsonDecode(response.body) as int;
return Result.ok(json);
} else {
throw "Something wrong happened";
}
} catch (e) {
return Result.error(Exception(e));
} finally {
client.close();
}
}
}

View file

@ -1,5 +1,6 @@
import 'package:go_router/go_router.dart';
import 'package:provider/provider.dart';
import 'package:seshat/config/constants.dart';
import 'package:seshat/data/repositories/auth_repository.dart';
import 'package:seshat/routing/routes.dart';
import 'package:seshat/ui/add_page/view_model/add_view_model.dart';
@ -12,14 +13,24 @@ import 'package:seshat/ui/home_page/view_model/home_view_model.dart';
import 'package:seshat/ui/home_page/widgets/home_page.dart';
import 'package:seshat/ui/sell_page/view_model/sell_view_model.dart';
import 'package:seshat/ui/sell_page/widgets/sell_page.dart';
import 'package:seshat/utils/result.dart';
GoRouter router(AuthRepository authRepository) => GoRouter(
initialLocation: Routes.add,
redirect: (context, state) async {
final loggedIn = await context.read<AuthRepository>().isLoggedIn;
final result = await context.read<AuthRepository>().getRemoteApiVersion();
bool isUpToDate = false;
switch (result) {
case Ok():
isUpToDate = result.value == apiVersion;
break;
default:
break;
}
final logginIn = state.matchedLocation == Routes.login;
if (!loggedIn) {
if (!loggedIn || !isUpToDate) {
return Routes.login;
}

View file

@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:seshat/config/constants.dart';
import 'package:seshat/data/repositories/auth_repository.dart';
import 'package:seshat/utils/command.dart';
import 'package:seshat/utils/result.dart';
@ -7,6 +8,7 @@ class LoginViewModel extends ChangeNotifier {
LoginViewModel({required AuthRepository authRepository})
: _authRepository = authRepository {
login = Command1<void, (String username, String password)>(_login);
load = Command0(_load)..execute();
}
final AuthRepository _authRepository;
@ -18,4 +20,39 @@ class LoginViewModel extends ChangeNotifier {
final result = await _authRepository.login(username, password);
return result;
}
/*
* =================================
* =====[ COMMAND AND LOADING ]=====
* =================================
*/
late final Command0 load;
bool isLoaded = false;
bool isUpToDate = false;
Future<Result<void>> _load() async {
final result1 = await _loadApiVersion();
switch (result1) {
case Ok():
isLoaded = true;
break;
default:
break;
}
notifyListeners();
return result1;
}
Future<Result<void>> _loadApiVersion() async {
final result = await _authRepository.getRemoteApiVersion();
switch (result) {
case Ok():
isUpToDate = result.value == apiVersion;
break;
default:
break;
}
return result;
}
}

View file

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:seshat/routing/routes.dart';
import 'package:seshat/ui/auth/viewmodel/login_view_model.dart';
import 'package:seshat/ui/core/ui/await_loading.dart';
class LoginPage extends StatefulWidget {
const LoginPage({super.key, required this.viewModel});
@ -40,100 +41,104 @@ class _LoginPageState extends State<LoginPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ListenableBuilder(
listenable: widget.viewModel.login,
builder: (context, child) {
return Form(
key: _formKey,
child: SingleChildScrollView(
child: SizedBox(
width: 300,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("Bienvenue", style: TextStyle(fontSize: 40)),
SizedBox(height: 50),
TextFormField(
decoration: InputDecoration(
labelText: "Identifiant de section",
border: OutlineInputBorder(),
),
validator: (value) {
if (value == null || value.isEmpty) {
return "Veuillez entrer un identifiant";
}
return null;
},
onSaved: (newValue) {
username = newValue!;
},
),
SizedBox(height: 10),
TextFormField(
decoration: InputDecoration(
labelText: "Mot de passe",
border: OutlineInputBorder(),
suffixIcon: IconButton(
onPressed: () {
setState(() {
hidePassword = !hidePassword;
});
},
icon: Icon(
(hidePassword)
? Icons.visibility
: Icons.visibility_off,
),
),
),
obscureText: hidePassword,
enableSuggestions: false,
autocorrect: false,
validator: (value) {
if (value == null || value.isEmpty) {
return "Veuillez entrer un mot de passe";
}
return null;
},
onSaved: (newValue) {
password = newValue!;
},
),
SizedBox(height: 10),
ElevatedButton(
onPressed: () {
_formKey.currentState!.validate();
_formKey.currentState!.save();
widget.viewModel.login.execute((username, password));
},
child: Text("Valider"),
),
],
),
body: ListenableBuilder(
listenable: widget.viewModel,
builder: (context, child) => switch (widget.viewModel.isLoaded) {
false => AwaitLoading(),
true => switch (widget.viewModel.isUpToDate) {
false => Center(
child: SizedBox(
width: 300,
child: Text(
"L'application que vous utilisez n'est pas à jour. Si aucune mise à jour n'est disponible, contactez dev@ueauvergne.fr",
),
),
);
),
true => Center(
child: ListenableBuilder(
listenable: widget.viewModel.login,
builder: (context, child) {
return Form(
key: _formKey,
child: SingleChildScrollView(
child: SizedBox(
width: 300,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("Bienvenue", style: TextStyle(fontSize: 40)),
SizedBox(height: 50),
TextFormField(
decoration: InputDecoration(
labelText: "Identifiant de section",
border: OutlineInputBorder(),
),
validator: (value) {
if (value == null || value.isEmpty) {
return "Veuillez entrer un identifiant";
}
return null;
},
onSaved: (newValue) {
username = newValue!;
},
),
SizedBox(height: 10),
TextFormField(
decoration: InputDecoration(
labelText: "Mot de passe",
border: OutlineInputBorder(),
suffixIcon: IconButton(
onPressed: () {
setState(() {
hidePassword = !hidePassword;
});
},
icon: Icon(
(hidePassword)
? Icons.visibility
: Icons.visibility_off,
),
),
),
obscureText: hidePassword,
enableSuggestions: false,
autocorrect: false,
validator: (value) {
if (value == null || value.isEmpty) {
return "Veuillez entrer un mot de passe";
}
return null;
},
onSaved: (newValue) {
password = newValue!;
},
),
SizedBox(height: 10),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
widget.viewModel.login.execute((
username,
password,
));
}
},
child: Text("Valider"),
),
],
),
),
),
);
},
),
),
},
),
// TextField(controller: _username),
// TextField(controller: _password),
// ListenableBuilder(
// listenable: widget.viewModel.login,
// builder: (context, child) {
// return FilledButton(
// onPressed: () {
// widget.viewModel.login.execute((
// _username.value.text,
// _password.value.text,
// ));
// },
// child: Text("Connexion"),
// );
// },
// ),
},
),
);
}