feat: Upgrade Firebase Functions dependencies, enhance notification service with APNS support and FCM
This commit is contained in:
5
.firebaserc
Normal file
5
.firebaserc
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"projects": {
|
||||||
|
"default": "travelmate-a47f5"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,6 +22,7 @@ android {
|
|||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility = JavaVersion.VERSION_17
|
sourceCompatibility = JavaVersion.VERSION_17
|
||||||
targetCompatibility = JavaVersion.VERSION_17
|
targetCompatibility = JavaVersion.VERSION_17
|
||||||
|
isCoreLibraryDesugaringEnabled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
@@ -59,6 +60,10 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4")
|
||||||
|
}
|
||||||
|
|
||||||
flutter {
|
flutter {
|
||||||
source = "../.."
|
source = "../.."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1,37 @@
|
|||||||
{"flutter":{"platforms":{"android":{"default":{"projectId":"travelmate-a47f5","appId":"1:521527250907:android:be3db7fc84f053ec7da1fe","fileOutput":"android/app/google-services.json"}},"dart":{"lib/firebase_options.dart":{"projectId":"travelmate-a47f5","configurations":{"android":"1:521527250907:android:be3db7fc84f053ec7da1fe","ios":"1:521527250907:ios:64b41be39c54db1c7da1fe","windows":"1:521527250907:web:53ff98bcdb8c218f7da1fe"}}}}}}
|
{
|
||||||
|
"flutter": {
|
||||||
|
"platforms": {
|
||||||
|
"android": {
|
||||||
|
"default": {
|
||||||
|
"projectId": "travelmate-a47f5",
|
||||||
|
"appId": "1:521527250907:android:be3db7fc84f053ec7da1fe",
|
||||||
|
"fileOutput": "android/app/google-services.json"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dart": {
|
||||||
|
"lib/firebase_options.dart": {
|
||||||
|
"projectId": "travelmate-a47f5",
|
||||||
|
"configurations": {
|
||||||
|
"android": "1:521527250907:android:be3db7fc84f053ec7da1fe",
|
||||||
|
"ios": "1:521527250907:ios:64b41be39c54db1c7da1fe",
|
||||||
|
"windows": "1:521527250907:web:53ff98bcdb8c218f7da1fe"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"functions": [
|
||||||
|
{
|
||||||
|
"source": "functions",
|
||||||
|
"codebase": "default",
|
||||||
|
"disallowLegacyRuntimeConfig": true,
|
||||||
|
"ignore": [
|
||||||
|
"node_modules",
|
||||||
|
".git",
|
||||||
|
"firebase-debug.log",
|
||||||
|
"firebase-debug.*.log",
|
||||||
|
"*.local"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|||||||
2
functions/.gitignore
vendored
Normal file
2
functions/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
node_modules/
|
||||||
|
*.local
|
||||||
@@ -1,27 +1,14 @@
|
|||||||
const functions = require("firebase-functions");
|
const functions = require("firebase-functions/v1");
|
||||||
const admin = require("firebase-admin");
|
const admin = require("firebase-admin");
|
||||||
|
|
||||||
admin.initializeApp();
|
admin.initializeApp();
|
||||||
|
|
||||||
// Helper function to send notifications
|
// Helper function to send notifications to a list of users
|
||||||
async function sendNotificationToTripParticipants(tripId, title, body, excludeUserId) {
|
async function sendNotificationToUsers(userIds, title, body, excludeUserId, data = {}) {
|
||||||
try {
|
try {
|
||||||
const tripDoc = await admin.firestore().collection("trips").doc(tripId).get();
|
|
||||||
if (!tripDoc.exists) {
|
|
||||||
console.log(`Trip ${tripId} not found`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const trip = tripDoc.data();
|
|
||||||
const participants = trip.participants || [];
|
|
||||||
// Add creator if not in participants list (though usually they are)
|
|
||||||
if (trip.createdBy && !participants.includes(trip.createdBy)) {
|
|
||||||
participants.push(trip.createdBy);
|
|
||||||
}
|
|
||||||
|
|
||||||
const tokens = [];
|
const tokens = [];
|
||||||
|
|
||||||
for (const userId of participants) {
|
for (const userId of userIds) {
|
||||||
if (userId === excludeUserId) continue;
|
if (userId === excludeUserId) continue;
|
||||||
|
|
||||||
const userDoc = await admin.firestore().collection("users").doc(userId).get();
|
const userDoc = await admin.firestore().collection("users").doc(userId).get();
|
||||||
@@ -41,8 +28,8 @@ async function sendNotificationToTripParticipants(tripId, title, body, excludeUs
|
|||||||
},
|
},
|
||||||
tokens: tokens,
|
tokens: tokens,
|
||||||
data: {
|
data: {
|
||||||
tripId: tripId,
|
|
||||||
click_action: "FLUTTER_NOTIFICATION_CLICK",
|
click_action: "FLUTTER_NOTIFICATION_CLICK",
|
||||||
|
...data
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -55,13 +42,31 @@ async function sendNotificationToTripParticipants(tripId, title, body, excludeUs
|
|||||||
}
|
}
|
||||||
|
|
||||||
exports.onActivityCreated = functions.firestore
|
exports.onActivityCreated = functions.firestore
|
||||||
.document("trips/{tripId}/activities/{activityId}")
|
.document("activities/{activityId}")
|
||||||
.onCreate(async (snapshot, context) => {
|
.onCreate(async (snapshot, context) => {
|
||||||
const activity = snapshot.data();
|
const activity = snapshot.data();
|
||||||
const tripId = context.params.tripId;
|
const tripId = activity.tripId;
|
||||||
const createdBy = activity.createdBy || "Unknown"; // Assuming createdBy field exists
|
const createdBy = activity.createdBy || "Unknown";
|
||||||
|
|
||||||
// Fetch creator name if possible, otherwise use generic message
|
if (!tripId) {
|
||||||
|
console.log("No tripId found in activity");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch trip to get participants
|
||||||
|
const tripDoc = await admin.firestore().collection("trips").doc(tripId).get();
|
||||||
|
if (!tripDoc.exists) {
|
||||||
|
console.log(`Trip ${tripId} not found`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const trip = tripDoc.data();
|
||||||
|
const participants = trip.participants || [];
|
||||||
|
if (trip.createdBy && !participants.includes(trip.createdBy)) {
|
||||||
|
participants.push(trip.createdBy);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch creator name
|
||||||
let creatorName = "Quelqu'un";
|
let creatorName = "Quelqu'un";
|
||||||
if (createdBy !== "Unknown") {
|
if (createdBy !== "Unknown") {
|
||||||
const userDoc = await admin.firestore().collection("users").doc(createdBy).get();
|
const userDoc = await admin.firestore().collection("users").doc(createdBy).get();
|
||||||
@@ -70,56 +75,72 @@ exports.onActivityCreated = functions.firestore
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await sendNotificationToTripParticipants(
|
await sendNotificationToUsers(
|
||||||
tripId,
|
participants,
|
||||||
"Nouvelle activité !",
|
"Nouvelle activité !",
|
||||||
`${creatorName} a ajouté une nouvelle activité : ${activity.title}`,
|
`${creatorName} a ajouté une nouvelle activité : ${activity.name || activity.title}`,
|
||||||
createdBy
|
createdBy,
|
||||||
|
{ tripId: tripId }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
exports.onMessageCreated = functions.firestore
|
exports.onMessageCreated = functions.firestore
|
||||||
.document("trips/{tripId}/messages/{messageId}")
|
.document("groups/{groupId}/messages/{messageId}")
|
||||||
.onCreate(async (snapshot, context) => {
|
.onCreate(async (snapshot, context) => {
|
||||||
const message = snapshot.data();
|
const message = snapshot.data();
|
||||||
const tripId = context.params.tripId;
|
const groupId = context.params.groupId;
|
||||||
const senderId = message.senderId;
|
const senderId = message.senderId;
|
||||||
|
|
||||||
let senderName = "Quelqu'un";
|
// Fetch group to get members
|
||||||
if (senderId) {
|
const groupDoc = await admin.firestore().collection("groups").doc(groupId).get();
|
||||||
const userDoc = await admin.firestore().collection("users").doc(senderId).get();
|
if (!groupDoc.exists) {
|
||||||
if (userDoc.exists) {
|
console.log(`Group ${groupId} not found`);
|
||||||
senderName = userDoc.data().prenom || "Quelqu'un";
|
return;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await sendNotificationToTripParticipants(
|
const group = groupDoc.data();
|
||||||
tripId,
|
const memberIds = group.memberIds || [];
|
||||||
|
|
||||||
|
let senderName = message.senderName || "Quelqu'un";
|
||||||
|
|
||||||
|
await sendNotificationToUsers(
|
||||||
|
memberIds,
|
||||||
"Nouveau message",
|
"Nouveau message",
|
||||||
`${senderName} : ${message.content}`,
|
`${senderName} : ${message.text}`,
|
||||||
senderId
|
senderId,
|
||||||
|
{ groupId: groupId }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
exports.onExpenseCreated = functions.firestore
|
exports.onExpenseCreated = functions.firestore
|
||||||
.document("trips/{tripId}/expenses/{expenseId}")
|
.document("expenses/{expenseId}")
|
||||||
.onCreate(async (snapshot, context) => {
|
.onCreate(async (snapshot, context) => {
|
||||||
const expense = snapshot.data();
|
const expense = snapshot.data();
|
||||||
const tripId = context.params.tripId;
|
const groupId = expense.groupId;
|
||||||
const paidBy = expense.paidBy;
|
const paidBy = expense.paidById || expense.paidBy;
|
||||||
|
|
||||||
let payerName = "Quelqu'un";
|
if (!groupId) {
|
||||||
if (paidBy) {
|
console.log("No groupId found in expense");
|
||||||
const userDoc = await admin.firestore().collection("users").doc(paidBy).get();
|
return;
|
||||||
if (userDoc.exists) {
|
|
||||||
payerName = userDoc.data().prenom || "Quelqu'un";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await sendNotificationToTripParticipants(
|
// Fetch group to get members
|
||||||
tripId,
|
const groupDoc = await admin.firestore().collection("groups").doc(groupId).get();
|
||||||
|
if (!groupDoc.exists) {
|
||||||
|
console.log(`Group ${groupId} not found`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const group = groupDoc.data();
|
||||||
|
const memberIds = group.memberIds || [];
|
||||||
|
|
||||||
|
let payerName = expense.paidByName || "Quelqu'un";
|
||||||
|
|
||||||
|
await sendNotificationToUsers(
|
||||||
|
memberIds,
|
||||||
"Nouvelle dépense",
|
"Nouvelle dépense",
|
||||||
`${payerName} a ajouté une dépense : ${expense.amount} ${expense.currency || '€'}`,
|
`${payerName} a ajouté une dépense : ${expense.amount} ${expense.currency || '€'}`,
|
||||||
paidBy
|
paidBy,
|
||||||
|
{ groupId: groupId }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
11099
functions/package-lock.json
generated
11099
functions/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,25 +1,23 @@
|
|||||||
{
|
{
|
||||||
"name": "functions",
|
"name": "functions",
|
||||||
"description": "Cloud Functions for Travel Mate",
|
"description": "Cloud Functions for Firebase",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "eslint .",
|
"serve": "firebase emulators:start --only functions",
|
||||||
"serve": "firebase emulators:start --only functions",
|
"shell": "firebase functions:shell",
|
||||||
"shell": "firebase functions:shell",
|
"start": "npm run shell",
|
||||||
"start": "npm run shell",
|
"deploy": "firebase deploy --only functions",
|
||||||
"deploy": "firebase deploy --only functions",
|
"logs": "firebase functions:log"
|
||||||
"logs": "firebase functions:log"
|
},
|
||||||
},
|
"engines": {
|
||||||
"engines": {
|
"node": "20"
|
||||||
"node": "18"
|
},
|
||||||
},
|
"main": "index.js",
|
||||||
"main": "index.js",
|
"dependencies": {
|
||||||
"dependencies": {
|
"firebase-admin": "^13.6.0",
|
||||||
"firebase-admin": "^11.8.0",
|
"firebase-functions": "^7.0.0"
|
||||||
"firebase-functions": "^4.3.1"
|
},
|
||||||
},
|
"devDependencies": {
|
||||||
"devDependencies": {
|
"firebase-functions-test": "^3.4.1"
|
||||||
"eslint": "^8.15.0",
|
},
|
||||||
"eslint-config-google": "^0.14.0"
|
"private": true
|
||||||
},
|
|
||||||
"private": true
|
|
||||||
}
|
}
|
||||||
@@ -1216,6 +1216,9 @@ PODS:
|
|||||||
- Firebase/Firestore (12.4.0):
|
- Firebase/Firestore (12.4.0):
|
||||||
- Firebase/CoreOnly
|
- Firebase/CoreOnly
|
||||||
- FirebaseFirestore (~> 12.4.0)
|
- FirebaseFirestore (~> 12.4.0)
|
||||||
|
- Firebase/Messaging (12.4.0):
|
||||||
|
- Firebase/CoreOnly
|
||||||
|
- FirebaseMessaging (~> 12.4.0)
|
||||||
- Firebase/Storage (12.4.0):
|
- Firebase/Storage (12.4.0):
|
||||||
- Firebase/CoreOnly
|
- Firebase/CoreOnly
|
||||||
- FirebaseStorage (~> 12.4.0)
|
- FirebaseStorage (~> 12.4.0)
|
||||||
@@ -1223,9 +1226,13 @@ PODS:
|
|||||||
- Firebase/Auth (= 12.4.0)
|
- Firebase/Auth (= 12.4.0)
|
||||||
- firebase_core
|
- firebase_core
|
||||||
- Flutter
|
- Flutter
|
||||||
- firebase_core (4.2.0):
|
- firebase_core (4.2.1):
|
||||||
- Firebase/CoreOnly (= 12.4.0)
|
- Firebase/CoreOnly (= 12.4.0)
|
||||||
- Flutter
|
- Flutter
|
||||||
|
- firebase_messaging (16.0.4):
|
||||||
|
- Firebase/Messaging (= 12.4.0)
|
||||||
|
- firebase_core
|
||||||
|
- Flutter
|
||||||
- firebase_storage (13.0.3):
|
- firebase_storage (13.0.3):
|
||||||
- Firebase/Storage (= 12.4.0)
|
- Firebase/Storage (= 12.4.0)
|
||||||
- firebase_core
|
- firebase_core
|
||||||
@@ -1269,6 +1276,20 @@ PODS:
|
|||||||
- gRPC-Core (~> 1.69.0)
|
- gRPC-Core (~> 1.69.0)
|
||||||
- leveldb-library (~> 1.22)
|
- leveldb-library (~> 1.22)
|
||||||
- nanopb (~> 3.30910.0)
|
- nanopb (~> 3.30910.0)
|
||||||
|
- FirebaseInstallations (12.4.0):
|
||||||
|
- FirebaseCore (~> 12.4.0)
|
||||||
|
- GoogleUtilities/Environment (~> 8.1)
|
||||||
|
- GoogleUtilities/UserDefaults (~> 8.1)
|
||||||
|
- PromisesObjC (~> 2.4)
|
||||||
|
- FirebaseMessaging (12.4.0):
|
||||||
|
- FirebaseCore (~> 12.4.0)
|
||||||
|
- FirebaseInstallations (~> 12.4.0)
|
||||||
|
- GoogleDataTransport (~> 10.1)
|
||||||
|
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
|
||||||
|
- GoogleUtilities/Environment (~> 8.1)
|
||||||
|
- GoogleUtilities/Reachability (~> 8.1)
|
||||||
|
- GoogleUtilities/UserDefaults (~> 8.1)
|
||||||
|
- nanopb (~> 3.30910.0)
|
||||||
- FirebaseSharedSwift (12.4.0)
|
- FirebaseSharedSwift (12.4.0)
|
||||||
- FirebaseStorage (12.4.0):
|
- FirebaseStorage (12.4.0):
|
||||||
- FirebaseAppCheckInterop (~> 12.4.0)
|
- FirebaseAppCheckInterop (~> 12.4.0)
|
||||||
@@ -1278,6 +1299,8 @@ PODS:
|
|||||||
- GoogleUtilities/Environment (~> 8.1)
|
- GoogleUtilities/Environment (~> 8.1)
|
||||||
- GTMSessionFetcher/Core (< 6.0, >= 3.4)
|
- GTMSessionFetcher/Core (< 6.0, >= 3.4)
|
||||||
- Flutter (1.0.0)
|
- Flutter (1.0.0)
|
||||||
|
- flutter_local_notifications (0.0.1):
|
||||||
|
- Flutter
|
||||||
- geolocator_apple (1.2.0):
|
- geolocator_apple (1.2.0):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
@@ -1292,6 +1315,9 @@ PODS:
|
|||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- GoogleSignIn (~> 9.0)
|
- GoogleSignIn (~> 9.0)
|
||||||
- GTMSessionFetcher (>= 3.4.0)
|
- GTMSessionFetcher (>= 3.4.0)
|
||||||
|
- GoogleDataTransport (10.1.0):
|
||||||
|
- nanopb (~> 3.30910.0)
|
||||||
|
- PromisesObjC (~> 2.4)
|
||||||
- GoogleMaps (9.4.0):
|
- GoogleMaps (9.4.0):
|
||||||
- GoogleMaps/Maps (= 9.4.0)
|
- GoogleMaps/Maps (= 9.4.0)
|
||||||
- GoogleMaps/Maps (9.4.0)
|
- GoogleMaps/Maps (9.4.0)
|
||||||
@@ -1456,8 +1482,10 @@ DEPENDENCIES:
|
|||||||
- cloud_firestore (from `.symlinks/plugins/cloud_firestore/ios`)
|
- cloud_firestore (from `.symlinks/plugins/cloud_firestore/ios`)
|
||||||
- firebase_auth (from `.symlinks/plugins/firebase_auth/ios`)
|
- firebase_auth (from `.symlinks/plugins/firebase_auth/ios`)
|
||||||
- firebase_core (from `.symlinks/plugins/firebase_core/ios`)
|
- firebase_core (from `.symlinks/plugins/firebase_core/ios`)
|
||||||
|
- firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`)
|
||||||
- firebase_storage (from `.symlinks/plugins/firebase_storage/ios`)
|
- firebase_storage (from `.symlinks/plugins/firebase_storage/ios`)
|
||||||
- Flutter (from `Flutter`)
|
- Flutter (from `Flutter`)
|
||||||
|
- flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
|
||||||
- geolocator_apple (from `.symlinks/plugins/geolocator_apple/darwin`)
|
- geolocator_apple (from `.symlinks/plugins/geolocator_apple/darwin`)
|
||||||
- google_maps_flutter_ios (from `.symlinks/plugins/google_maps_flutter_ios/ios`)
|
- google_maps_flutter_ios (from `.symlinks/plugins/google_maps_flutter_ios/ios`)
|
||||||
- google_sign_in_ios (from `.symlinks/plugins/google_sign_in_ios/darwin`)
|
- google_sign_in_ios (from `.symlinks/plugins/google_sign_in_ios/darwin`)
|
||||||
@@ -1485,9 +1513,12 @@ SPEC REPOS:
|
|||||||
- FirebaseCoreInternal
|
- FirebaseCoreInternal
|
||||||
- FirebaseFirestore
|
- FirebaseFirestore
|
||||||
- FirebaseFirestoreInternal
|
- FirebaseFirestoreInternal
|
||||||
|
- FirebaseInstallations
|
||||||
|
- FirebaseMessaging
|
||||||
- FirebaseSharedSwift
|
- FirebaseSharedSwift
|
||||||
- FirebaseStorage
|
- FirebaseStorage
|
||||||
- Google-Maps-iOS-Utils
|
- Google-Maps-iOS-Utils
|
||||||
|
- GoogleDataTransport
|
||||||
- GoogleMaps
|
- GoogleMaps
|
||||||
- GoogleSignIn
|
- GoogleSignIn
|
||||||
- GoogleUtilities
|
- GoogleUtilities
|
||||||
@@ -1507,10 +1538,14 @@ EXTERNAL SOURCES:
|
|||||||
:path: ".symlinks/plugins/firebase_auth/ios"
|
:path: ".symlinks/plugins/firebase_auth/ios"
|
||||||
firebase_core:
|
firebase_core:
|
||||||
:path: ".symlinks/plugins/firebase_core/ios"
|
:path: ".symlinks/plugins/firebase_core/ios"
|
||||||
|
firebase_messaging:
|
||||||
|
:path: ".symlinks/plugins/firebase_messaging/ios"
|
||||||
firebase_storage:
|
firebase_storage:
|
||||||
:path: ".symlinks/plugins/firebase_storage/ios"
|
:path: ".symlinks/plugins/firebase_storage/ios"
|
||||||
Flutter:
|
Flutter:
|
||||||
:path: Flutter
|
:path: Flutter
|
||||||
|
flutter_local_notifications:
|
||||||
|
:path: ".symlinks/plugins/flutter_local_notifications/ios"
|
||||||
geolocator_apple:
|
geolocator_apple:
|
||||||
:path: ".symlinks/plugins/geolocator_apple/darwin"
|
:path: ".symlinks/plugins/geolocator_apple/darwin"
|
||||||
google_maps_flutter_ios:
|
google_maps_flutter_ios:
|
||||||
@@ -1542,7 +1577,8 @@ SPEC CHECKSUMS:
|
|||||||
cloud_firestore: 79014bb3b303d451717ed5fe69fded8a2b2e8dc2
|
cloud_firestore: 79014bb3b303d451717ed5fe69fded8a2b2e8dc2
|
||||||
Firebase: f07b15ae5a6ec0f93713e30b923d9970d144af3e
|
Firebase: f07b15ae5a6ec0f93713e30b923d9970d144af3e
|
||||||
firebase_auth: c2b8be95d602d4e8a9148fae72333ef78e69cc20
|
firebase_auth: c2b8be95d602d4e8a9148fae72333ef78e69cc20
|
||||||
firebase_core: 744984dbbed8b3036abf34f0b98d80f130a7e464
|
firebase_core: f1aafb21c14f497e5498f7ffc4dc63cbb52b2594
|
||||||
|
firebase_messaging: c17a29984eafce4b2997fe078bb0a9e0b06f5dde
|
||||||
firebase_storage: 0ba617a05b24aec050395e4d5d3773c0d7518a15
|
firebase_storage: 0ba617a05b24aec050395e4d5d3773c0d7518a15
|
||||||
FirebaseAppCheckInterop: f734c802f21fe1da0837708f0f9a27218c8a4ed0
|
FirebaseAppCheckInterop: f734c802f21fe1da0837708f0f9a27218c8a4ed0
|
||||||
FirebaseAuth: 4a2aed737c84114a9d9b33d11ae1b147d6b94889
|
FirebaseAuth: 4a2aed737c84114a9d9b33d11ae1b147d6b94889
|
||||||
@@ -1552,13 +1588,17 @@ SPEC CHECKSUMS:
|
|||||||
FirebaseCoreInternal: d7f5a043c2cd01a08103ab586587c1468047bca6
|
FirebaseCoreInternal: d7f5a043c2cd01a08103ab586587c1468047bca6
|
||||||
FirebaseFirestore: 2a6183381cf7679b1bb000eb76a8e3178e25dee2
|
FirebaseFirestore: 2a6183381cf7679b1bb000eb76a8e3178e25dee2
|
||||||
FirebaseFirestoreInternal: 6577a27cd5dc3722b900042527f86d4ea1626134
|
FirebaseFirestoreInternal: 6577a27cd5dc3722b900042527f86d4ea1626134
|
||||||
|
FirebaseInstallations: ae9f4902cb5bf1d0c5eaa31ec1f4e5495a0714e2
|
||||||
|
FirebaseMessaging: d33971b7bb252745ea6cd31ab190d1a1df4b8ed5
|
||||||
FirebaseSharedSwift: 93426a1de92f19e1199fac5295a4f8df16458daa
|
FirebaseSharedSwift: 93426a1de92f19e1199fac5295a4f8df16458daa
|
||||||
FirebaseStorage: 20d6b56fb8a40ebaa03d6a2889fe33dac64adb73
|
FirebaseStorage: 20d6b56fb8a40ebaa03d6a2889fe33dac64adb73
|
||||||
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
|
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
|
||||||
|
flutter_local_notifications: a5a732f069baa862e728d839dd2ebb904737effb
|
||||||
geolocator_apple: ab36aa0e8b7d7a2d7639b3b4e48308394e8cef5e
|
geolocator_apple: ab36aa0e8b7d7a2d7639b3b4e48308394e8cef5e
|
||||||
Google-Maps-iOS-Utils: 0a484b05ed21d88c9f9ebbacb007956edd508a96
|
Google-Maps-iOS-Utils: 0a484b05ed21d88c9f9ebbacb007956edd508a96
|
||||||
google_maps_flutter_ios: 0291eb2aa252298a769b04d075e4a9d747ff7264
|
google_maps_flutter_ios: 0291eb2aa252298a769b04d075e4a9d747ff7264
|
||||||
google_sign_in_ios: 205742c688aea0e64db9da03c33121694a365109
|
google_sign_in_ios: 205742c688aea0e64db9da03c33121694a365109
|
||||||
|
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
|
||||||
GoogleMaps: 0608099d4870cac8754bdba9b6953db543432438
|
GoogleMaps: 0608099d4870cac8754bdba9b6953db543432438
|
||||||
GoogleSignIn: c7f09cfbc85a1abf69187be091997c317cc33b77
|
GoogleSignIn: c7f09cfbc85a1abf69187be091997c317cc33b77
|
||||||
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
|
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
|
||||||
|
|||||||
@@ -1,23 +1,24 @@
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:firebase_auth/firebase_auth.dart';
|
import 'package:firebase_auth/firebase_auth.dart';
|
||||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||||
|
import 'package:travel_mate/services/notification_service.dart';
|
||||||
import 'user_event.dart' as event;
|
import 'user_event.dart' as event;
|
||||||
import 'user_state.dart' as state;
|
import 'user_state.dart' as state;
|
||||||
|
|
||||||
/// BLoC for managing user data and operations.
|
/// BLoC for managing user data and operations.
|
||||||
///
|
///
|
||||||
/// This BLoC handles user-related operations including loading user data,
|
/// This BLoC handles user-related operations including loading user data,
|
||||||
/// updating user information, and managing user state throughout the application.
|
/// updating user information, and managing user state throughout the application.
|
||||||
/// It coordinates with Firebase Auth and Firestore to manage user data persistence.
|
/// It coordinates with Firebase Auth and Firestore to manage user data persistence.
|
||||||
class UserBloc extends Bloc<event.UserEvent, state.UserState> {
|
class UserBloc extends Bloc<event.UserEvent, state.UserState> {
|
||||||
/// Firebase Auth instance for authentication operations.
|
/// Firebase Auth instance for authentication operations.
|
||||||
final FirebaseAuth _auth = FirebaseAuth.instance;
|
final FirebaseAuth _auth = FirebaseAuth.instance;
|
||||||
|
|
||||||
/// Firestore instance for user data operations.
|
/// Firestore instance for user data operations.
|
||||||
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
|
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
|
||||||
|
|
||||||
/// Creates a new [UserBloc] with initial state.
|
/// Creates a new [UserBloc] with initial state.
|
||||||
///
|
///
|
||||||
/// Registers event handlers for all user-related events.
|
/// Registers event handlers for all user-related events.
|
||||||
UserBloc() : super(state.UserInitial()) {
|
UserBloc() : super(state.UserInitial()) {
|
||||||
on<event.UserInitialized>(_onUserInitialized);
|
on<event.UserInitialized>(_onUserInitialized);
|
||||||
@@ -25,9 +26,9 @@ class UserBloc extends Bloc<event.UserEvent, state.UserState> {
|
|||||||
on<event.UserUpdated>(_onUserUpdated);
|
on<event.UserUpdated>(_onUserUpdated);
|
||||||
on<event.UserLoggedOut>(_onUserLoggedOut);
|
on<event.UserLoggedOut>(_onUserLoggedOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handles [UserInitialized] events.
|
/// Handles [UserInitialized] events.
|
||||||
///
|
///
|
||||||
/// Initializes the current authenticated user's data by fetching it from Firestore.
|
/// Initializes the current authenticated user's data by fetching it from Firestore.
|
||||||
/// If the user doesn't exist in Firestore, creates a default user document.
|
/// If the user doesn't exist in Firestore, creates a default user document.
|
||||||
/// This is typically called when the app starts or after successful authentication.
|
/// This is typically called when the app starts or after successful authentication.
|
||||||
@@ -36,49 +37,70 @@ class UserBloc extends Bloc<event.UserEvent, state.UserState> {
|
|||||||
Emitter<state.UserState> emit,
|
Emitter<state.UserState> emit,
|
||||||
) async {
|
) async {
|
||||||
emit(state.UserLoading());
|
emit(state.UserLoading());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final currentUser = _auth.currentUser;
|
final currentUser = _auth.currentUser;
|
||||||
|
|
||||||
if (currentUser == null) {
|
if (currentUser == null) {
|
||||||
emit(state.UserError('No user currently authenticated'));
|
emit(state.UserError('No user currently authenticated'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize notifications and update token
|
||||||
|
final notificationService = NotificationService();
|
||||||
|
await notificationService.initialize();
|
||||||
|
final fcmToken = await notificationService.getFCMToken();
|
||||||
|
print('DEBUG: UserBloc - FCM Token retrieved: $fcmToken');
|
||||||
|
|
||||||
// Fetch user data from Firestore
|
// Fetch user data from Firestore
|
||||||
final userDoc = await _firestore
|
final userDoc = await _firestore
|
||||||
.collection('users')
|
.collection('users')
|
||||||
.doc(currentUser.uid)
|
.doc(currentUser.uid)
|
||||||
.get();
|
.get();
|
||||||
|
|
||||||
if (!userDoc.exists) {
|
if (!userDoc.exists) {
|
||||||
// Create a default user if it doesn't exist
|
// Create a default user if it doesn't exist
|
||||||
final defaultUser = state.UserModel(
|
final defaultUser = state.UserModel(
|
||||||
id: currentUser.uid,
|
id: currentUser.uid,
|
||||||
email: currentUser.email ?? '',
|
email: currentUser.email ?? '',
|
||||||
prenom: currentUser.displayName ?? 'Voyageur',
|
prenom: currentUser.displayName ?? 'Voyageur',
|
||||||
|
fcmToken: fcmToken,
|
||||||
);
|
);
|
||||||
|
|
||||||
await _firestore
|
await _firestore
|
||||||
.collection('users')
|
.collection('users')
|
||||||
.doc(currentUser.uid)
|
.doc(currentUser.uid)
|
||||||
.set(defaultUser.toJson());
|
.set(defaultUser.toJson());
|
||||||
|
|
||||||
emit(state.UserLoaded(defaultUser));
|
emit(state.UserLoaded(defaultUser));
|
||||||
} else {
|
} else {
|
||||||
final user = state.UserModel.fromJson({
|
final user = state.UserModel.fromJson({
|
||||||
'id': currentUser.uid,
|
'id': currentUser.uid,
|
||||||
...userDoc.data()!,
|
...userDoc.data()!,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Update FCM token if it changed
|
||||||
|
if (fcmToken != null && user.fcmToken != fcmToken) {
|
||||||
|
print('DEBUG: UserBloc - Updating FCM token in Firestore');
|
||||||
|
await _firestore.collection('users').doc(currentUser.uid).update({
|
||||||
|
'fcmToken': fcmToken,
|
||||||
|
});
|
||||||
|
print('DEBUG: UserBloc - FCM token updated');
|
||||||
|
} else {
|
||||||
|
print(
|
||||||
|
'DEBUG: UserBloc - FCM token not updated. Local: $fcmToken, Firestore: ${user.fcmToken}',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
emit(state.UserLoaded(user));
|
emit(state.UserLoaded(user));
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
emit(state.UserError('Error loading user: $e'));
|
emit(state.UserError('Error loading user: $e'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handles [LoadUser] events.
|
/// Handles [LoadUser] events.
|
||||||
///
|
///
|
||||||
/// Loads a specific user's data from Firestore by their user ID.
|
/// Loads a specific user's data from Firestore by their user ID.
|
||||||
/// This is useful when you need to display information about other users.
|
/// This is useful when you need to display information about other users.
|
||||||
Future<void> _onLoadUser(
|
Future<void> _onLoadUser(
|
||||||
@@ -86,13 +108,13 @@ class UserBloc extends Bloc<event.UserEvent, state.UserState> {
|
|||||||
Emitter<state.UserState> emit,
|
Emitter<state.UserState> emit,
|
||||||
) async {
|
) async {
|
||||||
emit(state.UserLoading());
|
emit(state.UserLoading());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final userDoc = await _firestore
|
final userDoc = await _firestore
|
||||||
.collection('users')
|
.collection('users')
|
||||||
.doc(event.userId)
|
.doc(event.userId)
|
||||||
.get();
|
.get();
|
||||||
|
|
||||||
if (userDoc.exists) {
|
if (userDoc.exists) {
|
||||||
final user = state.UserModel.fromJson({
|
final user = state.UserModel.fromJson({
|
||||||
'id': event.userId,
|
'id': event.userId,
|
||||||
@@ -106,9 +128,9 @@ class UserBloc extends Bloc<event.UserEvent, state.UserState> {
|
|||||||
emit(state.UserError('Error loading user: $e'));
|
emit(state.UserError('Error loading user: $e'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handles [UserUpdated] events.
|
/// Handles [UserUpdated] events.
|
||||||
///
|
///
|
||||||
/// Updates the current user's data in Firestore with the provided information.
|
/// Updates the current user's data in Firestore with the provided information.
|
||||||
/// Only updates the fields specified in the userData map, allowing for partial updates.
|
/// Only updates the fields specified in the userData map, allowing for partial updates.
|
||||||
/// After successful update, reloads the user data to reflect changes.
|
/// After successful update, reloads the user data to reflect changes.
|
||||||
@@ -118,32 +140,32 @@ class UserBloc extends Bloc<event.UserEvent, state.UserState> {
|
|||||||
) async {
|
) async {
|
||||||
if (this.state is state.UserLoaded) {
|
if (this.state is state.UserLoaded) {
|
||||||
final currentUser = (this.state as state.UserLoaded).user;
|
final currentUser = (this.state as state.UserLoaded).user;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await _firestore
|
await _firestore
|
||||||
.collection('users')
|
.collection('users')
|
||||||
.doc(currentUser.id)
|
.doc(currentUser.id)
|
||||||
.update(event.userData);
|
.update(event.userData);
|
||||||
|
|
||||||
final updatedDoc = await _firestore
|
final updatedDoc = await _firestore
|
||||||
.collection('users')
|
.collection('users')
|
||||||
.doc(currentUser.id)
|
.doc(currentUser.id)
|
||||||
.get();
|
.get();
|
||||||
|
|
||||||
final updatedUser = state.UserModel.fromJson({
|
final updatedUser = state.UserModel.fromJson({
|
||||||
'id': currentUser.id,
|
'id': currentUser.id,
|
||||||
...updatedDoc.data()!,
|
...updatedDoc.data()!,
|
||||||
});
|
});
|
||||||
|
|
||||||
emit(state.UserLoaded(updatedUser));
|
emit(state.UserLoaded(updatedUser));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
emit(state.UserError('Error updating user: $e'));
|
emit(state.UserError('Error updating user: $e'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handles [UserLoggedOut] events.
|
/// Handles [UserLoggedOut] events.
|
||||||
///
|
///
|
||||||
/// Resets the user bloc state to initial when the user logs out.
|
/// Resets the user bloc state to initial when the user logs out.
|
||||||
/// This clears any cached user data from the application state.
|
/// This clears any cached user data from the application state.
|
||||||
Future<void> _onUserLoggedOut(
|
Future<void> _onUserLoggedOut(
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
|
|
||||||
/// Abstract base class for all user-related states.
|
/// Abstract base class for all user-related states.
|
||||||
///
|
///
|
||||||
/// This class extends [Equatable] to enable value equality for state comparison.
|
/// This class extends [Equatable] to enable value equality for state comparison.
|
||||||
/// All user states in the application should inherit from this class.
|
/// All user states in the application should inherit from this class.
|
||||||
abstract class UserState extends Equatable {
|
abstract class UserState extends Equatable {
|
||||||
@@ -10,79 +10,82 @@ abstract class UserState extends Equatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Initial state of the user bloc.
|
/// Initial state of the user bloc.
|
||||||
///
|
///
|
||||||
/// This state represents the initial state before any user operations
|
/// This state represents the initial state before any user operations
|
||||||
/// have been performed or when the user has logged out.
|
/// have been performed or when the user has logged out.
|
||||||
class UserInitial extends UserState {}
|
class UserInitial extends UserState {}
|
||||||
|
|
||||||
/// State indicating that a user operation is in progress.
|
/// State indicating that a user operation is in progress.
|
||||||
///
|
///
|
||||||
/// This state is used to show loading indicators during user data
|
/// This state is used to show loading indicators during user data
|
||||||
/// operations like loading, updating, or initializing user information.
|
/// operations like loading, updating, or initializing user information.
|
||||||
class UserLoading extends UserState {}
|
class UserLoading extends UserState {}
|
||||||
|
|
||||||
/// State indicating that user data has been successfully loaded.
|
/// State indicating that user data has been successfully loaded.
|
||||||
///
|
///
|
||||||
/// This state contains the loaded user information and is used
|
/// This state contains the loaded user information and is used
|
||||||
/// throughout the app to access current user data.
|
/// throughout the app to access current user data.
|
||||||
class UserLoaded extends UserState {
|
class UserLoaded extends UserState {
|
||||||
/// The loaded user data.
|
/// The loaded user data.
|
||||||
final UserModel user;
|
final UserModel user;
|
||||||
|
|
||||||
/// Creates a [UserLoaded] state with the given [user] data.
|
/// Creates a [UserLoaded] state with the given [user] data.
|
||||||
UserLoaded(this.user);
|
UserLoaded(this.user);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [user];
|
List<Object?> get props => [user];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// State indicating that a user operation has failed.
|
/// State indicating that a user operation has failed.
|
||||||
///
|
///
|
||||||
/// This state contains an error message that can be displayed to the user
|
/// This state contains an error message that can be displayed to the user
|
||||||
/// when user operations fail.
|
/// when user operations fail.
|
||||||
class UserError extends UserState {
|
class UserError extends UserState {
|
||||||
/// The error message describing what went wrong.
|
/// The error message describing what went wrong.
|
||||||
final String message;
|
final String message;
|
||||||
|
|
||||||
/// Creates a [UserError] state with the given error [message].
|
/// Creates a [UserError] state with the given error [message].
|
||||||
UserError(this.message);
|
UserError(this.message);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [message];
|
List<Object?> get props => [message];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Simple user model for representing user data in the application.
|
/// Simple user model for representing user data in the application.
|
||||||
///
|
///
|
||||||
/// This model contains basic user information and provides methods for
|
/// This model contains basic user information and provides methods for
|
||||||
/// serialization/deserialization with Firestore operations.
|
/// serialization/deserialization with Firestore operations.
|
||||||
/// Simple user model for representing user data in the application.
|
/// Simple user model for representing user data in the application.
|
||||||
///
|
///
|
||||||
/// This model contains basic user information and provides methods for
|
/// This model contains basic user information and provides methods for
|
||||||
/// serialization/deserialization with Firestore operations.
|
/// serialization/deserialization with Firestore operations.
|
||||||
class UserModel {
|
class UserModel {
|
||||||
/// Unique identifier for the user (Firebase UID).
|
/// Unique identifier for the user (Firebase UID).
|
||||||
final String id;
|
final String id;
|
||||||
|
|
||||||
/// User's email address.
|
/// User's email address.
|
||||||
final String email;
|
final String email;
|
||||||
|
|
||||||
/// User's first name.
|
/// User's first name.
|
||||||
final String prenom;
|
final String prenom;
|
||||||
|
|
||||||
/// User's last name (optional).
|
/// User's last name (optional).
|
||||||
final String? nom;
|
final String? nom;
|
||||||
|
|
||||||
/// Platform used for authentication (e.g., 'google', 'apple', 'email').
|
/// Platform used for authentication (e.g., 'google', 'apple', 'email').
|
||||||
final String? authMethod;
|
final String? authMethod;
|
||||||
|
|
||||||
/// User's phone number (optional).
|
/// User's phone number (optional).
|
||||||
final String? phoneNumber;
|
final String? phoneNumber;
|
||||||
|
|
||||||
/// User's profile picture URL (optional).
|
/// User's profile picture URL (optional).
|
||||||
final String? profilePictureUrl;
|
final String? profilePictureUrl;
|
||||||
|
|
||||||
|
/// Firebase Cloud Messaging token for push notifications.
|
||||||
|
final String? fcmToken;
|
||||||
|
|
||||||
/// Creates a new [UserModel] instance.
|
/// Creates a new [UserModel] instance.
|
||||||
///
|
///
|
||||||
/// [id], [email], and [prenom] are required fields.
|
/// [id], [email], and [prenom] are required fields.
|
||||||
/// [nom], [authMethod], [phoneNumber], and [profilePictureUrl] are optional and can be null.
|
/// [nom], [authMethod], [phoneNumber], and [profilePictureUrl] are optional and can be null.
|
||||||
UserModel({
|
UserModel({
|
||||||
@@ -93,10 +96,11 @@ class UserModel {
|
|||||||
this.authMethod,
|
this.authMethod,
|
||||||
this.phoneNumber,
|
this.phoneNumber,
|
||||||
this.profilePictureUrl,
|
this.profilePictureUrl,
|
||||||
|
this.fcmToken,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Creates a [UserModel] instance from a JSON map.
|
/// Creates a [UserModel] instance from a JSON map.
|
||||||
///
|
///
|
||||||
/// Handles null values gracefully by providing default values.
|
/// Handles null values gracefully by providing default values.
|
||||||
/// [prenom] defaults to 'Voyageur' (Traveler) if not provided.
|
/// [prenom] defaults to 'Voyageur' (Traveler) if not provided.
|
||||||
factory UserModel.fromJson(Map<String, dynamic> json) {
|
factory UserModel.fromJson(Map<String, dynamic> json) {
|
||||||
@@ -108,11 +112,12 @@ class UserModel {
|
|||||||
authMethod: json['authMethod'] ?? json['platform'],
|
authMethod: json['authMethod'] ?? json['platform'],
|
||||||
phoneNumber: json['phoneNumber'],
|
phoneNumber: json['phoneNumber'],
|
||||||
profilePictureUrl: json['profilePictureUrl'],
|
profilePictureUrl: json['profilePictureUrl'],
|
||||||
|
fcmToken: json['fcmToken'],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts the [UserModel] instance to a JSON map.
|
/// Converts the [UserModel] instance to a JSON map.
|
||||||
///
|
///
|
||||||
/// Useful for storing user data in Firestore or other JSON-based operations.
|
/// Useful for storing user data in Firestore or other JSON-based operations.
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
return {
|
return {
|
||||||
@@ -123,6 +128,7 @@ class UserModel {
|
|||||||
'authMethod': authMethod,
|
'authMethod': authMethod,
|
||||||
'phoneNumber': phoneNumber,
|
'phoneNumber': phoneNumber,
|
||||||
'profilePictureUrl': profilePictureUrl,
|
'profilePictureUrl': profilePictureUrl,
|
||||||
|
'fcmToken': fcmToken,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -213,8 +213,8 @@ class _GroupExpensesPageState extends State<GroupExpensesPage>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
floatingActionButton: FloatingActionButton(
|
floatingActionButton: FloatingActionButton(
|
||||||
|
heroTag: 'group_expenses_fab',
|
||||||
onPressed: _showAddExpenseDialog,
|
onPressed: _showAddExpenseDialog,
|
||||||
heroTag: "add_expense_fab",
|
|
||||||
backgroundColor: Colors.blue,
|
backgroundColor: Colors.blue,
|
||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
elevation: 4,
|
elevation: 4,
|
||||||
|
|||||||
@@ -155,6 +155,7 @@ class _HomeContentState extends State<HomeContent>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
floatingActionButton: FloatingActionButton(
|
floatingActionButton: FloatingActionButton(
|
||||||
|
heroTag: 'home_fab',
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
final tripBloc = context.read<TripBloc>();
|
final tripBloc = context.read<TripBloc>();
|
||||||
|
|
||||||
|
|||||||
@@ -663,6 +663,7 @@ class _MapContentState extends State<MapContent> {
|
|||||||
),
|
),
|
||||||
|
|
||||||
floatingActionButton: FloatingActionButton(
|
floatingActionButton: FloatingActionButton(
|
||||||
|
heroTag: 'map_fab',
|
||||||
onPressed: _getCurrentLocation,
|
onPressed: _getCurrentLocation,
|
||||||
tooltip: 'Ma position',
|
tooltip: 'Ma position',
|
||||||
child: const Icon(Icons.my_location),
|
child: const Icon(Icons.my_location),
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'dart:io';
|
||||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||||
import 'package:travel_mate/services/logger_service.dart';
|
import 'package:travel_mate/services/logger_service.dart';
|
||||||
@@ -58,7 +59,26 @@ class NotificationService {
|
|||||||
|
|
||||||
Future<String?> getFCMToken() async {
|
Future<String?> getFCMToken() async {
|
||||||
try {
|
try {
|
||||||
return await _firebaseMessaging.getToken();
|
if (Platform.isIOS) {
|
||||||
|
String? apnsToken = await _firebaseMessaging.getAPNSToken();
|
||||||
|
int retries = 0;
|
||||||
|
while (apnsToken == null && retries < 3) {
|
||||||
|
LoggerService.info(
|
||||||
|
'Waiting for APNS token... (Attempt ${retries + 1}/3)',
|
||||||
|
);
|
||||||
|
await Future.delayed(const Duration(seconds: 1));
|
||||||
|
apnsToken = await _firebaseMessaging.getAPNSToken();
|
||||||
|
retries++;
|
||||||
|
}
|
||||||
|
if (apnsToken == null) {
|
||||||
|
LoggerService.error('APNS token not available after retries');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final token = await _firebaseMessaging.getToken();
|
||||||
|
LoggerService.info('NotificationService - FCM Token: $token');
|
||||||
|
return token;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
LoggerService.error('Error getting FCM token: $e');
|
LoggerService.error('Error getting FCM token: $e');
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
Reference in New Issue
Block a user