Skip to content

Commit 7fbf23b

Browse files
committed
feat(config): gate Firebase behind WEBTRIT_APP_FIREBASE_ENABLED
Add a WEBTRIT_APP_FIREBASE_ENABLED config (default true, runtime-overridable via EnvRegistry) that gates every Firebase touchpoint in bootstrap: Firebase app, messaging and local pushes init, the app-id provider (FirebaseAppIdProvider vs SharedPreferencesAppIdProvider), and Remote Config (falls back to the local shared-preferences cache when off). The analytics repository is now provided lazily and its navigator observer is only attached when the flag is on, so the app never touches FirebaseAnalytics.instance when Firebase is disabled. This lets a host that owns the default Firebase app - the theme configurator's realtime preview - run the embedded app Firebase-free by configuring the flag off, while standalone builds default to true and behave exactly as before.
1 parent eb55858 commit 7fbf23b

7 files changed

Lines changed: 59 additions & 9 deletions

File tree

dart_define.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
{
2+
"WEBTRIT_APP_FIREBASE_ENABLED": true,
3+
"__WEBTRIT_APP_FIREBASE_ENABLED_DESCRIPTION": "Whether Firebase (app, messaging, local push, Remote Config, Installations, Analytics) is initialised. Default true. Disable when the app is embedded in a host that owns the default Firebase app (e.g. the theme configurator's realtime preview) so the embedded app runs Firebase-free.",
24
"WEBTRIT_APP_DATABASE_LOG_STATEMENTS": false,
35
"_WEBTRIT_APP_CORE_URL": "",
46
"WEBTRIT_APP_DEMO_CORE_URL": "http://192.168.10.100:4000",

docs/environment.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ maintainability and enabling tools to parse configuration reliably.
3636
- `WEBTRIT_APP_DEMO_CORE_URL` – Demo core URL (_default: **http://localhost:4000**_).
3737
- `WEBTRIT_APP_DATABASE_LOG_STATEMENTS` – Enables logging of database queries (
3838
_default: **false**_).
39+
- `WEBTRIT_APP_FIREBASE_ENABLED` – Whether Firebase is initialised (_default: **true**_). Set to
40+
`false` to run embedded in a host that owns the default Firebase app (configurator realtime preview).
3941
- `_WEBTRIT_APP_CORE_URL` – Custom core URL (optional override).
4042
- `_WEBTRIT_APP_CORE_VERSION_CONSTRAINT` – Core compatibility range.
4143
- `_WEBTRIT_APP_ABOUT_URL` – URL for "About" screen content.

lib/app/view/app.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ class _AppState extends State<App> {
147147
deepLinkBuilder: isDeepLinkEnabled ? appRouter.deepLinkBuilder : null,
148148
navigatorObservers: () => [
149149
AppRouterObserver(),
150-
context.read<AppAnalyticsRepository>().createObserver(),
150+
if (EnvironmentConfig.FIREBASE_ENABLED) context.read<AppAnalyticsRepository>().createObserver(),
151151
AutoRouteObserver(),
152152
],
153153
reevaluateListenable: ReevaluateListenable.stream(

lib/bootstrap.dart

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,17 +42,27 @@ IsolateContext? _isolateContext;
4242
Future<InstanceRegistry> bootstrap() async {
4343
final registry = InstanceRegistry();
4444

45-
// External SDKs (Side effects only, don't need registration)
46-
await _initFirebaseApp();
47-
await _initFirebaseMessaging();
48-
await _initLocalPushs();
45+
// External SDKs (Side effects only, don't need registration). Gated by the
46+
// WEBTRIT_APP_FIREBASE_ENABLED config so a host that owns the default Firebase
47+
// app (e.g. the theme configurator's realtime preview) can run this app
48+
// Firebase-free by configuring that flag off.
49+
final firebaseEnabled = EnvironmentConfig.FIREBASE_ENABLED;
50+
if (firebaseEnabled) {
51+
await _initFirebaseApp();
52+
await _initFirebaseMessaging();
53+
await _initLocalPushs();
54+
}
4955

5056
// Initialize Components
5157

5258
// App Info & Device Data
5359

5460
final packageInfo = await PackageInfoFactory.init();
55-
final appInfo = await AppInfo.init(FirebaseAppIdProvider());
61+
// FirebaseAppIdProvider uses Firebase Installations; without Firebase use the
62+
// local shared-preferences id provider instead.
63+
final appInfo = await AppInfo.init(
64+
firebaseEnabled ? FirebaseAppIdProvider() : const SharedPreferencesAppIdProvider(),
65+
);
5666
final deviceInfo = await DeviceInfoFactory.init();
5767

5868
// Storages
@@ -97,9 +107,21 @@ Future<InstanceRegistry> bootstrap() async {
97107
apiClientFactory: apiClientFactory,
98108
);
99109

100-
// Remote configuration
110+
// Remote configuration. Firebase Remote Config needs the Firebase app, so when
111+
// Firebase is disabled fall back to the local shared-preferences cache
112+
// (DefaultRemoteCacheConfigService also implements RemoteConfigService).
101113
final remoteCacheConfigService = await DefaultRemoteCacheConfigService.init();
102-
final cachedRemoteConfigService = await CachedRemoteConfigService.init(remoteCacheConfigService);
114+
RemoteConfigService cachedRemoteConfigService;
115+
if (firebaseEnabled) {
116+
try {
117+
cachedRemoteConfigService = await CachedRemoteConfigService.init(remoteCacheConfigService);
118+
} catch (e, s) {
119+
Logger('bootstrap').warning('Firebase Remote Config init failed; using local cache fallback', e, s);
120+
cachedRemoteConfigService = remoteCacheConfigService;
121+
}
122+
} else {
123+
cachedRemoteConfigService = remoteCacheConfigService;
124+
}
103125

104126
final featureAccessStreamFactory = FeatureAccessStreamFactory(
105127
appThemes: appThemes,

lib/environment_config.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,14 @@ class EnvironmentConfig {
4747
return seconds > 0 ? seconds : compileTime;
4848
}
4949

50+
// Whether Firebase (app, messaging, local push, Remote Config, Installations,
51+
// Analytics) is initialised. Disable it when this app is embedded in a host
52+
// that owns the default Firebase app (the theme configurator's realtime
53+
// preview), so the embedded app runs Firebase-free.
54+
static const FIREBASE_ENABLED__NAME = 'WEBTRIT_APP_FIREBASE_ENABLED';
55+
static bool get FIREBASE_ENABLED =>
56+
_env.boolean(FIREBASE_ENABLED__NAME, const bool.fromEnvironment(FIREBASE_ENABLED__NAME, defaultValue: true));
57+
5058
static const DATABASE_LOG_STATEMENTS__NAME = 'WEBTRIT_APP_DATABASE_LOG_STATEMENTS';
5159
static bool get DATABASE_LOG_STATEMENTS => _env.boolean(
5260
DATABASE_LOG_STATEMENTS__NAME,

lib/main.dart

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,13 @@ class RootApp extends StatelessWidget {
147147
create: (_) => instanceRegistry.get(),
148148
dispose: disposeIfDisposable,
149149
),
150-
RepositoryProvider.value(value: AppAnalyticsRepository(instance: FirebaseAnalytics.instance)),
150+
// Lazy so the FirebaseAnalytics.instance handle is created only when the
151+
// analytics observer is actually attached. With WEBTRIT_APP_FIREBASE_ENABLED
152+
// false the observer is skipped (see App), so this provider is never read and
153+
// the embedded app stays Firebase-free.
154+
RepositoryProvider<AppAnalyticsRepository>(
155+
create: (_) => AppAnalyticsRepository(instance: FirebaseAnalytics.instance),
156+
),
151157
RepositoryProvider<RegisterStatusRepository>.value(value: registerStatusRepository),
152158
RepositoryProvider<PresenceSettingsRepository>.value(value: presenceSettingsRepository),
153159
RepositoryProvider<QueuedTerminationRequestsRepository>.value(value: queuedTerminationRequestsRepository),

test/environment_config_test.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,5 +91,15 @@ void main() {
9191
EnvironmentConfig.applyOverrides({name: '30'});
9292
expect(EnvironmentConfig.USER_REPOSITORY_POLLING_INTERVAL_SECONDS, 30);
9393
});
94+
95+
test('FIREBASE_ENABLED defaults to true and reflects a false override', () {
96+
expect(EnvironmentConfig.FIREBASE_ENABLED, isTrue);
97+
98+
EnvironmentConfig.applyOverrides({EnvironmentConfig.FIREBASE_ENABLED__NAME: 'false'});
99+
expect(EnvironmentConfig.FIREBASE_ENABLED, isFalse);
100+
101+
EnvironmentConfig.clearOverrides();
102+
expect(EnvironmentConfig.FIREBASE_ENABLED, isTrue);
103+
});
94104
});
95105
}

0 commit comments

Comments
 (0)