diff --git a/nirvana-ios/Models/Message.swift b/nirvana-ios/Models/Message.swift index 9d45699..51eda11 100644 --- a/nirvana-ios/Models/Message.swift +++ b/nirvana-ios/Models/Message.swift @@ -14,16 +14,25 @@ struct Message: Identifiable, Codable { @DocumentID var id: String? = UUID().uuidString var receiverId: String var senderId: String - var listenCount: Int + var senderIdReceiverIdComposite: [String] + var listenCount: Int? var audioDataUrl:String @ServerTimestamp var sentTimestamp:Date? var firstListenTimestamp:Date? + + init(sendId: String, receivId: String, audioDUrl:String) { + self.senderIdReceiverIdComposite = [sendId, receivId] + self.senderId = sendId + self.receiverId = receivId + self.audioDataUrl = audioDUrl + } enum CodingKeys: String, CodingKey { case id case receiverId case senderId + case senderIdReceiverIdComposite case listenCount case audioDataUrl case sentTimestamp diff --git a/nirvana-ios/Services/AuthSessionStore.swift b/nirvana-ios/Services/AuthSessionStore.swift index 68de559..c6bd990 100644 --- a/nirvana-ios/Services/AuthSessionStore.swift +++ b/nirvana-ios/Services/AuthSessionStore.swift @@ -209,7 +209,7 @@ extension AuthSessionStore { } // MARK: keeping the friends list updated - // TODO: break into firestoreService + // TODO: break into firestoreService metadata of each change...later tho since this listener will barely get changes // different actions on additions, modifications, and removals // parse through the new result set @@ -279,68 +279,50 @@ extension AuthSessionStore { } - // MARK: keeping the list of messages updated - // one listener for received messages - - // don't know if we need a listener for sent messages as we can alter our model from local and add to the dictionary + // MARK: listener for messages + // TODO: don't know if we need a listener for sent messages as we can alter our model from local and add to the dictionary + // senderIdReceiverIdComposite: composite array of strings which contains the senderId and receiverId as elements // order: sent time should make it easy to add to dict - // limit: because each user should have most 12 friends and so would at most need 24 messages to show turns and all that...save myself from hackers here - // TODO: use the indexes I created - db.collection("messages").whereField("receiverId", isEqualTo: userId).order(by: "sentTimestamp").limit(to: 100) + // limit: because each user should have most 12 friends and so would at most need 24 messages to show turns and all that...save myself from hackers here...unless a user gets 100 messages from someone, and that too at least they will be ordered + + // SOLUTION: composite with array + db.collection("messages").whereField("senderIdReceiverIdComposite", arrayContains: userId).order(by: "sentTimestamp", descending: true).limit(to: 100) .addSnapshotListener { querySnapshot, error in - guard let documents = querySnapshot?.documents else { - print("Error fetching messages: \(error!)") - return - } - print("got all messages relevant") - - self.messagesArr = documents.compactMap { (queryDocumentSnapshot) -> Message? in - do { - let currMessage = try queryDocumentSnapshot.data(as: Message.self) - print("new message received! \(queryDocumentSnapshot.data())") - - if currMessage != nil { // not really possible but just check - // if the user doesn't exist for the dictionary, then add it - // this means it's most likely someone new (never had user_friend relationship before) messaging for the user's inbox - // TODO: prolly want to make a call to get this sender user details for the inbox, but they should either be in the friendsArr or their are not a friend so won't be there - if self.friendMessagesDict[currMessage!.senderId] == nil { - self.friendMessagesDict[currMessage!.senderId] = [currMessage!] - } else { - self.friendMessagesDict[currMessage!.senderId]?.append(currMessage!) + guard let snapshot = querySnapshot else { + print("Error fetching snapshots: \(error!)") + return + } + try? snapshot.documentChanges.forEach { diff in + // only need to modify array on new additions... + if (diff.type == .added) { + let currMessage = try diff.document.data(as: Message.self) + print("new message received! \(currMessage)") + + if currMessage != nil { // not really possible but just check + // if the user doesn't exist for the dictionary, then add it + // this means it's most likely someone new (never had user_friend relationship before) messaging for the user's inbox + // TODO: is the firestore ordering working? maybe need clientside ordering here...just maybe...keep array of messages sorted, but this should automatically be sorted? + if self.friendMessagesDict[currMessage!.senderId] == nil { + self.friendMessagesDict[currMessage!.senderId] = [currMessage!] + } else { // [2, 1] => [2, 1] + self.friendMessagesDict[currMessage!.senderId]?.append(currMessage!) + } + + //self.objectWillChange.send()// TODO: maybe don't need? value type friendsMessagesDict? so publishes changes? } - self.objectWillChange.send() } - return currMessage - } catch { - print("error in trying to decode message \(error)") + // no need to a alter view model as of now + if (diff.type == .modified) { + print("Modified city: \(diff.document.data())") + } + // no feature for deleting messages as of now + if (diff.type == .removed) { + print("Removed city: \(diff.document.data())") + } } - return nil } - // TODO: maybe need to call object will change here - - // TODO: optimize later -// guard let snapshot = querySnapshot else { -// print("Error fetching messages: \(error!)") -// return -// } -// snapshot.documentChanges.forEach { diff in -// if (diff.type == .added) { -// print("New message: \(diff.document.data())") -// -// // keep array of messages sorted, but this should automatically be sorted? -// } -// if (diff.type == .modified) { -// // nothing to really do here -// print("Modified message: \(diff.document.data())") -// } -// if (diff.type == .removed) { -// print("Removed message: \(diff.document.data())") -// } -// } - } - } private func deinitDataListeners() { diff --git a/nirvana-ios/Views/InnerCircle/CircleGridView.swift b/nirvana-ios/Views/InnerCircle/CircleGridView.swift index a81e241..4620791 100644 --- a/nirvana-ios/Views/InnerCircle/CircleGridView.swift +++ b/nirvana-ios/Views/InnerCircle/CircleGridView.swift @@ -169,8 +169,9 @@ struct CircleGridView: View { if self.authSessionStore.user != nil { let userId = self.authSessionStore.user!.id // O(1) // currUser who is signed in + // get most recent message in the convo and see who has spoken if let messagesRelatedToFriend = self.authSessionStore.friendMessagesDict[friendDbId] { // O(1) - return messagesRelatedToFriend.last?.receiverId == userId && messagesRelatedToFriend.last?.listenCount == 0 + return messagesRelatedToFriend.first?.receiverId == userId } } @@ -284,25 +285,22 @@ extension CircleGridView { // TODO: OPTIMIZATION...buffer and load all AVAssets to create AVPlayerItems before a tap happens...but this can also cause load in background if user is not playing a message right now...this isn't an optimization of the data/firestore but rather the player - // I want to play the last x messages if I was the receiver - // ["sarth": [me, me]] -> play nothing - // ["sarth": [me, him, him, him]] -> play his two messages + // I want to play the last x messages if I was the receiver...the array is sorted from backend so that the + // most recent comes first + // ["sarth": [ME, HIM...]] -> play nothing + // ["sarth": [HIM, HIM, me, him...]] -> play his two messages // traverse through reversed list of messages and add to audio player queue // TODO: protect against force unwraps var AVPlayerItems: [AVPlayerItem] = [] - let messagesRelatedToFriend = self.authSessionStore.friendMessagesDict[friend.id!]!.reversed() + let messagesRelatedToFriend = self.authSessionStore.friendMessagesDict[friend.id!]! print("have \(messagesRelatedToFriend.count) messages to play") for message in messagesRelatedToFriend { - // if it's me then don't play + // if it's starting to get to my messages then don't play if message.senderId == self.authSessionStore.user?.id { break } - // if I already listened to this "last" message, then break as well - if message.listenCount >= 1 { - break - } // only add to queue if we can convert the database url to a valid url here if let audioUrl = URL(string: message.audioDataUrl) { @@ -310,9 +308,11 @@ extension CircleGridView { AVPlayerItems.append(playerMessage) } } - + // start playing if there are messages to listen to if AVPlayerItems.count > 0 { + // reverse the items because we want to listen to the most recent messages in order + AVPlayerItems = AVPlayerItems.reversed() queuePlayer = AVQueuePlayer(items: AVPlayerItems) // TODO: make sure these options are viable for different scenarios diff --git a/nirvana-ios/Views/InnerCircle/InnerCircleViewModel.swift b/nirvana-ios/Views/InnerCircle/InnerCircleViewModel.swift index dd3fa6c..6ec4c3c 100644 --- a/nirvana-ios/Views/InnerCircle/InnerCircleViewModel.swift +++ b/nirvana-ios/Views/InnerCircle/InnerCircleViewModel.swift @@ -68,8 +68,8 @@ class InnerCircleViewModel: ObservableObject { return } - let newMessage = Message(receiverId: receiverId, senderId: senderId, listenCount: 0, audioDataUrl: audioDataUrl!.absoluteString) - + let newMessage = Message(sendId: senderId, receivId: receiverId, audioDUrl: audioDataUrl!.absoluteString) + print(newMessage) // create a new message in firestore with the url for receiving user to automatically get notified self?.firestoreService.createMessage(message: newMessage) {[weak self] res in print(res)