From f36505a38e87e36c293fb95bf04baf0d26a80b5e Mon Sep 17 00:00:00 2001 From: Mikkel Troels Kongsted Date: Wed, 19 Mar 2025 14:52:07 +0100 Subject: [PATCH] cookies as persistent storage --- mobile/lib/controllers/session.dart | 83 ++++++++++++++++++------- mobile/lib/main.dart | 8 ++- mobile/lib/pages/all_products_page.dart | 8 +-- 3 files changed, 69 insertions(+), 30 deletions(-) diff --git a/mobile/lib/controllers/session.dart b/mobile/lib/controllers/session.dart index 991e39f..a19b419 100644 --- a/mobile/lib/controllers/session.dart +++ b/mobile/lib/controllers/session.dart @@ -1,12 +1,40 @@ +import 'dart:io'; + import 'package:flutter/material.dart'; import 'package:mobile/models/user.dart'; import 'package:mobile/results.dart'; import 'package:mobile/server/server.dart'; +import 'package:path_provider/path_provider.dart'; + +class CookieController { + CookieController(); + + static Future get _cacheFile async { + final directory = await getApplicationCacheDirectory(); + return File("${directory.path}/cookies.txt").create(); + } + + Future clear() async { + (await _cacheFile).writeAsString("", mode: FileMode.write); + } + + Future save(String token) async { + (await _cacheFile).writeAsString(token, mode: FileMode.write); + } + + Future> load() async { + final token = await (await _cacheFile).readAsString(); + if (token.isEmpty) { + return const Err(null); + } + return Ok(token); + } +} class SessionController { final Server server; + final CookieController cookieController = CookieController(); - String? _sessionToken; User? _sessionUser; final List<_ChangeListener> _sessionChangeListeners = []; @@ -18,7 +46,7 @@ class SessionController { final loginResult = await server.login(email, password); switch (loginResult) { case Ok(value: final sessionToken): - _sessionToken = sessionToken; + await cookieController.save(sessionToken); notifySessionChangeListeners(); return const Ok(null); case Err(value: final message): @@ -27,8 +55,13 @@ class SessionController { } Future> loadCachedUser() async { - // TODO: retrieve session from cache, if exists - return _loadCurrentUser(); + switch (await cookieController.load()) { + case Ok(): + return _loadCurrentUser(); + case Err(): + notifyUserChangeListeners(); + return const Err(null); + } } Future> loadUpdatedUser() async { @@ -49,13 +82,16 @@ class SessionController { } Future logout() async { - final sessionToken = _sessionToken; - if (sessionToken == null) { - return; + switch (await cookieController.load()) { + case Ok(value: final sessionToken): + await server.logout(sessionToken); + await cookieController.clear(); + _sessionUser = null; + notifySessionChangeListeners(); + case Err(): + notifySessionChangeListeners(); + return; } - await server.logout(sessionToken); - _sessionToken = null; - notifySessionChangeListeners(); } User get user { @@ -86,21 +122,22 @@ class SessionController { Future> requestWithSession( Future> Function(Server server, String sessionToken) func) async { - final sessionToken = _sessionToken; - if (sessionToken == null) { - return const Err("unathorized"); - } - final result = await func(server, sessionToken); - if (result case Err(value: final message)) { - if (message == "unauthorized") { - _sessionToken = null; - _sessionUser = null; - notifySessionChangeListeners(); - notifyUserChangeListeners(); + switch (await cookieController.load()) { + case Err(): return const Err("unathorized"); - } + case Ok(value: final sessionToken): + final result = await func(server, sessionToken); + if (result case Err(value: final message)) { + if (message == "unauthorized") { + cookieController.clear(); + _sessionUser = null; + notifySessionChangeListeners(); + notifyUserChangeListeners(); + return const Err("unathorized"); + } + } + return result; } - return result; } void notifySessionChangeListeners() { diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index 7012d69..00c107d 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -18,6 +18,7 @@ import 'package:provider/provider.dart'; import 'package:mobile/controllers/routing.dart'; void main() { + WidgetsFlutterBinding.ensureInitialized(); final server = BackendServer(); final usersController = UsersController(server: server); final sessionController = SessionController(server: server); @@ -47,6 +48,7 @@ class MyApp extends StatelessWidget { Widget build(BuildContext context) { return MultiProvider( providers: [ + Provider(create: (_) => CookieController()), ChangeNotifierProvider( create: (_) => SessionProvider(controller: sessionController)), ChangeNotifierProvider( @@ -79,12 +81,12 @@ class MyApp extends StatelessWidget { useMaterial3: true, ), home: Consumer2( - builder: (_, provider1, provider2, ___) { - if (provider1.controller.hasUser) { + builder: (_, sessionProvider, currentUserProvider, ___) { + if (sessionProvider.controller.hasUser) { return Dashboard(); } return FutureBuilder( - future: provider1.controller.loadCachedUser(), + future: sessionProvider.controller.loadCachedUser(), builder: (_, snapshot) { final error = snapshot.error; if (error != null) { diff --git a/mobile/lib/pages/all_products_page.dart b/mobile/lib/pages/all_products_page.dart index 0f7077d..27803f7 100644 --- a/mobile/lib/pages/all_products_page.dart +++ b/mobile/lib/pages/all_products_page.dart @@ -102,12 +102,12 @@ class AllProductsPage extends StatefulWidget { } class _AllProductsPageState extends State { - final seawchContwowwew = TextEditingController(); + final searchController = TextEditingController(); @override void initState() { - final contwowwew = context.read(); - seawchContwowwew.text = contwowwew.query; + final controller = context.read(); + searchController.text = controller.query; super.initState(); } @@ -128,7 +128,7 @@ class _AllProductsPageState extends State { onChanged: (query) { productRepo.searchProducts(query); }, - controller: seawchContwowwew, + controller: searchController, decoration: const InputDecoration( label: Text("Search"), contentPadding: EdgeInsets.only(top: 20))),