From 8e379241eecc2a10086fc0e01850174953ffa5a5 Mon Sep 17 00:00:00 2001 From: alzalia1 Date: Wed, 13 Aug 2025 14:16:34 +0200 Subject: [PATCH] feat: check for correct version --- lib/config/constants.dart | 1 + lib/data/repositories/auth_repository.dart | 4 + lib/data/services/auth_client.dart | 19 ++ lib/routing/router.dart | 13 +- lib/ui/auth/viewmodel/login_view_model.dart | 37 ++++ lib/ui/auth/widgets/login_page.dart | 187 ++++++++++---------- 6 files changed, 169 insertions(+), 92 deletions(-) diff --git a/lib/config/constants.dart b/lib/config/constants.dart index 52e6729..fb391cd 100644 --- a/lib/config/constants.dart +++ b/lib/config/constants.dart @@ -1 +1,2 @@ const apiBasePath = "bal.ueauvergne.fr/api"; +const apiVersion = 1; diff --git a/lib/data/repositories/auth_repository.dart b/lib/data/repositories/auth_repository.dart index 747e161..2b56ddb 100644 --- a/lib/data/repositories/auth_repository.dart +++ b/lib/data/repositories/auth_repository.dart @@ -39,4 +39,8 @@ class AuthRepository extends ChangeNotifier { return Result.error(Exception(e)); } } + + Future> getRemoteApiVersion() async { + return await _authClient.getRemoteApiVersion(); + } } diff --git a/lib/data/services/auth_client.dart b/lib/data/services/auth_client.dart index 1790346..4d77b4f 100644 --- a/lib/data/services/auth_client.dart +++ b/lib/data/services/auth_client.dart @@ -66,4 +66,23 @@ class AuthClient { client.close(); } } + + Future> 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(); + } + } } diff --git a/lib/routing/router.dart b/lib/routing/router.dart index 0275bf0..3fa53d3 100644 --- a/lib/routing/router.dart +++ b/lib/routing/router.dart @@ -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().isLoggedIn; + final result = await context.read().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; } diff --git a/lib/ui/auth/viewmodel/login_view_model.dart b/lib/ui/auth/viewmodel/login_view_model.dart index df0a91f..190ea46 100644 --- a/lib/ui/auth/viewmodel/login_view_model.dart +++ b/lib/ui/auth/viewmodel/login_view_model.dart @@ -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(_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> _load() async { + final result1 = await _loadApiVersion(); + switch (result1) { + case Ok(): + isLoaded = true; + break; + default: + break; + } + notifyListeners(); + return result1; + } + + Future> _loadApiVersion() async { + final result = await _authRepository.getRemoteApiVersion(); + switch (result) { + case Ok(): + isUpToDate = result.value == apiVersion; + break; + default: + break; + } + return result; + } } diff --git a/lib/ui/auth/widgets/login_page.dart b/lib/ui/auth/widgets/login_page.dart index 7c162da..2bc0295 100644 --- a/lib/ui/auth/widgets/login_page.dart +++ b/lib/ui/auth/widgets/login_page.dart @@ -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 { @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"), - // ); - // }, - // ), + }, ), ); }