171 lines
6.5 KiB
Dart
171 lines
6.5 KiB
Dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
|
|
import 'domain/models/server_config.dart';
|
|
import 'domain/models/sensor.dart';
|
|
import 'domain/models/floor.dart';
|
|
import 'domain/models/tag.dart';
|
|
import 'domain/models/particle.dart';
|
|
import 'domain/models/floor_plan_mode.dart';
|
|
import 'data/sources/local/credential_store.dart';
|
|
import 'data/sources/localiser/onboarding_client.dart';
|
|
import 'data/sources/localiser/session_client.dart';
|
|
import 'data/sources/localiser/floor_client.dart';
|
|
import 'data/sources/localiser/sensor_client.dart';
|
|
import 'data/sources/localiser/tag_client.dart';
|
|
import 'data/sources/localiser/realtime_data_client.dart';
|
|
import 'data/repositories/onboarding_repository.dart';
|
|
import 'data/repositories/sensor_repository.dart';
|
|
import 'data/repositories/tag_repository.dart';
|
|
import 'data/repositories/floor_plan_repository.dart';
|
|
import 'data/repositories/phoenix_onboarding_repository.dart';
|
|
import 'data/repositories/phoenix_sensor_repository.dart';
|
|
import 'data/repositories/phoenix_tag_repository.dart';
|
|
import 'data/repositories/phoenix_floor_plan_repository.dart';
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Connection / auth state — set imperatively after login
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/// The server the user has selected. Null until a server is chosen.
|
|
final serverConfigProvider = StateProvider<ServerConfig?>((ref) => null);
|
|
|
|
/// JWT returned by /api/session or /api/setup. Null until authenticated.
|
|
final authTokenProvider = StateProvider<String?>((ref) => null);
|
|
|
|
/// Live WebSocket connection. Null until [RealtimeDataClient.connect] succeeds.
|
|
final realtimeDataClientProvider =
|
|
StateProvider<RealtimeDataClient?>((ref) => null);
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Convenience
|
|
// ---------------------------------------------------------------------------
|
|
|
|
final credentialStoreProvider = Provider<CredentialStore>((ref) {
|
|
return CredentialStore();
|
|
});
|
|
|
|
/// MQTT broker settings stored locally on the device.
|
|
/// Null if not yet configured. Set via [CredentialStore.saveMqttBroker].
|
|
final mqttBrokerProvider = FutureProvider<({String host, int port})?>(
|
|
(ref) => ref.watch(credentialStoreProvider).loadMqttBroker(),
|
|
);
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Feature clients — throw if required state is missing
|
|
// ---------------------------------------------------------------------------
|
|
|
|
ServerConfig _requireConfig(Ref ref) {
|
|
final config = ref.watch(serverConfigProvider);
|
|
if (config == null) throw StateError('no server selected');
|
|
return config;
|
|
}
|
|
|
|
String _requireToken(Ref ref) {
|
|
final token = ref.watch(authTokenProvider);
|
|
if (token == null) throw StateError('not authenticated');
|
|
return token;
|
|
}
|
|
|
|
RealtimeDataClient _requireRealtime(Ref ref) {
|
|
final rt = ref.watch(realtimeDataClientProvider);
|
|
if (rt == null) throw StateError('realtime not connected');
|
|
return rt;
|
|
}
|
|
|
|
final onboardingClientProvider = Provider<OnboardingClient>((ref) {
|
|
return OnboardingClient(config: _requireConfig(ref));
|
|
});
|
|
|
|
final sessionClientProvider = Provider<SessionClient>((ref) {
|
|
return SessionClient(config: _requireConfig(ref));
|
|
});
|
|
|
|
final floorClientProvider = Provider<FloorClient>((ref) {
|
|
return FloorClient(config: _requireConfig(ref), token: _requireToken(ref));
|
|
});
|
|
|
|
final sensorClientProvider = Provider<SensorClient>((ref) {
|
|
return SensorClient(config: _requireConfig(ref), token: _requireToken(ref));
|
|
});
|
|
|
|
final tagClientProvider = Provider<TagClient>((ref) {
|
|
return TagClient(config: _requireConfig(ref), token: _requireToken(ref));
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Repositories
|
|
// ---------------------------------------------------------------------------
|
|
|
|
final onboardingRepositoryProvider = Provider<OnboardingRepository>((ref) {
|
|
return PhoenixOnboardingRepository(
|
|
client: ref.watch(onboardingClientProvider));
|
|
});
|
|
|
|
final sensorRepositoryProvider = Provider<SensorRepository>((ref) {
|
|
return PhoenixSensorRepository(client: ref.watch(sensorClientProvider));
|
|
});
|
|
|
|
final tagRepositoryProvider = Provider<TagRepository>((ref) {
|
|
return PhoenixTagRepository(
|
|
tagClient: ref.watch(tagClientProvider),
|
|
realtime: _requireRealtime(ref),
|
|
);
|
|
});
|
|
|
|
final floorRepositoryProvider = Provider<FloorRepository>((ref) {
|
|
return PhoenixFloorPlanRepository(client: ref.watch(floorClientProvider));
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Floor plan data
|
|
// ---------------------------------------------------------------------------
|
|
|
|
final floorProvider = FutureProvider.autoDispose<Floor?>((ref) =>
|
|
ref.watch(floorRepositoryProvider).getFirstFloor());
|
|
|
|
final roomsProvider = FutureProvider.autoDispose<List<Room>>((ref) async {
|
|
final floor = await ref.watch(floorProvider.future);
|
|
if (floor == null) return [];
|
|
return ref.watch(floorRepositoryProvider).getRooms(floor.id);
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Sensor data
|
|
// ---------------------------------------------------------------------------
|
|
|
|
final sensorsProvider = FutureProvider.autoDispose<List<Sensor>>((ref) {
|
|
return ref.watch(sensorRepositoryProvider).getSensors();
|
|
});
|
|
|
|
final sensorProvider =
|
|
FutureProvider.autoDispose.family<Sensor, int>((ref, id) {
|
|
return ref.watch(sensorRepositoryProvider).getSensor(id);
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Cross-tab UI state
|
|
// ---------------------------------------------------------------------------
|
|
|
|
final selectedSensorIdProvider = StateProvider<String?>((ref) => null);
|
|
|
|
/// Non-null while the user is placing a sensor on the floor plan via the
|
|
/// center-dot placement flow. Cleared on Place, Cancel, or back navigation.
|
|
final sensorPlacementProvider = StateProvider<Sensor?>((ref) => null);
|
|
|
|
final floorPlanModeProvider =
|
|
StateProvider<FloorPlanMode>((ref) => FloorPlanMode.view);
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Live data streams
|
|
// ---------------------------------------------------------------------------
|
|
|
|
final tagPositionsProvider = StreamProvider<List<TagPosition>>((ref) {
|
|
final repo = ref.watch(tagRepositoryProvider);
|
|
return repo.watchPositions();
|
|
});
|
|
|
|
final particleCloudProvider = StreamProvider<List<Particle>>((ref) {
|
|
final repo = ref.watch(tagRepositoryProvider);
|
|
return repo.watchParticleCloud();
|
|
});
|