66 deleteDoc ,
77 setDoc ,
88 query ,
9+ where ,
910 orderBy ,
1011 limit ,
1112 startAfter ,
@@ -21,6 +22,7 @@ import { ref, uploadBytes, getDownloadURL } from 'firebase/storage';
2122import { getFirebaseFirestore , getFirebaseStorage } from './firebase' ;
2223import { UserService } from './user' ;
2324import {
25+ ConversationProps ,
2426 FireStoreCollection ,
2527 IMessage ,
2628 MediaType ,
@@ -67,62 +69,50 @@ export class ChatService {
6769 conversationId ?: string ,
6870 ) : Promise < string > {
6971 try {
72+ // Per-user names: initiator sees otherName, others see initiator's name
73+ const names : Record < string , string > = { } ;
74+ const unRead : Record < string , number > = { } ;
75+ memberIds . forEach ( ( id ) => {
76+ names [ id ] = id === initiatorId ? ( otherName || '' ) : ( name || '' ) ;
77+ unRead [ id ] = 0 ;
78+ } ) ;
79+
7080 const conversationData = {
7181 members : memberIds ,
7282 type,
73- name : otherName || '' ,
83+ names,
84+ unRead,
7485 createdAt : Date . now ( ) ,
7586 updatedAt : Date . now ( ) ,
7687 latestMessage : null ,
7788 latestMessageTime : null ,
78- createdBy : initiatorId
89+ createdBy : initiatorId ,
7990 } ;
8091
8192 let docRefId : string ;
8293
8394 if ( conversationId ) {
84- // Create the document with the specified conversationId
8595 await updateDoc (
8696 doc ( this . db , COLLECTIONS . CONVERSATIONS , conversationId ) ,
8797 conversationData
88- ) . catch ( async ( _err ) => {
89- // If doc does not exist, set it
98+ ) . catch ( async ( ) => {
9099 await setDoc (
91100 doc ( this . db , COLLECTIONS . CONVERSATIONS , conversationId ) ,
92101 conversationData
93102 ) ;
94103 } ) ;
95104 docRefId = conversationId ;
96105 } else {
97- // Add a new document with auto-generated ID
98106 const docRef = await addDoc (
99107 collection ( this . db , COLLECTIONS . CONVERSATIONS ) ,
100108 conversationData
101109 ) ;
102110 docRefId = docRef . id ;
103111 }
104112
105- // Create user conversation references for each member
106- const promises = memberIds . map ( async ( memberId ) => {
107- // Ensure user document exists
108- await this . userService . createUserIfNotExists ( memberId ) ;
109-
110- // Get the chat name based on the memberId and initiatorId
111- const chatName = memberId === initiatorId ? otherName : name ;
112- // Add conversation reference to user's conversations subcollection
113- await setDoc (
114- doc ( this . db , COLLECTIONS . USERS , memberId , COLLECTIONS . CONVERSATIONS , docRefId ) ,
115- {
116- joinedAt : Date . now ( ) ,
117- unRead : 0 ,
118- updatedAt : Date . now ( ) ,
119- members : memberIds ,
120- name : chatName || '' ,
121- }
122- ) ;
123- } ) ;
113+ // Ensure user documents exist for all members
114+ await Promise . all ( memberIds . map ( ( id ) => this . userService . createUserIfNotExists ( id ) ) ) ;
124115
125- await Promise . all ( promises ) ;
126116 return docRefId ;
127117 } catch ( error ) {
128118 console . error ( 'Error creating conversation:' , error ) ;
@@ -167,7 +157,9 @@ export class ChatService {
167157 try {
168158 // Check if conversation exists, create if it doesn't
169159 const conversationExists = await this . conversationExists ( conversationId ) ;
170- const memberIds = conversationOptions ?. memberIds || [ String ( message . senderId ) ] ;
160+ const memberIds = conversationOptions ?. memberIds && conversationOptions . memberIds . length > 1
161+ ? conversationOptions . memberIds
162+ : [ String ( message . senderId ) ] ;
171163
172164 if ( ! conversationExists ) {
173165 // Use provided options or defaults
@@ -206,30 +198,27 @@ export class ChatService {
206198 }
207199 ) ;
208200
209- // Update unread counts for other members
201+ // Update unread counts for other members on the conversation document
210202 const conversationDoc = await getDoc (
211203 doc ( this . db , COLLECTIONS . CONVERSATIONS , conversationId )
212204 ) ;
213205
214206 if ( conversationDoc . exists ( ) ) {
215- // Use members from the conversation document so all participants are updated,
216- // regardless of what memberIds was passed by the caller
217207 const allMembers : string [ ] = conversationDoc . data ( ) ?. members || memberIds ;
218- const updatePromises = allMembers . map ( async ( memberId : string ) => {
219- const isSender = memberId === message . senderId ;
220- // Update unread count in user's conversations subcollection
221- await setDoc (
222- doc ( this . db , COLLECTIONS . USERS , memberId , COLLECTIONS . CONVERSATIONS , conversationId ) ,
223- {
224- unRead : increment ( isSender ? 0 : 1 ) ,
225- updatedAt : Date . now ( ) ,
226- latestMessage : convertToLatestMessage ( `${ message . senderId } ` , conversationOptions ?. name || '' , message . text || '' ) ,
227- } ,
228- { merge : true } // Merge with existing data if document exists
229- ) ;
208+ const unreadUpdates : Record < string , unknown > = { } ;
209+ allMembers . forEach ( ( memberId : string ) => {
210+ if ( memberId !== message . senderId ) {
211+ unreadUpdates [ `unRead.${ memberId } ` ] = increment ( 1 ) ;
212+ }
230213 } ) ;
231214
232- await Promise . all ( updatePromises ) ;
215+ await updateDoc (
216+ doc ( this . db , COLLECTIONS . CONVERSATIONS , conversationId ) ,
217+ {
218+ ...unreadUpdates ,
219+ latestMessage : convertToLatestMessage ( `${ message . senderId } ` , conversationOptions ?. name || '' , message . text || '' ) ,
220+ }
221+ ) ;
233222 }
234223 } catch ( error ) {
235224 console . error ( 'Error sending message:' , error ) ;
@@ -279,32 +268,39 @@ export class ChatService {
279268 }
280269
281270 /**
282- * Get user's conversations from the user's conversations subcollection
271+ * Get user's conversations from the top-level /conversations collection
272+ * filtered by membership.
283273 * @param userId - The ID of the user
284274 * @param callback - Callback function to receive conversations
285275 * @returns Unsubscribe function
286276 */
287277 subscribeToUserConversations (
288278 userId : string ,
289- callback : ( userConversations : any [ ] ) => void
279+ callback : ( userConversations : ConversationProps [ ] ) => void
290280 ) : ( ) => void {
291281 // First ensure user document exists
292282 this . userService . createUserIfNotExists ( userId ) . catch ( console . error ) ;
293283
294284 const userConversationsQuery = query (
295- collection ( this . db , COLLECTIONS . USERS , userId , 'conversations' ) ,
285+ collection ( this . db , COLLECTIONS . CONVERSATIONS ) ,
286+ where ( 'members' , 'array-contains' , userId ) ,
296287 orderBy ( 'updatedAt' , 'desc' )
297288 ) ;
298289
299290 return onSnapshot ( userConversationsQuery , ( snapshot ) => {
300- const userConversations : any [ ] = [ ] ;
301- snapshot . forEach ( ( doc ) => {
302- const data = doc . data ( ) ;
291+ const userConversations : ConversationProps [ ] = [ ] ;
292+ snapshot . forEach ( ( docSnap ) => {
293+ const data = docSnap . data ( ) ;
294+ const names : Record < string , string > = data . names || { } ;
295+ const unreadCounts : Record < string , number > = data . unRead || { } ;
303296 userConversations . push ( {
304- id : doc . id ,
305- ...data ,
297+ id : docSnap . id ,
298+ name : names [ userId ] || '' ,
299+ members : data . members || [ ] ,
300+ unRead : unreadCounts ,
306301 updatedAt : data . updatedAt ? new Date ( data . updatedAt ) . valueOf ( ) : Date . now ( ) ,
307- joinedAt : data . joinedAt ? new Date ( data . joinedAt ) . valueOf ( ) : Date . now ( ) ,
302+ joinedAt : data . createdAt ? new Date ( data . createdAt ) . valueOf ( ) : Date . now ( ) ,
303+ latestMessage : data . latestMessage || undefined ,
308304 } ) ;
309305 } ) ;
310306 callback ( userConversations ) ;
@@ -359,12 +355,18 @@ export class ChatService {
359355 } ) ;
360356 }
361357
362- // Update unread count
358+ // Update unread count on the main conversation document
363359 async updateUnread ( conversationId : string , userId : string ) : Promise < void > {
364360 try {
365- const conversationRef = doc ( this . db , FireStoreCollection . users , userId , FireStoreCollection . conversations , conversationId ) ;
366- await setDoc ( conversationRef , { unRead : 0 } , { merge : true } ) ;
367- } catch ( error ) {
361+ await updateDoc (
362+ doc ( this . db , COLLECTIONS . CONVERSATIONS , conversationId ) ,
363+ { [ `unRead.${ userId } ` ] : 0 }
364+ ) ;
365+ } catch ( error : any ) {
366+ if ( error ?. code === 'not-found' ) {
367+ // Conversation document doesn't exist yet, we can safely ignore
368+ return ;
369+ }
368370 console . error ( 'Error updating unread count:' , error ) ;
369371 throw new Error ( 'Failed to update unread count' ) ;
370372 }
0 commit comments