srs/lib/core/providers.dart
Aswin Biju 45fcdcebfb Initial commit
Add SRS app: results, shops, orders and users, wired to srs-api
Flutter web/PWA for the lottery agency, built on Riverpod and go_router,
talking to srs-api over HTTP (HttpApi, token kept in SharedPreferences).

- Results, Shops, Orders and Users tabs (Users for admins only), with
  phone, tablet and desktop layouts and shared widgets and theme tokens
- Users: list with search and Active/Inactive filters; user detail with
  shops, sign-in details, and an Access card
- WhatsApp results switch on each user (sendResults on
  PATCH /api/users/:id). Shows the WhatsApp logo (Simple Icons, CC0,
  drawn with flutter_svg), green only when results will really go out.
  Can't be turned on without a phone number ("Add a phone number first"
  opens the phone sheet), and is paused while the user is inactive. The
  users list marks people who get results with the logo.
- Each Access switch has a one-line description; the explanation
  paragraphs are gone
- Remove demo mode: the app always uses the real AP
  test/support and is used only by tests.
2026-11-04 23:21:50 +05:30

123 lines
3.1 KiB
Dart

import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'api.dart';
import 'format.dart';
import 'models.dart';
final apiProvider = Provider<Api>((ref) => throw UnimplementedError());
final startDateProvider = Provider<DateTime>(
(ref) => throw UnimplementedError(),
);
class DataVersion extends Notifier<int> {
@override
int build() => 0;
void bump() => state++;
}
final dataVersionProvider = NotifierProvider<DataVersion, int>(DataVersion.new);
class SessionNotifier extends Notifier<AppUser?> {
SessionNotifier(this.initial);
final AppUser? initial;
@override
AppUser? build() {
final sub = ref.read(apiProvider).sessionEnded.listen((_) => _end());
ref.onDispose(sub.cancel);
return initial;
}
Future<void> login(String username, String password) async {
final api = ref.read(apiProvider);
final u = await api.login(username, password);
state = u;
try {
ref.read(selectedDateProvider.notifier).set(await api.latestDrawDate());
} on ApiException {// ignore: empty_catches
}
}
Future<void> logout() async {
ref.read(apiProvider).logout();
_end();
}
void _end() {
state = null;
ref.invalidate(selectedDateProvider);
}
}
final sessionProvider = NotifierProvider<SessionNotifier, AppUser?>(
() => SessionNotifier(null),
);
class SelectedDate extends Notifier<DateTime> {
@override
DateTime build() => ref.read(startDateProvider);
void set(DateTime d) => state = dateOnly(d);
}
final selectedDateProvider = NotifierProvider<SelectedDate, DateTime>(
SelectedDate.new,
);
final resultsProvider = FutureProvider.family<DrawResults, DateTime>((ref, d) {
ref.watch(dataVersionProvider);
return ref.read(apiProvider).results(d);
});
final shopsProvider = FutureProvider.family<List<ShopDay>, DateTime>((ref, d) {
ref.watch(dataVersionProvider);
return ref.read(apiProvider).shops(d);
});
final shopDetailProvider = FutureProvider.family<ShopDetail, (int, DateTime)>((
ref,
k,
) {
ref.watch(dataVersionProvider);
return ref.read(apiProvider).shopDetail(k.$1, k.$2);
});
final ordersProvider = FutureProvider.family<List<Order>, DateTime>((ref, d) {
ref.watch(dataVersionProvider);
return ref.read(apiProvider).orders(d);
});
final orderProvider = FutureProvider.family<Order, int>((ref, id) {
ref.watch(dataVersionProvider);
return ref.read(apiProvider).order(id);
});
final monthProvider = FutureProvider.family<List<DrawInfo>, (int, int)>((
ref,
k,
) {
ref.watch(dataVersionProvider);
return ref.read(apiProvider).month(k.$1, k.$2);
});
final activeShopsProvider = FutureProvider<List<Shop>>((ref) {
ref.watch(dataVersionProvider);
return ref.read(apiProvider).activeShops();
});
final usersProvider = FutureProvider<List<AppUser>>((ref) {
ref.watch(dataVersionProvider);
return ref.read(apiProvider).users();
});
final userProvider = FutureProvider.family<AppUser, int>((ref, id) {
ref.watch(dataVersionProvider);
return ref.read(apiProvider).user(id);
});
Future<T> mutate<T>(WidgetRef ref, Future<T> Function() fn) async {
final r = await fn();
ref.read(dataVersionProvider.notifier).bump();
return r;
}