From aec0ad9a4fc8bf5be6bd628e0cea993369a259d0 Mon Sep 17 00:00:00 2001 From: talksik Date: Fri, 24 Dec 2021 17:29:00 -0800 Subject: [PATCH] decent place with the whole refactoring but not done, and not tested at all --- nirvana-ios/Services/AuthSessionStore.swift | 98 ++++++--------- .../Views/Contacts/FindFriendsView.swift | 11 +- .../Views/InnerCircle/CircleFooterView.swift | 4 +- .../Views/InnerCircle/CircleGridView.swift | 114 ++++++++++++------ .../Views/InnerCircle/InnerCircleView.swift | 6 +- 5 files changed, 124 insertions(+), 109 deletions(-) diff --git a/nirvana-ios/Services/AuthSessionStore.swift b/nirvana-ios/Services/AuthSessionStore.swift index b2aa28e..6e21284 100644 --- a/nirvana-ios/Services/AuthSessionStore.swift +++ b/nirvana-ios/Services/AuthSessionStore.swift @@ -33,13 +33,12 @@ final class AuthSessionStore: ObservableObject, SessionStore { @Published var sessionState: SessionState = SessionState.notCheckedYet // TODO: figure out which ones to publish - @Published var friendsArr: [User] = [] // all active and inactive friends var messagesArr: [Message] = [] - @Published var userFriends: [UserFriends] = [] // all active and inactive relationships + var userFriendsDict: [String: UserFriends] = [:] // all active and inactive relationships // transformed data for the views + @Published var relevantUsersDict: [String: User] = [:] // all cached/snapshotted users from db for app use @Published var relevantMessagesByUserDict: [String: [Message]] = [:] // note, this is all messages related to me - @Published var inboxUsers: [User] = [] private var dataListeners: [ListenerRegistration] = [] private var listenersActive = false // false on app/processes killed @@ -245,8 +244,8 @@ extension AuthSessionStore { } // resetting array to reset friends - self.friendsArr.removeAll() - self.userFriends.removeAll() + self.relevantUsersDict.removeAll() + self.userFriendsDict.removeAll() for document in querySnapshot!.documents { let userFriend:UserFriends? = try? document.data(as: UserFriends.self) @@ -255,15 +254,16 @@ extension AuthSessionStore { continue } - self.userFriends.append(userFriend!) + self.userFriendsDict[userFriend!.friendId] = userFriend! // get user and initialize this user in dict self.db.collection("users").document(userFriend!.friendId).getDocument { (document, error) in if let document = document, document.exists { let returnedUser = try? document.data(as: User.self) + DispatchQueue.main.async { if returnedUser != nil { - self.friendsArr.append(returnedUser!) + self.relevantUsersDict[userFriend!.friendId] = returnedUser! self.objectWillChange.send() @@ -318,7 +318,6 @@ extension AuthSessionStore { // clearing dict to allow clean list of messages to be put forth //optimize this? but also saving on memory and same db reads self.relevantMessagesByUserDict.removeAll() - self.inboxUsers.removeAll() self.messagesArr = documents.compactMap { (queryDocumentSnapshot) -> Message? in do { @@ -329,7 +328,7 @@ extension AuthSessionStore { 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 + // TODO: prolly want to make a call to get this sender user details for the inbox, but they should either be in the friendsDict or their are not a friend so won't be there // also add in any messages where I am the sender if currMessage!.senderId == currUserId { // if I am the sender if self.relevantMessagesByUserDict[currMessage!.receiverId] == nil { @@ -345,6 +344,21 @@ extension AuthSessionStore { self.relevantMessagesByUserDict[currMessage!.senderId]?.append(currMessage!) } + // need to get and add this user to inbox if they are not a friend, active or inactive +// self.updateInboxUsers(currMessage!.senderId) + // might be an inbox message from someone completely new, but also can be a concurrency thing, where we haven't completely fetched all friends, active or inactive with the previous listener + // no worries though as we keep everything update to date in the view + if !self.relevantUsersDict.keys.contains(currMessage!.senderId) { + self.firestoreService.getUser(userId: currMessage!.senderId) {[weak self] returnedUser in + if returnedUser == nil { + print("couldn't get new user info") + return + } + + print("new user for inbox fetched") + self?.relevantUsersDict[currMessage!.senderId] = returnedUser! + } + } } } @@ -356,11 +370,7 @@ extension AuthSessionStore { print(error) } return nil - } - - // TODO: concurrency problem if the friends array is not filled? prolly not - // get all inbox users' data - self.getIncomingNewMessageUserIds() + } } self.listenersActive = true @@ -387,55 +397,21 @@ extension AuthSessionStore { // transforming data to make it more valuable extension AuthSessionStore { - func getActiveFriends() -> [User] { - let activeFriendRelationships = self.userFriends.filter {userFriend in - return userFriend.isActive - } - let activeFriendsIds = activeFriendRelationships.map {activeUserFriend in - return activeUserFriend.id - } + func getActiveFriendIds() -> [String] { + var activeFriendIds: [String] = [] - // they should already be in the friends arr - return self.friendsArr.filter {friend in - return activeFriendsIds.contains(friend.id) - } - } - - func getInactiveFriends() -> [User] { - let inactiveFriendRelationships = self.userFriends.filter {userFriend in - return !userFriend.isActive - } - let inactiveFriendsIds = inactiveFriendRelationships.map {inactiveUserFriend in - return inactiveUserFriend.id - } - - // they should already be in the friends arr - return self.friendsArr.filter {friend in - return inactiveFriendsIds.contains(friend.id) - } - } - - // people info of people who are new to me and sent me a message...not active nor inactive - func getIncomingNewMessageUserIds() { - let allFriends = self.friendsArr.map {friend in - return friend.id - } - - let allIncomingPeopleIds = self.relevantMessagesByUserDict.keys.filter {friendId in - return !allFriends.contains(friendId) - } - - // make call to firestore to get data of each of these people - for newPersonId in allIncomingPeopleIds { - self.firestoreService.getUser(userId: newPersonId) {[weak self] returnedUser in - if returnedUser == nil { - print("couldn't get new user info") - return - } - - print("new user for inbox fetched") - self?.inboxUsers.append(returnedUser!) + for (friendId, userFriend) in self.userFriendsDict { + if userFriend.isActive { + activeFriendIds.append(friendId) } } + + return activeFriendIds + } + + func getInboxUsersIds() -> [String] { + return self.relevantUsersDict.keys.filter {userId in + return !self.userFriendsDict.keys.contains(userId) + } } } diff --git a/nirvana-ios/Views/Contacts/FindFriendsView.swift b/nirvana-ios/Views/Contacts/FindFriendsView.swift index c8ac53c..85a25a7 100644 --- a/nirvana-ios/Views/Contacts/FindFriendsView.swift +++ b/nirvana-ios/Views/Contacts/FindFriendsView.swift @@ -58,10 +58,13 @@ struct FindFriendsView: View { let keys = (Array(self.contactsVM.contacts.keys) as [String]).sorted() // checking if currcontact is already a friend - let currFriends = self.authSessionStore.friendsArr.map { $0.id } - let newPotentialFriends = keys.filter { - if let friend = self.contactsVM.contacts[$0]?.user { // seeing if this contact is an existing user - if currFriends.contains(friend.id) { // if this is already a friend + let activeFriendIds = self.authSessionStore.getActiveFriendIds() + + let newPotentialFriends = keys.filter {contactSortProp in + if let friend = self.contactsVM.contacts[contactSortProp]?.user { // seeing if this contact is an existing user + // if this is already an ACTIVE friend -> don't show + // we want to show inactive friends + if activeFriendIds.contains(friend.id!) { return false } diff --git a/nirvana-ios/Views/InnerCircle/CircleFooterView.swift b/nirvana-ios/Views/InnerCircle/CircleFooterView.swift index 3210fee..d422f9e 100644 --- a/nirvana-ios/Views/InnerCircle/CircleFooterView.swift +++ b/nirvana-ios/Views/InnerCircle/CircleFooterView.swift @@ -12,7 +12,7 @@ struct CircleFooterView: View { @EnvironmentObject var navigationStack: NavigationStack @EnvironmentObject var authSessionStore: AuthSessionStore - @Binding var selectedFriendIndex: Int? + @Binding var selectedFriendIndex: String? @State private var convoRelativeTime = "" @State private var selectedFriend: User? @@ -94,7 +94,7 @@ struct CircleFooterView: View { // set convo moment/relative time to show if newValue != nil && myId != nil { - self.selectedFriend = self.authSessionStore.friendsArr[newValue!] + self.selectedFriend = self.authSessionStore.relevantUsersDict[newValue!] // get the latest message's timestamp...will be the first in list now let lastMessage = self.authSessionStore.relevantMessagesByUserDict[self.selectedFriend!.id!]?.first diff --git a/nirvana-ios/Views/InnerCircle/CircleGridView.swift b/nirvana-ios/Views/InnerCircle/CircleGridView.swift index 8ef8068..0f99ecf 100644 --- a/nirvana-ios/Views/InnerCircle/CircleGridView.swift +++ b/nirvana-ios/Views/InnerCircle/CircleGridView.swift @@ -19,25 +19,21 @@ struct CircleGridView: View { let universalSize = UIScreen.main.bounds - var body: some View { - gridContent - } - // magic variables for grid // TODO: make the top left person or one horizontal person be in the center of screen...makes the top honeycomb pop - private static var numberOfItems: Int = 12 + @State var numberOfItems: Int = 12 private static let size: CGFloat = UIScreen.main.bounds.height*0.15 // scaling with screen size private static let spacingBetweenColumns: CGFloat = 0 private static let spacingBetweenRows: CGFloat = 0 - private static let totalColumns: Int = Int(log2(Double(Self.numberOfItems))) // scaling the circles and calculating column count - - @Binding var selectedFriendIndex: Int? + private static let totalColumns: Int = 3 // scaling the circles and calculating column count // TODO: change the size to adaptive or something to make outer ring items shrink their overall size and fit better let gridItems: [GridItem] = Array( repeating: GridItem(.fixed(Self.size), spacing: spacingBetweenColumns, alignment: .center), count: totalColumns) + @Binding var selectedFriendIndex: String? + private let big:CGFloat = 1 private let medium:CGFloat = 0.75 private let small:CGFloat = 0.5 @@ -49,10 +45,12 @@ struct CircleGridView: View { let longPressMinDuration = 0.5 - private var gridContent: some View { + var body: some View { // main communication hub // TODO: client side, sort the honeycomb from top left to bottom right // based on most close friends to least + let activeFriends = self.authSessionStore.getActiveFriendIds() + let inboxUsers = self.authSessionStore.getInboxUsersIds() ScrollViewReader {scrollReaderValue in ScrollView([.horizontal, .vertical], showsIndicators: false) { LazyVGrid( @@ -60,29 +58,27 @@ struct CircleGridView: View { alignment: .center, spacing: Self.spacingBetweenRows ) { - ForEach(Array(self.authSessionStore.friendsArr.enumerated()), id: \.offset) { value, friend in -// for (value, element) in self.authSessionStore.friendMessagesDict.keys.enumerated() { + // active friends + ForEach(0.. Bool { 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) + if let messagesRelatedToFriend = self.authSessionStore.relevantMessagesByUserDict[friendDbId] { // O(1) return messagesRelatedToFriend.first?.receiverId == userId } } @@ -179,7 +215,7 @@ struct CircleGridView: View { } private func getBubbleTint(friendIndex: Int, friendDbId: String) -> Color { - if (friendIndex == self.selectedFriendIndex) { // user clicked on this user + if (friendDbId == self.selectedFriendIndex) { // user clicked on this user return NirvanaColor.dimTeal.opacity(0.4) } else if self.haveNewMessageFromFriend(friendDbId: friendDbId) { // this user has a message @@ -202,9 +238,9 @@ struct CircleGridView_Previews: PreviewProvider { extension CircleGridView { // getting the proxy of an individual item // and decoding into a scale that the item should take - private func getScale(proxy: GeometryProxy, itemNumber: Int) -> CGFloat { + private func getScale(proxy: GeometryProxy, itemNumber: Int, userId: String) -> CGFloat { // if this user is selected - if itemNumber == self.selectedFriendIndex { + if userId == self.selectedFriendIndex { return big + 0.2 } @@ -269,18 +305,18 @@ extension CircleGridView { // extension for handling the gestures and actions extension CircleGridView { // listening to messages - private func handleTap(friendIndex: Int, friend: User) { + private func handleTap(gridItemIndex: Int, friendId: String) { print("tap gesture activated") // clearing the player to make room for this friend's convo or to deselect this user self.queuePlayer.removeAllItems() // if user had previously selected user, put nil as a toggle - if self.selectedFriendIndex == friendIndex { + if self.selectedFriendIndex == friendId { self.selectedFriendIndex = nil return } else { - self.selectedFriendIndex = friendIndex + self.selectedFriendIndex = friendId } // 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 @@ -293,7 +329,7 @@ extension CircleGridView { // 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!] ?? [] + let messagesRelatedToFriend = self.authSessionStore.relevantMessagesByUserDict[friendId] ?? [] if messagesRelatedToFriend.count == 0 { return diff --git a/nirvana-ios/Views/InnerCircle/InnerCircleView.swift b/nirvana-ios/Views/InnerCircle/InnerCircleView.swift index 5b9bb30..c2982d9 100644 --- a/nirvana-ios/Views/InnerCircle/InnerCircleView.swift +++ b/nirvana-ios/Views/InnerCircle/InnerCircleView.swift @@ -13,7 +13,7 @@ struct InnerCircleView: View { @EnvironmentObject var authSessionStore: AuthSessionStore @EnvironmentObject var navigationStack: NavigationStack - @State var selectedFriendIndex: Int? = nil + @State var selectedFriendIndex: String? = nil let universalSize = UIScreen.main.bounds @@ -56,7 +56,7 @@ struct InnerCircleView: View { } .padding() } - else if self.authSessionStore.friendsArr.count == 0 { + else if self.authSessionStore.getActiveFriendIds().count == 0 && self.authSessionStore.getInboxUsersIds().count == 0 { VStack(alignment: .center) { Image("undraw_fall_is_coming_yl-0-x") .renderingMode(.original) @@ -109,7 +109,7 @@ struct InnerCircleView: View { // helper for new users // TODO: make it back to 1 instead of 10...testing - if self.authSessionStore.friendsArr.count == 1 && self.selectedFriendIndex == nil { // don't show if bottom metadata showing + if self.authSessionStore.getActiveFriendIds().count == 1 && self.selectedFriendIndex == nil { // don't show if bottom metadata showing ZStack(alignment: .bottomTrailing) { Color.clear