128 lines
4.0 KiB
Dart
128 lines
4.0 KiB
Dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
|
|
import '../core/permissions.dart';
|
|
|
|
/// Injected in `main()` once SharedPreferences has loaded.
|
|
final prefsProvider = Provider<SharedPreferences>(
|
|
(ref) => throw UnimplementedError('prefsProvider must be overridden'),
|
|
);
|
|
|
|
class SessionState {
|
|
const SessionState({
|
|
this.phone,
|
|
this.signedIn = false,
|
|
this.locationGranted = false,
|
|
this.notificationsGranted = false,
|
|
this.cameraGranted = false,
|
|
});
|
|
|
|
final String? phone;
|
|
final bool signedIn;
|
|
final bool locationGranted;
|
|
final bool notificationsGranted;
|
|
final bool cameraGranted;
|
|
|
|
/// The three-step permission run shown right after verification.
|
|
bool get permissionsDone =>
|
|
locationGranted && notificationsGranted && cameraGranted;
|
|
|
|
SessionState copyWith({
|
|
String? phone,
|
|
bool? signedIn,
|
|
bool? locationGranted,
|
|
bool? notificationsGranted,
|
|
bool? cameraGranted,
|
|
}) => SessionState(
|
|
phone: phone ?? this.phone,
|
|
signedIn: signedIn ?? this.signedIn,
|
|
locationGranted: locationGranted ?? this.locationGranted,
|
|
notificationsGranted: notificationsGranted ?? this.notificationsGranted,
|
|
cameraGranted: cameraGranted ?? this.cameraGranted,
|
|
);
|
|
}
|
|
|
|
class SessionController extends Notifier<SessionState> {
|
|
static const _kPhone = 'phone';
|
|
static const _kSignedIn = 'signed_in';
|
|
static const _kLocation = 'perm_location';
|
|
static const _kNotifications = 'perm_notifications';
|
|
static const _kCamera = 'perm_camera';
|
|
|
|
SharedPreferences get _prefs => ref.read(prefsProvider);
|
|
|
|
@override
|
|
SessionState build() {
|
|
final p = _prefs;
|
|
return SessionState(
|
|
phone: p.getString(_kPhone),
|
|
signedIn: p.getBool(_kSignedIn) ?? false,
|
|
locationGranted: p.getBool(_kLocation) ?? false,
|
|
notificationsGranted: p.getBool(_kNotifications) ?? false,
|
|
cameraGranted: p.getBool(_kCamera) ?? false,
|
|
);
|
|
}
|
|
|
|
/// The demo backend accepts any 10-digit number and always sends `123456`.
|
|
static const demoCode = '123456';
|
|
|
|
String sendCode(String phone) {
|
|
_prefs.setString(_kPhone, phone);
|
|
state = state.copyWith(phone: phone);
|
|
return demoCode;
|
|
}
|
|
|
|
bool verifyCode(String code) {
|
|
if (code != demoCode) return false;
|
|
_prefs.setBool(_kSignedIn, true);
|
|
state = state.copyWith(signedIn: true);
|
|
return true;
|
|
}
|
|
|
|
/// Asks the OS, then records what it actually said.
|
|
///
|
|
/// The stored flag is a cache for the UI, never the source of truth — the
|
|
/// user can revoke a permission in system settings without the app running,
|
|
/// so [refresh] re-reads the real status whenever a screen needs it.
|
|
Future<bool> request(AppPermission permission) async {
|
|
final granted = await ref
|
|
.read(permissionServiceProvider)
|
|
.request(permission);
|
|
_record(permission, granted);
|
|
return granted;
|
|
}
|
|
|
|
/// Re-reads all three from the OS. Called when a screen that shows their
|
|
/// state appears, so a change made in system settings is picked up.
|
|
Future<void> refresh() async {
|
|
final service = ref.read(permissionServiceProvider);
|
|
for (final permission in AppPermission.values) {
|
|
_record(permission, await service.isGranted(permission));
|
|
}
|
|
}
|
|
|
|
void _record(AppPermission permission, bool granted) {
|
|
switch (permission) {
|
|
case AppPermission.location:
|
|
_prefs.setBool(_kLocation, granted);
|
|
state = state.copyWith(locationGranted: granted);
|
|
case AppPermission.notifications:
|
|
_prefs.setBool(_kNotifications, granted);
|
|
state = state.copyWith(notificationsGranted: granted);
|
|
case AppPermission.camera:
|
|
_prefs.setBool(_kCamera, granted);
|
|
state = state.copyWith(cameraGranted: granted);
|
|
}
|
|
}
|
|
|
|
Future<void> signOut() async {
|
|
await _prefs.remove(_kSignedIn);
|
|
await _prefs.remove(_kPhone);
|
|
state = state.copyWith(signedIn: false, phone: '');
|
|
}
|
|
}
|
|
|
|
final sessionProvider = NotifierProvider<SessionController, SessionState>(
|
|
SessionController.new,
|
|
);
|