cookies as persistent storage

This commit is contained in:
Mikkel Troels Kongsted 2025-03-19 14:52:07 +01:00
parent fd288dafee
commit f36505a38e
3 changed files with 69 additions and 30 deletions

View File

@ -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<File> get _cacheFile async {
final directory = await getApplicationCacheDirectory();
return File("${directory.path}/cookies.txt").create();
}
Future<void> clear() async {
(await _cacheFile).writeAsString("", mode: FileMode.write);
}
Future<void> save(String token) async {
(await _cacheFile).writeAsString(token, mode: FileMode.write);
}
Future<Result<String, Null>> 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<String, String>(value: final sessionToken):
_sessionToken = sessionToken;
await cookieController.save(sessionToken);
notifySessionChangeListeners();
return const Ok(null);
case Err<String, String>(value: final message):
@ -27,8 +55,13 @@ class SessionController {
}
Future<Result<Null, Null>> loadCachedUser() async {
// TODO: retrieve session from cache, if exists
switch (await cookieController.load()) {
case Ok<String, Null>():
return _loadCurrentUser();
case Err<String, Null>():
notifyUserChangeListeners();
return const Err(null);
}
}
Future<Result<Null, Null>> loadUpdatedUser() async {
@ -49,13 +82,16 @@ class SessionController {
}
Future<Null> logout() async {
final sessionToken = _sessionToken;
if (sessionToken == null) {
switch (await cookieController.load()) {
case Ok<String, Null>(value: final sessionToken):
await server.logout(sessionToken);
await cookieController.clear();
_sessionUser = null;
notifySessionChangeListeners();
case Err<String, Null>():
notifySessionChangeListeners();
return;
}
await server.logout(sessionToken);
_sessionToken = null;
notifySessionChangeListeners();
}
User get user {
@ -86,14 +122,14 @@ class SessionController {
Future<Result<T, String>> requestWithSession<T>(
Future<Result<T, String>> Function(Server server, String sessionToken)
func) async {
final sessionToken = _sessionToken;
if (sessionToken == null) {
switch (await cookieController.load()) {
case Err<String, Null>():
return const Err("unathorized");
}
case Ok<String, Null>(value: final sessionToken):
final result = await func(server, sessionToken);
if (result case Err<T, String>(value: final message)) {
if (message == "unauthorized") {
_sessionToken = null;
cookieController.clear();
_sessionUser = null;
notifySessionChangeListeners();
notifyUserChangeListeners();
@ -102,6 +138,7 @@ class SessionController {
}
return result;
}
}
void notifySessionChangeListeners() {
for (final listener in _sessionChangeListeners) {

View File

@ -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<SessionProvider, CurrentUserProvider>(
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) {

View File

@ -102,12 +102,12 @@ class AllProductsPage extends StatefulWidget {
}
class _AllProductsPageState extends State<AllProductsPage> {
final seawchContwowwew = TextEditingController();
final searchController = TextEditingController();
@override
void initState() {
final contwowwew = context.read<ProductController>();
seawchContwowwew.text = contwowwew.query;
final controller = context.read<ProductController>();
searchController.text = controller.query;
super.initState();
}
@ -128,7 +128,7 @@ class _AllProductsPageState extends State<AllProductsPage> {
onChanged: (query) {
productRepo.searchProducts(query);
},
controller: seawchContwowwew,
controller: searchController,
decoration: const InputDecoration(
label: Text("Search"),
contentPadding: EdgeInsets.only(top: 20))),