From 7d3b47b3ba142165cebe739a61ca6b67bb16b91a Mon Sep 17 00:00:00 2001 From: null Date: Tue, 23 Jun 2026 12:40:00 -0500 Subject: [PATCH] fix(firestore): handle Timestamp type for lastAnsweredAt/createdAt in Android + Cloud Function --- .../data/remote/FirestoreCoupleDataSource.kt | 13 ++++++++++--- .../dist/couples/scheduledOutcomesReminder.js | 17 ++++++++++++++--- .../couples/scheduledOutcomesReminder.js.map | 2 +- .../src/couples/scheduledOutcomesReminder.ts | 14 +++++++++++++- 4 files changed, 38 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/app/closer/data/remote/FirestoreCoupleDataSource.kt b/app/src/main/java/app/closer/data/remote/FirestoreCoupleDataSource.kt index 9fee513f..c8d04bd4 100644 --- a/app/src/main/java/app/closer/data/remote/FirestoreCoupleDataSource.kt +++ b/app/src/main/java/app/closer/data/remote/FirestoreCoupleDataSource.kt @@ -3,6 +3,7 @@ package app.closer.data.remote import app.closer.crypto.RecoveryKeyManager import app.closer.crypto.CoupleEncryptionManager import app.closer.domain.model.Couple +import com.google.firebase.Timestamp import com.google.firebase.firestore.DocumentSnapshot import com.google.firebase.firestore.FirebaseFirestore import com.google.firebase.firestore.SetOptions @@ -142,7 +143,7 @@ class FirestoreCoupleDataSource @Inject constructor( .addOnSuccessListener { cont.resume(it) } .addOnFailureListener { cont.resumeWithException(it) } } - val lastAnsweredAt = snap.getLong("lastAnsweredAt") ?: 0L + val lastAnsweredAt = snap.millisOrNull("lastAnsweredAt") ?: 0L val now = System.currentTimeMillis() val current = (snap.getLong("streakCount") ?: 0L).toInt() val newStreak = if (lastAnsweredAt == 0L || now - lastAnsweredAt > TWO_DAYS_MS) 1 else current + 1 @@ -167,10 +168,10 @@ class FirestoreCoupleDataSource @Inject constructor( id = id, userIds = (get("userIds") as? List) ?: emptyList(), inviteCode = getString("inviteCode") ?: "", - createdAt = getLong("createdAt") ?: 0L, + createdAt = millisOrNull("createdAt") ?: 0L, currentQuestionId = getString("currentQuestionId"), streakCount = (getLong("streakCount") ?: 0L).toInt(), - lastAnsweredAt = getLong("lastAnsweredAt"), + lastAnsweredAt = millisOrNull("lastAnsweredAt"), activePackId = getString("activePackId"), encryptionVersion = (getLong("encryptionVersion") ?: 0L).toInt(), wrappedCoupleKey = getString("wrappedCoupleKey"), @@ -180,6 +181,12 @@ class FirestoreCoupleDataSource @Inject constructor( ?: emptyMap() ) + private fun DocumentSnapshot.millisOrNull(field: String): Long? = when (val raw = get(field)) { + is Number -> raw.toLong() + is Timestamp -> raw.toDate().time + else -> null + } + companion object { private const val TWO_DAYS_MS = 48L * 60 * 60 * 1000 } diff --git a/functions/dist/couples/scheduledOutcomesReminder.js b/functions/dist/couples/scheduledOutcomesReminder.js index f13edec1..f857a852 100644 --- a/functions/dist/couples/scheduledOutcomesReminder.js +++ b/functions/dist/couples/scheduledOutcomesReminder.js @@ -42,7 +42,7 @@ const DAY_KEY_MAP = { 30: 'day_30', 60: 'day_60', 90: 'day_90' }; exports.scheduledOutcomesReminder = functions.pubsub .schedule('every 24 hours') .onRun(async () => { - var _a, _b, _c; + var _a, _b; const db = admin.firestore(); const messaging = admin.messaging(); const now = Date.now(); @@ -51,14 +51,14 @@ exports.scheduledOutcomesReminder = functions.pubsub for (const coupleDoc of couplesSnap.docs) { const coupleId = coupleDoc.id; const data = (_a = coupleDoc.data()) !== null && _a !== void 0 ? _a : {}; - const createdAt = Number((_b = data.createdAt) !== null && _b !== void 0 ? _b : 0); + const createdAt = millisFromFirestoreValue(data.createdAt); if (createdAt <= 0) continue; const ageDays = Math.floor((now - createdAt) / DAY_MS); const dueDays = REMINDER_DAYS.filter((day) => ageDays >= day && ageDays <= day + 2); if (dueDays.length === 0) continue; - const userIds = ((_c = data.userIds) !== null && _c !== void 0 ? _c : []); + const userIds = ((_b = data.userIds) !== null && _b !== void 0 ? _b : []); if (userIds.length === 0) continue; // Check each due checkpoint; only remind for the first one without an outcome. @@ -83,6 +83,17 @@ exports.scheduledOutcomesReminder = functions.pubsub await Promise.all(notifications.map((notification) => sendOutcomeReminder(db, messaging, notification))); console.log(`[scheduledOutcomesReminder] scanned ${couplesSnap.size}; notified ${notifications.length}`); }); +function millisFromFirestoreValue(value) { + if (typeof value === 'number') + return value; + if (value instanceof admin.firestore.Timestamp) + return value.toMillis(); + if (value != null && + typeof value.toMillis === 'function') { + return value.toMillis(); + } + return 0; +} async function sendOutcomeReminder(db, messaging, notification) { await db .collection('users') diff --git a/functions/dist/couples/scheduledOutcomesReminder.js.map b/functions/dist/couples/scheduledOutcomesReminder.js.map index a85a2a1a..157478d2 100644 --- a/functions/dist/couples/scheduledOutcomesReminder.js.map +++ b/functions/dist/couples/scheduledOutcomesReminder.js.map @@ -1 +1 @@ -{"version":3,"file":"scheduledOutcomesReminder.js","sourceRoot":"","sources":["../../src/couples/scheduledOutcomesReminder.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"scheduledOutcomesReminder.js","sourceRoot":"","sources":["../../src/couples/scheduledOutcomesReminder.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/functions/src/couples/scheduledOutcomesReminder.ts b/functions/src/couples/scheduledOutcomesReminder.ts index c2bd7d6b..c21c23bc 100644 --- a/functions/src/couples/scheduledOutcomesReminder.ts +++ b/functions/src/couples/scheduledOutcomesReminder.ts @@ -40,7 +40,7 @@ export const scheduledOutcomesReminder = functions.pubsub for (const coupleDoc of couplesSnap.docs) { const coupleId = coupleDoc.id const data = coupleDoc.data() ?? {} - const createdAt = Number(data.createdAt ?? 0) + const createdAt = millisFromFirestoreValue(data.createdAt) if (createdAt <= 0) continue const ageDays = Math.floor((now - createdAt) / DAY_MS) @@ -80,6 +80,18 @@ export const scheduledOutcomesReminder = functions.pubsub console.log(`[scheduledOutcomesReminder] scanned ${couplesSnap.size}; notified ${notifications.length}`) }) +function millisFromFirestoreValue(value: unknown): number { + if (typeof value === 'number') return value + if (value instanceof admin.firestore.Timestamp) return value.toMillis() + if ( + value != null && + typeof (value as { toMillis?: unknown }).toMillis === 'function' + ) { + return (value as { toMillis: () => number }).toMillis() + } + return 0 +} + async function sendOutcomeReminder( db: admin.firestore.Firestore, messaging: admin.messaging.Messaging,