mirror of
https://github.com/hanskokx/arcane_framework.git
synced 2026-05-14 02:19:08 +02:00
v1.2.3
- Added `ValueNotifier`s to both the `ArcaneAuthenticationService` and `ArcaneFeatureFlags`. This enables the possibility of listening for changes to either service. Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
@@ -1,3 +1,27 @@
|
|||||||
|
## 1.2.3
|
||||||
|
|
||||||
|
- Added `ValueNotifier`s to both the `ArcaneAuthenticationService` and
|
||||||
|
`ArcaneFeatureFlags`. This enables the possibility of listening for changes to
|
||||||
|
either service.
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
```dart
|
||||||
|
// Listen to changes in the authentication status
|
||||||
|
Arcane.auth.isSignedIn.addListener(() {
|
||||||
|
if (Arcane.auth.isSignedIn.value) {
|
||||||
|
Arcane.log("User is signed in");
|
||||||
|
} else {
|
||||||
|
Arcane.log("User is signed out");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Listen to changes in the enabled/disabled features
|
||||||
|
Arcane.features.notifier.addListener(() {
|
||||||
|
Arcane.log("Enabled features have been updated: ${Arcane.features.notifier.value}");
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
## 1.2.2
|
## 1.2.2
|
||||||
|
|
||||||
- Lowered minimum required collection dependency version to prevent forcing
|
- Lowered minimum required collection dependency version to prevent forcing
|
||||||
|
|||||||
@@ -54,27 +54,27 @@ A service's purpose is to facilitate cross-feature communication of small pieces
|
|||||||
|
|
||||||
```dart
|
```dart
|
||||||
class FavoriteColorService extends ArcaneService {
|
class FavoriteColorService extends ArcaneService {
|
||||||
static bool _mocked = false;
|
|
||||||
static final FavoriteColorService _instance = FavoriteColorService._internal();
|
static final FavoriteColorService _instance = FavoriteColorService._internal();
|
||||||
|
|
||||||
static FavoriteColorService get I => _instance;
|
static FavoriteColorService get I => _instance;
|
||||||
|
|
||||||
FavoriteColorService._internal();
|
FavoriteColorService._internal();
|
||||||
|
|
||||||
Color? _myFavoriteColor;
|
Color? get myFavoriteColor => _notifier.value;
|
||||||
Color? get myFavoriteColor => _myFavoriteColor;
|
|
||||||
|
final ValueNotifier<Color?> _notifier = ValueNotifier<Color?>(null);
|
||||||
|
|
||||||
|
ValueNotifier<Color?> get notifier => _notifier;
|
||||||
|
|
||||||
void setMyFavoriteColor(Color? newValue) {
|
void setMyFavoriteColor(Color? newValue) {
|
||||||
if (_mocked) return;
|
if (_notifier.value != newValue) {
|
||||||
|
_notifier.value = newValue;
|
||||||
_myFavoriteColor = newValue;
|
}
|
||||||
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
@visibleForTesting
|
|
||||||
static void setMocked() => _mocked = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
To register a service with Arcane, simply add the instance of the `ArcaneService` to your list of services when initializing the `ArcaneApp`.
|
To register a service with Arcane, simply add the instance of the `ArcaneService` to your list of services when initializing the `ArcaneApp`.
|
||||||
@@ -88,7 +88,14 @@ ArcaneApp(
|
|||||||
),
|
),
|
||||||
```
|
```
|
||||||
|
|
||||||
Service properties can be accessed either directly (e.g., `FavoriteColorService.I.myFavoriteColor`) or via `BuildContext` (e.g., `context.serviceOfType<FavoriteColorService>()?.myFavoriteColor`). If the `notifyListeners()` method is included within your service, any widgets that are referencing the service property through `BuildContext` will automatically be notified of the change.
|
Service properties can be accessed either directly (e.g., `FavoriteColorService.I.myFavoriteColor`) or via `BuildContext` (e.g., `context.serviceOfType<FavoriteColorService>()?.myFavoriteColor`). If the `notifyListeners()` method is included within your service, any widgets that are referencing the service property through `BuildContext` will automatically be notified of the change. Additionally, a listener can be added to watch the value for changes.
|
||||||
|
|
||||||
|
```dart
|
||||||
|
FavoriteColorService.I.notifier.addListener(() {
|
||||||
|
final Color? color = FavoriteColorService.I.myFavoriteColor;
|
||||||
|
// Do something with the value
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
### Feature Flags
|
### Feature Flags
|
||||||
|
|
||||||
@@ -154,6 +161,14 @@ To get a list of the currently enabled features, simply ask the Arcane feature f
|
|||||||
final List<Enum> enabledFeatures = Arcane.features.enabledFeatures;
|
final List<Enum> enabledFeatures = Arcane.features.enabledFeatures;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
It is also possible to add a listener to watch for changes in the enabled features.
|
||||||
|
|
||||||
|
```dart
|
||||||
|
Arcane.features.notifier.addListener(() {
|
||||||
|
print("Features changed: ${Arcane.features.enabledFeatures}");
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
Note that it is possible to register multiple different `Enum` types in the feature flag service, should one have a need to do so.
|
Note that it is possible to register multiple different `Enum` types in the feature flag service, should one have a need to do so.
|
||||||
|
|
||||||
### Logging
|
### Logging
|
||||||
@@ -166,17 +181,13 @@ To get started, first create one or more logging interfaces, extending the `Logg
|
|||||||
class DebugConsole implements LoggingInterface {
|
class DebugConsole implements LoggingInterface {
|
||||||
static final DebugConsole _instance = DebugConsole._internal();
|
static final DebugConsole _instance = DebugConsole._internal();
|
||||||
static DebugConsole get I => _instance;
|
static DebugConsole get I => _instance;
|
||||||
|
DebugConsole._internal();
|
||||||
|
|
||||||
final bool _initialized = true;
|
final bool _initialized = true;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool get initialized => I._initialized;
|
bool get initialized => I._initialized;
|
||||||
|
|
||||||
DebugConsole._internal();
|
|
||||||
|
|
||||||
@visibleForTesting
|
|
||||||
void setMocked() => _mocked = true;
|
|
||||||
bool _mocked = false;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void log(
|
void log(
|
||||||
@@ -192,11 +203,7 @@ class DebugConsole implements LoggingInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<LoggingInterface?> init() async {
|
Future<LoggingInterface?> init() async => I;
|
||||||
if (_mocked) return null;
|
|
||||||
|
|
||||||
return I;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import "dart:async";
|
import "dart:async";
|
||||||
|
|
||||||
import "package:arcane_framework/arcane_framework.dart";
|
import "package:arcane_framework/arcane_framework.dart";
|
||||||
import "package:flutter/foundation.dart";
|
|
||||||
import "package:flutter/widgets.dart";
|
import "package:flutter/widgets.dart";
|
||||||
import "package:flutter_bloc/flutter_bloc.dart";
|
import "package:flutter_bloc/flutter_bloc.dart";
|
||||||
|
|
||||||
@@ -15,13 +14,16 @@ part "authentication_interface.dart";
|
|||||||
class ArcaneAuthenticationService extends ArcaneService {
|
class ArcaneAuthenticationService extends ArcaneService {
|
||||||
ArcaneAuthenticationService._internal();
|
ArcaneAuthenticationService._internal();
|
||||||
|
|
||||||
static final ArcaneAuthenticationService _instance =
|
static final ArcaneAuthenticationService _instance = ArcaneAuthenticationService._internal();
|
||||||
ArcaneAuthenticationService._internal();
|
|
||||||
|
|
||||||
/// Provides access to the singleton instance of this service.
|
/// Provides access to the singleton instance of this service.
|
||||||
static ArcaneAuthenticationService get I => _instance;
|
static ArcaneAuthenticationService get I => _instance;
|
||||||
|
|
||||||
AuthenticationStatus _status = AuthenticationStatus.unauthenticated;
|
final ValueNotifier<AuthenticationStatus> _notifier =
|
||||||
|
ValueNotifier<AuthenticationStatus>(AuthenticationStatus.unauthenticated);
|
||||||
|
|
||||||
|
/// A `ValueNotifier` that emits the current `AuthenticationStatus`.
|
||||||
|
ValueNotifier<AuthenticationStatus> get notifier => _notifier;
|
||||||
|
|
||||||
/// Returns the current `AuthenticationStatus`.
|
/// Returns the current `AuthenticationStatus`.
|
||||||
///
|
///
|
||||||
@@ -29,7 +31,7 @@ class ArcaneAuthenticationService extends ArcaneService {
|
|||||||
/// - `authenticated`: The user has successfully authenticated and is logged in.
|
/// - `authenticated`: The user has successfully authenticated and is logged in.
|
||||||
/// - `unauthenticated`: The user has not yet logged in.
|
/// - `unauthenticated`: The user has not yet logged in.
|
||||||
/// - `debug`: Debug mode has been enabled, enabling development features.
|
/// - `debug`: Debug mode has been enabled, enabling development features.
|
||||||
AuthenticationStatus get status => _status;
|
AuthenticationStatus get status => _notifier.value;
|
||||||
|
|
||||||
static ArcaneAuthInterface? _authInterface;
|
static ArcaneAuthInterface? _authInterface;
|
||||||
|
|
||||||
@@ -40,25 +42,26 @@ class ArcaneAuthenticationService extends ArcaneService {
|
|||||||
/// A shortcut to `status != AuthenticationStatus.unauthenticated`.
|
/// A shortcut to `status != AuthenticationStatus.unauthenticated`.
|
||||||
bool get isAuthenticated => status != AuthenticationStatus.unauthenticated;
|
bool get isAuthenticated => status != AuthenticationStatus.unauthenticated;
|
||||||
|
|
||||||
/// Expose the ValueListenable so other widgets can listen to changes.
|
final ValueNotifier<bool> _isSignedIn = ValueNotifier<bool>(false);
|
||||||
ValueListenable<bool> get isSignedIn => ValueNotifier<bool>(isAuthenticated);
|
|
||||||
|
/// A `ValueNotifier` that emits `true` if the user is currently signed in.
|
||||||
|
ValueNotifier<bool> get isSignedIn => _isSignedIn;
|
||||||
|
|
||||||
/// Returns a JWT access token if the registered `ArcaneAuthInterface`
|
/// Returns a JWT access token if the registered `ArcaneAuthInterface`
|
||||||
/// provides one. This token is often used in the headers of HTTP requests
|
/// provides one. This token is often used in the headers of HTTP requests
|
||||||
/// to the backend API.
|
/// to the backend API.
|
||||||
Future<String?> get accessToken =>
|
Future<String?> get accessToken => authInterface?.accessToken ?? Future.value("");
|
||||||
authInterface?.accessToken ?? Future.value("");
|
|
||||||
|
|
||||||
/// Returns a JWT refresh token if the registered `ArcaneAuthInterface`
|
/// Returns a JWT refresh token if the registered `ArcaneAuthInterface`
|
||||||
/// provides one.
|
/// provides one.
|
||||||
Future<String?> get refreshToken =>
|
Future<String?> get refreshToken => authInterface?.refreshToken ?? Future.value("");
|
||||||
authInterface?.refreshToken ?? Future.value("");
|
|
||||||
|
|
||||||
/// Removes any registered `ArcaneAuthInterface` and resets all values to
|
/// Removes any registered `ArcaneAuthInterface` and resets all values to
|
||||||
/// default.
|
/// default.
|
||||||
Future<void> reset() async {
|
Future<void> reset() async {
|
||||||
_authInterface = null;
|
_authInterface = null;
|
||||||
_status = AuthenticationStatus.unauthenticated;
|
_notifier.value = AuthenticationStatus.unauthenticated;
|
||||||
|
_isSignedIn.value = isAuthenticated;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,7 +146,10 @@ class ArcaneAuthenticationService extends ArcaneService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _setStatus(AuthenticationStatus newStatus) {
|
void _setStatus(AuthenticationStatus newStatus) {
|
||||||
_status = newStatus;
|
if (_notifier.value != newStatus) {
|
||||||
|
_notifier.value = newStatus;
|
||||||
|
_isSignedIn.value = isAuthenticated;
|
||||||
|
}
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,8 +273,7 @@ class ArcaneAuthenticationService extends ArcaneService {
|
|||||||
|
|
||||||
final auth = authInterface as ArcaneAuthAccountRegistration;
|
final auth = authInterface as ArcaneAuthAccountRegistration;
|
||||||
|
|
||||||
final Future<Result<String, String>>? result =
|
final Future<Result<String, String>>? result = auth.resendVerificationCode(input: email);
|
||||||
auth.resendVerificationCode(input: email);
|
|
||||||
|
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
return Result.error(
|
return Result.error(
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import "package:arcane_framework/arcane_framework.dart";
|
import "package:arcane_framework/arcane_framework.dart";
|
||||||
|
import "package:flutter/foundation.dart";
|
||||||
|
|
||||||
part "feature_flags_extensions.dart";
|
part "feature_flags_extensions.dart";
|
||||||
|
|
||||||
@@ -31,6 +32,11 @@ class ArcaneFeatureFlags extends ArcaneService {
|
|||||||
List<Enum> get enabledFeatures => _enabledFeatures;
|
List<Enum> get enabledFeatures => _enabledFeatures;
|
||||||
final List<Enum> _enabledFeatures = [];
|
final List<Enum> _enabledFeatures = [];
|
||||||
|
|
||||||
|
final ValueNotifier<List<Enum>> _notifier = ValueNotifier<List<Enum>>([]);
|
||||||
|
|
||||||
|
/// A `ValueNotifier` that notifies listeners when the list of enabled features changes.
|
||||||
|
ValueNotifier<List<Enum>> get notifier => _notifier;
|
||||||
|
|
||||||
/// Indicates whether the feature flags have been initialized.
|
/// Indicates whether the feature flags have been initialized.
|
||||||
bool _initialized = false;
|
bool _initialized = false;
|
||||||
|
|
||||||
@@ -65,6 +71,7 @@ class ArcaneFeatureFlags extends ArcaneService {
|
|||||||
if (_enabledFeatures.contains(feature)) return I;
|
if (_enabledFeatures.contains(feature)) return I;
|
||||||
|
|
||||||
_enabledFeatures.add(feature);
|
_enabledFeatures.add(feature);
|
||||||
|
_notifier.value.add(feature);
|
||||||
|
|
||||||
if (Arcane.logger.initialized) {
|
if (Arcane.logger.initialized) {
|
||||||
Arcane.logger.log(
|
Arcane.logger.log(
|
||||||
@@ -94,6 +101,7 @@ class ArcaneFeatureFlags extends ArcaneService {
|
|||||||
if (!_enabledFeatures.contains(feature)) return I;
|
if (!_enabledFeatures.contains(feature)) return I;
|
||||||
|
|
||||||
_enabledFeatures.remove(feature);
|
_enabledFeatures.remove(feature);
|
||||||
|
_notifier.value.remove(feature);
|
||||||
|
|
||||||
if (Arcane.logger.initialized) {
|
if (Arcane.logger.initialized) {
|
||||||
Arcane.logger.log(
|
Arcane.logger.log(
|
||||||
@@ -116,6 +124,7 @@ class ArcaneFeatureFlags extends ArcaneService {
|
|||||||
/// already been initialized.
|
/// already been initialized.
|
||||||
void _init() {
|
void _init() {
|
||||||
_enabledFeatures.clear();
|
_enabledFeatures.clear();
|
||||||
|
_notifier.value.clear();
|
||||||
|
|
||||||
I._initialized = true;
|
I._initialized = true;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
|||||||
+1
-1
@@ -1,6 +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: 1.2.2
|
version: 1.2.3
|
||||||
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
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user