You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
89 lines
3.4 KiB
Dart
89 lines
3.4 KiB
Dart
import 'dart:convert';
|
|
|
|
import 'package:firebase_remote_config/firebase_remote_config.dart';
|
|
import 'package:flutter/foundation.dart' show kDebugMode;
|
|
import 'package:flutter/services.dart' show rootBundle;
|
|
import 'package:hmg_patient_app_new/services/logger_service.dart';
|
|
|
|
/// Firebase Remote Config wrapper. Fully generic — the app never hardcodes
|
|
/// occasion-specific values here; it only knows about the `occasion_config`
|
|
/// remote key and reads whatever JSON is published on the Firebase console.
|
|
abstract class RemoteConfigService {
|
|
/// Must be called once at startup, before the first frame if possible.
|
|
Future<void> initialize();
|
|
|
|
/// Fetches the latest values from Firebase (respecting the configured
|
|
/// minimum fetch interval) and activates them.
|
|
Future<bool> fetchAndActivate();
|
|
|
|
/// Raw JSON array string currently held for the `occasion_config` key —
|
|
/// a list of scheduled occasions (bundled default `[]` until the first
|
|
/// successful fetch/activate).
|
|
String get occasionConfigRawJson;
|
|
}
|
|
|
|
class RemoteConfigServiceImp implements RemoteConfigService {
|
|
static const String occasionConfigKey = 'occasion_config';
|
|
static const String _defaultConfigAssetPath = 'assets/json/default_occasion_config.json';
|
|
|
|
final LoggerService loggerService;
|
|
final FirebaseRemoteConfig _remoteConfig;
|
|
|
|
RemoteConfigServiceImp({
|
|
required this.loggerService,
|
|
FirebaseRemoteConfig? remoteConfig,
|
|
}) : _remoteConfig = remoteConfig ?? FirebaseRemoteConfig.instance;
|
|
|
|
@override
|
|
Future<void> initialize() async {
|
|
try {
|
|
await _remoteConfig.setConfigSettings(
|
|
RemoteConfigSettings(
|
|
fetchTimeout: const Duration(seconds: 10),
|
|
// In debug builds, always fetch fresh values so you can test
|
|
// Remote Config changes instantly without waiting/throttling.
|
|
// Release/profile builds keep a 1-hour minimum interval —
|
|
// balances timely occasion-theme rollout against not hammering
|
|
// Remote Config across the whole user base.
|
|
minimumFetchInterval: kDebugMode ? Duration.zero : const Duration(hours: 1),
|
|
),
|
|
);
|
|
|
|
// Bundled fallback so behaviour is deterministic (and identical to the
|
|
// current app look) before the very first successful remote fetch.
|
|
final defaultJson = await rootBundle.loadString(_defaultConfigAssetPath);
|
|
await _remoteConfig.setDefaults(<String, dynamic>{occasionConfigKey: defaultJson});
|
|
} catch (ex) {
|
|
loggerService.logError('RemoteConfigService.initialize error: $ex');
|
|
}
|
|
}
|
|
|
|
@override
|
|
Future<bool> fetchAndActivate() async {
|
|
try {
|
|
return await _remoteConfig.fetchAndActivate();
|
|
} catch (ex) {
|
|
loggerService.logError('RemoteConfigService.fetchAndActivate error: $ex');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
@override
|
|
String get occasionConfigRawJson {
|
|
try {
|
|
final value = _remoteConfig.getString(occasionConfigKey);
|
|
if (value.isEmpty) return _fallbackDisabledJson;
|
|
// Validate JSON shape early; if malformed, fall back safely.
|
|
json.decode(value);
|
|
return value;
|
|
} catch (ex) {
|
|
loggerService.logError('RemoteConfigService.occasionConfigRawJson parse error: $ex');
|
|
return _fallbackDisabledJson;
|
|
}
|
|
}
|
|
|
|
/// Empty schedule — [OccasionConfigModel.resolveActive] treats this the
|
|
/// same as "no occasion currently active".
|
|
static const String _fallbackDisabledJson = '[]';
|
|
}
|