Add shared authentication and email verification services
Provide reusable models, injectable service contracts, lifecycle-safe providers and explicit dummy implementations for login, signup and verification. Add focused error and async lifecycle tests. Related to #4 and #5.
This commit is contained in:
1 parent
d9318053e8
commit
27424ed886
15 files changed
+708
No files matched your search
@@ -0,0 +1,73 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import '../models/verification.dart';
|
||||
import '../services/verification.dart';
|
||||
|
||||
class VerificationProvider extends ChangeNotifier {
|
||||
VerificationProvider(this.service);
|
||||
final VerificationService service;
|
||||
VerificationState _state = const VerificationState(VerificationStatus.idle);
|
||||
VerificationState get state => _state;
|
||||
bool _disposed = false;
|
||||
int _generation = 0;
|
||||
|
||||
Future<void> send({required String email, required String locale}) => _run(
|
||||
() => service.send(email: email.trim(), locale: locale),
|
||||
VerificationStatus.sending,
|
||||
VerificationStatus.sent,
|
||||
);
|
||||
Future<void> confirm(String token) => _run(
|
||||
() => token.trim().isEmpty
|
||||
? Future.error(const VerificationException(VerificationError.invalid))
|
||||
: service.confirm(token: token),
|
||||
VerificationStatus.checking,
|
||||
VerificationStatus.verified,
|
||||
);
|
||||
|
||||
Future<void> _run(
|
||||
Future<VerificationResult> Function() operation,
|
||||
VerificationStatus loading,
|
||||
VerificationStatus success,
|
||||
) async {
|
||||
if (_disposed || _state.isLoading) return;
|
||||
final generation = ++_generation;
|
||||
_state = VerificationState(loading);
|
||||
notifyListeners();
|
||||
if (_disposed || generation != _generation) return;
|
||||
try {
|
||||
final result = await operation();
|
||||
if (_disposed || generation != _generation) return;
|
||||
_state = VerificationState(success, isSimulated: result.isSimulated);
|
||||
} catch (error) {
|
||||
if (_disposed || generation != _generation) return;
|
||||
final code = error is VerificationException
|
||||
? error.code
|
||||
: VerificationError.internal;
|
||||
// Send failures must not be presented as confirmation-token failures.
|
||||
_state = VerificationState(
|
||||
loading == VerificationStatus.sending
|
||||
? VerificationStatus.error
|
||||
: switch (code) {
|
||||
VerificationError.expired => VerificationStatus.expired,
|
||||
VerificationError.invalid => VerificationStatus.invalid,
|
||||
VerificationError.internal => VerificationStatus.error,
|
||||
},
|
||||
);
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
if (_disposed) return;
|
||||
++_generation;
|
||||
_state = const VerificationState(VerificationStatus.idle);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_disposed = true;
|
||||
++_generation;
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user