Add configuration files and update authentication service error handling

- Introduced .vscode/settings.json and .vscode/launch.json for IDE configuration.
- Updated DebugAuthInterface and ArcaneAuthenticationService to return const Result.ok() for consistency.
- Added ArcaneTheme class for theme management.
- Updated pubspec.yaml to change result_monad dependency version.
- Modified authentication_service_test to return const Result.ok() in mock setups.

Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
2026-05-07 21:03:50 +02:00
parent 1e84e8f648
commit f5056c36df
7 changed files with 89 additions and 33 deletions
+4
View File
@@ -0,0 +1,4 @@
{
"dart.flutterSdkPath": "/Users/hans/.puro/envs/stable/flutter",
"dart.sdkPath": "/Users/hans/.puro/envs/stable/flutter/bin/cache/dart-sdk"
}
+25
View File
@@ -0,0 +1,25 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "example",
"request": "launch",
"type": "dart"
},
{
"name": "example (profile mode)",
"request": "launch",
"type": "dart",
"flutterMode": "profile"
},
{
"name": "example (release mode)",
"request": "launch",
"type": "dart",
"flutterMode": "release"
}
]
}
@@ -32,19 +32,19 @@ class DebugAuthInterface
_isSignedIn = false; _isSignedIn = false;
return Result.ok(null); return const Result.ok(null);
} }
@override @override
Future<Result<void, String>> login<Credentials>({ Future<Result<void, String>> login<T>({
Credentials? input, T? input,
Future<void> Function()? onLoggedIn, Future<void> Function()? onLoggedIn,
}) async { }) async {
final bool alreadyLoggedIn = await isSignedIn; final bool alreadyLoggedIn = await isSignedIn;
if (alreadyLoggedIn) return Result.ok(null); if (alreadyLoggedIn) return const Result.ok(null);
final credentials = input as ({String email, String password}); final credentials = input as Credentials;
final String email = credentials.email; final String email = credentials.email;
final String password = credentials.password; final String password = credentials.password;
@@ -53,7 +53,7 @@ class DebugAuthInterface
_isSignedIn = true; _isSignedIn = true;
return Result.ok(null); return const Result.ok(null);
} }
@override @override
@@ -61,15 +61,15 @@ class DebugAuthInterface
T? input, T? input,
}) async { }) async {
Arcane.log("Re-sending verification code to $input"); Arcane.log("Re-sending verification code to $input");
return Result.ok("Code sent"); return const Result.ok("Code sent");
} }
@override @override
Future<Result<SignUpStep, String>> register<Credentials>({ Future<Result<SignUpStep, String>> register<T>({
Credentials? input, T? input,
}) async { }) async {
if (input != null) { if (input != null) {
final credentials = input as ({String email, String password}); final credentials = input as Credentials;
final String email = credentials.email; final String email = credentials.email;
final String password = credentials.password; final String password = credentials.password;
@@ -77,7 +77,7 @@ class DebugAuthInterface
Arcane.log("Creating account for $email with password $password"); Arcane.log("Creating account for $email with password $password");
} }
return Result.ok(SignUpStep.confirmSignUp); return const Result.ok(SignUpStep.confirmSignUp);
} }
@override @override
@@ -88,7 +88,7 @@ class DebugAuthInterface
Arcane.log( Arcane.log(
"Confirming registration for $username with code $confirmationCode", "Confirming registration for $username with code $confirmationCode",
); );
return Result.ok(true); return const Result.ok(true);
} }
@override @override
@@ -98,7 +98,7 @@ class DebugAuthInterface
String? code, String? code,
}) async { }) async {
Arcane.log("Resetting password for $email"); Arcane.log("Resetting password for $email");
return Result.ok(true); return const Result.ok(true);
} }
@override @override
@@ -164,10 +164,10 @@ class ArcaneAuthenticationService extends ArcaneService {
Future<void> Function()? onLoggedOut, Future<void> Function()? onLoggedOut,
}) async { }) async {
if (_authInterface == null) { if (_authInterface == null) {
return Result.error("No ArcaneAuthInterface has been registered"); return const Result.error("No ArcaneAuthInterface has been registered");
} }
if (!isAuthenticated) Result.error("User is not authenticated."); if (!isAuthenticated) const Result.error("User is not authenticated.");
final Result<void, String> loggedOut = await authInterface!.logout( final Result<void, String> loggedOut = await authInterface!.logout(
onLoggedOut: onLoggedOut, onLoggedOut: onLoggedOut,
@@ -188,7 +188,7 @@ class ArcaneAuthenticationService extends ArcaneService {
Future<void> Function()? onLoggedIn, Future<void> Function()? onLoggedIn,
}) async { }) async {
if (_authInterface == null) { if (_authInterface == null) {
return Result.error("No ArcaneAuthInterface has been registered"); return const Result.error("No ArcaneAuthInterface has been registered");
} }
final Result<void, String> result = await authInterface!.login( final Result<void, String> result = await authInterface!.login(
@@ -210,11 +210,11 @@ class ArcaneAuthenticationService extends ArcaneService {
T? input, T? input,
}) async { }) async {
if (_authInterface == null) { if (_authInterface == null) {
return Result.error("No ArcaneAuthInterface has been registered"); return const Result.error("No ArcaneAuthInterface has been registered");
} }
if (authInterface is! ArcaneAuthAccountRegistration) { if (authInterface is! ArcaneAuthAccountRegistration) {
return Result.error( return const Result.error(
"The provided ArcaneAuthInterface does not support account registration.", "The provided ArcaneAuthInterface does not support account registration.",
); );
} }
@@ -226,7 +226,7 @@ class ArcaneAuthenticationService extends ArcaneService {
); );
if (result == null) { if (result == null) {
return Result.error( return const Result.error(
"Registered ArcaneAuthInterface returned a null value.", "Registered ArcaneAuthInterface returned a null value.",
); );
} }
@@ -241,11 +241,11 @@ class ArcaneAuthenticationService extends ArcaneService {
required String confirmationCode, required String confirmationCode,
}) async { }) async {
if (_authInterface == null) { if (_authInterface == null) {
return Result.error("No ArcaneAuthInterface has been registered"); return const Result.error("No ArcaneAuthInterface has been registered");
} }
if (authInterface is! ArcaneAuthAccountRegistration) { if (authInterface is! ArcaneAuthAccountRegistration) {
return Result.error( return const Result.error(
"The provided ArcaneAuthInterface does not support account registration.", "The provided ArcaneAuthInterface does not support account registration.",
); );
} }
@@ -258,7 +258,7 @@ class ArcaneAuthenticationService extends ArcaneService {
); );
if (result == null) { if (result == null) {
return Result.error( return const Result.error(
"Registered ArcaneAuthInterface returned a null value.", "Registered ArcaneAuthInterface returned a null value.",
); );
} }
@@ -270,11 +270,11 @@ class ArcaneAuthenticationService extends ArcaneService {
/// registration. /// registration.
Future<Result<String, String>> resendVerificationCode(String email) async { Future<Result<String, String>> resendVerificationCode(String email) async {
if (_authInterface == null) { if (_authInterface == null) {
return Result.error("No ArcaneAuthInterface has been registered"); return const Result.error("No ArcaneAuthInterface has been registered");
} }
if (authInterface is! ArcaneAuthAccountRegistration) { if (authInterface is! ArcaneAuthAccountRegistration) {
return Result.error( return const Result.error(
"The provided ArcaneAuthInterface does not support account registration.", "The provided ArcaneAuthInterface does not support account registration.",
); );
} }
@@ -285,7 +285,7 @@ class ArcaneAuthenticationService extends ArcaneService {
auth.resendVerificationCode(input: email); auth.resendVerificationCode(input: email);
if (result == null) { if (result == null) {
return Result.error( return const Result.error(
"Registered ArcaneAuthInterface returned a null value.", "Registered ArcaneAuthInterface returned a null value.",
); );
} }
@@ -305,11 +305,11 @@ class ArcaneAuthenticationService extends ArcaneService {
String? confirmationCode, String? confirmationCode,
}) async { }) async {
if (_authInterface == null) { if (_authInterface == null) {
return Result.error("No ArcaneAuthInterface has been registered"); return const Result.error("No ArcaneAuthInterface has been registered");
} }
if (authInterface is! ArcaneAuthPasswordManagement) { if (authInterface is! ArcaneAuthPasswordManagement) {
return Result.error( return const Result.error(
"The provided ArcaneAuthInterface does not support password management.", "The provided ArcaneAuthInterface does not support password management.",
); );
} }
@@ -323,7 +323,7 @@ class ArcaneAuthenticationService extends ArcaneService {
); );
if (result == null) { if (result == null) {
return Result.error( return const Result.error(
"Registered ArcaneAuthInterface returned a null value.", "Registered ArcaneAuthInterface returned a null value.",
); );
} }
@@ -0,0 +1,26 @@
import "package:flutter/material.dart";
class ArcaneTheme extends InheritedWidget {
final ThemeMode themeMode;
final bool followSystem;
final ThemeData? theme;
const ArcaneTheme({
required super.child,
this.themeMode = ThemeMode.light,
this.followSystem = false,
this.theme,
super.key,
});
static ArcaneTheme? of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<ArcaneTheme>();
}
@override
bool updateShouldNotify(ArcaneTheme oldWidget) {
return themeMode != oldWidget.themeMode ||
followSystem != oldWidget.followSystem ||
theme != oldWidget.theme;
}
}
+3 -2
View File
@@ -1,5 +1,6 @@
name: arcane_framework name: arcane_framework
description: "Agnostic Reusable Component Architecture for New Ecosystems: a modern framework for bootstrapping new applications" description: "Agnostic Reusable Component Architecture for New Ecosystems: a
modern framework for bootstrapping new applications"
version: 2.0.0-dev version: 2.0.0-dev
repository: https://github.com/hanskokx/arcane_framework repository: https://github.com/hanskokx/arcane_framework
issue_tracker: https://github.com/hanskokx/arcane_framework/issues issue_tracker: https://github.com/hanskokx/arcane_framework/issues
@@ -16,7 +17,7 @@ dependencies:
collection: ^1.19.0 collection: ^1.19.0
flutter: flutter:
sdk: flutter sdk: flutter
result_monad: ^2.3.2 result_monad: ^4.0.0
dev_dependencies: dev_dependencies:
arcane_analysis: ^1.0.3 arcane_analysis: ^1.0.3
@@ -23,10 +23,10 @@ void main() {
// Set up default mock behaviors // Set up default mock behaviors
when(mockInterface.login(input: anyNamed("input"))).thenAnswer( when(mockInterface.login(input: anyNamed("input"))).thenAnswer(
(_) async => Result.ok(null), (_) async => const Result.ok(null),
); );
when(mockInterface.logout()).thenAnswer( when(mockInterface.logout()).thenAnswer(
(_) async => Result.ok(null), (_) async => const Result.ok(null),
); );
when(mockInterface.init()).thenAnswer( when(mockInterface.init()).thenAnswer(
(_) async {}, (_) async {},
@@ -61,7 +61,7 @@ void main() {
testWidgets("login with failure", (WidgetTester tester) async { testWidgets("login with failure", (WidgetTester tester) async {
// Reset the mock behavior for this specific test // Reset the mock behavior for this specific test
when(mockInterface.login(input: anyNamed("input"))) when(mockInterface.login(input: anyNamed("input")))
.thenAnswer((_) async => Result.error("error")); .thenAnswer((_) async => const Result.error("error"));
final result = await ArcaneAuthenticationService.I final result = await ArcaneAuthenticationService.I
.login(input: {"username": "test"}); .login(input: {"username": "test"});