feat(firebase): add production config and push token refresh listener

This commit is contained in:
2026-04-17 03:07:57 +02:00
parent 973fc2490c
commit e83adbfdbf
10 changed files with 161 additions and 17 deletions

View File

@@ -0,0 +1,29 @@
{
"project_info": {
"project_number": "950566980029",
"project_id": "sf-platform-pro",
"storage_bucket": "sf-platform-pro.firebasestorage.app"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:950566980029:android:75a7c10b6259d09681aad4",
"android_client_info": {
"package_name": "com.savefamily.app"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyDkjNdOAK0ype7wgdgiC1BCKV_pP4s_mlA"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
}
],
"configuration_version": "1"
}

View File

@@ -1,6 +1,6 @@
{
"env": "production",
"apiBaseUrl": "https://api-neki-b2b.neki.es/gateway/api/",
"apiOrigin": "https://neki-b2b.neki.es",
"wsUrl": "wss://api-neki-b2b.neki.es/websocket"
}
"apiBaseUrl": "https://platform.savefamily.app/gateway/api/",
"apiOrigin": "https://api-platform.savefamily.app/",
"wsUrl": "wss://api-platform.savefamily.app/websocket"
}

View File

@@ -1 +1 @@
{"flutter":{"platforms":{"android":{"default":{"projectId":"sf-platform-pre","appId":"1:535646668726:android:b87245b807258e3e5e6317","fileOutput":"android/app/google-services.json"}},"ios":{"default":{"projectId":"sf-platform-pre","appId":"1:535646668726:ios:5172d626d02dfe215e6317","uploadDebugSymbols":true,"fileOutput":"ios/Runner/GoogleService-Info.plist"}},"dart":{"lib/firebase_options_dev.dart":{"projectId":"sf-platform-pre","configurations":{"android":"1:535646668726:android:c3a09d6c26f0cdf95e6317","ios":"1:535646668726:ios:524afa641f61d7cb5e6317"}},"lib/firebase_options_staging.dart":{"projectId":"sf-platform-pre","configurations":{"android":"1:535646668726:android:b87245b807258e3e5e6317","ios":"1:535646668726:ios:5172d626d02dfe215e6317"}}}}}}
{"flutter":{"platforms":{"android":{"default":{"projectId":"sf-platform-pre","appId":"1:535646668726:android:b87245b807258e3e5e6317","fileOutput":"android/app/google-services.json"}},"ios":{"default":{"projectId":"sf-platform-pre","appId":"1:535646668726:ios:5172d626d02dfe215e6317","uploadDebugSymbols":true,"fileOutput":"ios/Runner/GoogleService-Info.plist"}},"dart":{"lib/firebase_options_dev.dart":{"projectId":"sf-platform-pre","configurations":{"android":"1:535646668726:android:c3a09d6c26f0cdf95e6317","ios":"1:535646668726:ios:524afa641f61d7cb5e6317"}},"lib/firebase_options_staging.dart":{"projectId":"sf-platform-pre","configurations":{"android":"1:535646668726:android:b87245b807258e3e5e6317","ios":"1:535646668726:ios:5172d626d02dfe215e6317"}},"lib/firebase_options_prod.dart":{"projectId":"sf-platform-pro","configurations":{"android":"1:950566980029:android:75a7c10b6259d09681aad4","ios":"1:950566980029:ios:987b4f0b9e9b897481aad4"}}}}}}

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>API_KEY</key>
<string>AIzaSyC0_d7Z6uVHHKhaf7JHRROaY6g2mvvpOXU</string>
<key>GCM_SENDER_ID</key>
<string>950566980029</string>
<key>PLIST_VERSION</key>
<string>1</string>
<key>BUNDLE_ID</key>
<string>com.savefamily.app</string>
<key>PROJECT_ID</key>
<string>sf-platform-pro</string>
<key>STORAGE_BUCKET</key>
<string>sf-platform-pro.firebasestorage.app</string>
<key>IS_ADS_ENABLED</key>
<false></false>
<key>IS_ANALYTICS_ENABLED</key>
<false></false>
<key>IS_APPINVITE_ENABLED</key>
<true></true>
<key>IS_GCM_ENABLED</key>
<true></true>
<key>IS_SIGNIN_ENABLED</key>
<true></true>
<key>GOOGLE_APP_ID</key>
<string>1:950566980029:ios:987b4f0b9e9b897481aad4</string>
</dict>
</plist>

View File

@@ -7,6 +7,7 @@ import 'package:flutter/foundation.dart';
import '../config/env/environment_enum.dart';
import '../firebase_options_dev.dart' as dev_options;
import '../firebase_options_prod.dart' as prod_options;
import '../firebase_options_staging.dart' as staging_options;
import 'app_version_check/remote_config_keys.dart';
@@ -18,12 +19,7 @@ Future<void> setupFirebase(EnvironmentEnum env) async {
case EnvironmentEnum.staging:
options = staging_options.DefaultFirebaseOptions.currentPlatform;
case EnvironmentEnum.production:
// TODO: replace with prod_options.DefaultFirebaseOptions.currentPlatform
// once the production Firebase project is created.
throw UnsupportedError(
'Production Firebase project is not configured yet. '
'Run `flutterfire configure --project=<prod-project-id>` and import it here.',
);
options = prod_options.DefaultFirebaseOptions.currentPlatform;
}
await Firebase.initializeApp(options: options);
@@ -54,7 +50,7 @@ Future<void> setupFirebase(EnvironmentEnum env) async {
RemoteConfigKeys.latestBuild: 0,
RemoteConfigKeys.updateForce: false,
RemoteConfigKeys.updateMessage: '',
RemoteConfigKeys.updateUrlIos: 'https://apps.apple.com/app/id6759873957',
RemoteConfigKeys.updateUrlIos: 'https://apps.apple.com/app/id6759875039',
RemoteConfigKeys.updateUrlAndroid:
'https://play.google.com/store/apps/details?id=com.savefamily.app',
});

View File

@@ -56,11 +56,6 @@ Future<void> setupNotifications() async {
_onMessageOpenedApp(initialMessage);
}
// TODO: integrate with backend (Treezor/SaveFamily api).
messaging.onTokenRefresh.listen((newToken) {
debugPrint('[FCM] token refreshed: $newToken');
});
try {
final token = await messaging.getToken();
debugPrint('[FCM] initial token: $token');

View File

@@ -0,0 +1,68 @@
// File generated by FlutterFire CLI.
// ignore_for_file: type=lint
import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
import 'package:flutter/foundation.dart'
show defaultTargetPlatform, kIsWeb, TargetPlatform;
/// Default [FirebaseOptions] for use with your Firebase apps.
///
/// Example:
/// ```dart
/// import 'firebase_options_prod.dart';
/// // ...
/// await Firebase.initializeApp(
/// options: DefaultFirebaseOptions.currentPlatform,
/// );
/// ```
class DefaultFirebaseOptions {
static FirebaseOptions get currentPlatform {
if (kIsWeb) {
throw UnsupportedError(
'DefaultFirebaseOptions have not been configured for web - '
'you can reconfigure this by running the FlutterFire CLI again.',
);
}
switch (defaultTargetPlatform) {
case TargetPlatform.android:
return android;
case TargetPlatform.iOS:
return ios;
case TargetPlatform.macOS:
throw UnsupportedError(
'DefaultFirebaseOptions have not been configured for macos - '
'you can reconfigure this by running the FlutterFire CLI again.',
);
case TargetPlatform.windows:
throw UnsupportedError(
'DefaultFirebaseOptions have not been configured for windows - '
'you can reconfigure this by running the FlutterFire CLI again.',
);
case TargetPlatform.linux:
throw UnsupportedError(
'DefaultFirebaseOptions have not been configured for linux - '
'you can reconfigure this by running the FlutterFire CLI again.',
);
default:
throw UnsupportedError(
'DefaultFirebaseOptions are not supported for this platform.',
);
}
}
static const FirebaseOptions android = FirebaseOptions(
apiKey: 'AIzaSyDkjNdOAK0ype7wgdgiC1BCKV_pP4s_mlA',
appId: '1:950566980029:android:75a7c10b6259d09681aad4',
messagingSenderId: '950566980029',
projectId: 'sf-platform-pro',
storageBucket: 'sf-platform-pro.firebasestorage.app',
);
static const FirebaseOptions ios = FirebaseOptions(
apiKey: 'AIzaSyC0_d7Z6uVHHKhaf7JHRROaY6g2mvvpOXU',
appId: '1:950566980029:ios:987b4f0b9e9b897481aad4',
messagingSenderId: '950566980029',
projectId: 'sf-platform-pro',
storageBucket: 'sf-platform-pro.firebasestorage.app',
iosBundleId: 'com.savefamily.app',
);
}

View File

@@ -1,4 +1,5 @@
import 'package:auth/auth.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@@ -72,6 +73,9 @@ class SaveFamilyAppState extends ConsumerState<SaveFamilyApp>
_walletHeartbeat?.stop();
_legacyHeartbeat?.stop();
_webSocket?.disconnect();
FirebaseMessaging.instance.deleteToken().catchError((Object e) {
debugPrint('[FCM] deleteToken on logout failed: $e');
});
};
}
@@ -124,6 +128,7 @@ class SaveFamilyAppState extends ConsumerState<SaveFamilyApp>
@override
Widget build(BuildContext context) {
SizeUtils.init(context: context);
ref.watch(pushTokenRefreshListenerProvider);
return AppUpdateGate(
child: MaterialApp.router(

View File

@@ -32,6 +32,7 @@ export 'src/data/datasource/user_remote_datasource_impl.dart';
export 'src/data/datasource/notifications_remote_datasource.dart';
export 'src/data/datasource/notifications_remote_datasource_impl.dart';
export 'src/providers/notifications_remote_datasource_provider.dart';
export 'src/providers/push_token_refresh_listener_provider.dart';
export 'src/domain/entities/beneficiary_validation_entity.dart';
export 'src/domain/entities/transaction_beneficiary_entity.dart';
export 'src/data/models/payout_beneficiary_model.dart';

View File

@@ -0,0 +1,20 @@
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'notifications_remote_datasource_provider.dart';
final pushTokenRefreshListenerProvider = Provider<void>((ref) {
final datasource = ref.watch(notificationsRemoteDatasourceProvider);
final subscription = FirebaseMessaging.instance.onTokenRefresh.listen((
newToken,
) async {
try {
await datasource.registerPushToken(newToken);
debugPrint('[FCM] token refreshed and re-registered: $newToken');
} catch (e) {
debugPrint('[FCM] token refresh re-register failed: $e');
}
});
ref.onDispose(subscription.cancel);
});