decent place with the whole refactoring but not done, and not tested at all
This commit is contained in:
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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..<activeFriends.count, id: \.self) {value in
|
||||
let friendId = activeFriends[value]
|
||||
GeometryReader {gridProxy in
|
||||
let scale = getScale(proxy: gridProxy, itemNumber: value)
|
||||
|
||||
// TODO: sort the list but may already be sorted from the query and creation of the array of messages?
|
||||
// shouldn't be nil...hopefully
|
||||
let scale = getScale(proxy: gridProxy, itemNumber: value, userId: friendId)
|
||||
|
||||
ZStack(alignment: .topTrailing) {
|
||||
// check if the last message in the conversation between me and my friend was me talking or him
|
||||
// also check if I have listened to it once or twice
|
||||
if self.haveNewMessageFromFriend(friendDbId: friend.id!) { // him talking
|
||||
if self.haveNewMessageFromFriend(friendDbId: friendId) { // him talking
|
||||
Image(systemName: "wave.3.right.circle.fill")
|
||||
.foregroundColor(Color.orange)
|
||||
.font(.title)
|
||||
}
|
||||
|
||||
Circle()
|
||||
.foregroundColor(self.getBubbleTint(friendIndex: value, friendDbId: friend.id!)) // different color for a selected user
|
||||
.foregroundColor(self.getBubbleTint(friendIndex: value, friendDbId: friendId)) // different color for a selected user
|
||||
.blur(radius: 8)
|
||||
.cornerRadius(100)
|
||||
|
||||
Image(friend.avatar ?? Avatars.avatarSystemNames[0])
|
||||
Image(self.authSessionStore.relevantUsersDict[friendId]?.avatar ?? Avatars.avatarSystemNames[0])
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.shadow(color: Color.black.opacity(0.2), radius: 10, x: 0, y: 20)
|
||||
@@ -94,10 +90,10 @@ struct CircleGridView: View {
|
||||
x: honeycombOffSetX(value),
|
||||
y: 0
|
||||
)
|
||||
.id(value) // id for scrollviewreader
|
||||
.id(friendId) // id for scrollviewreader
|
||||
.frame(height: Self.size)
|
||||
.onTapGesture {
|
||||
self.handleTap(friendIndex: value, friend: friend)
|
||||
self.handleTap(gridItemIndex: value, friendId: friendId)
|
||||
}// TODO: maybe add simulataneous gesture or sequence? with the tap gesture?
|
||||
.gesture(
|
||||
LongPressGesture(minimumDuration: longPressMinDuration)
|
||||
@@ -106,7 +102,7 @@ struct CircleGridView: View {
|
||||
self.queuePlayer.removeAllItems()
|
||||
|
||||
print("activated long press!")
|
||||
self.selectedFriendIndex = value
|
||||
self.selectedFriendIndex = friendId
|
||||
|
||||
self.activateHaptics()
|
||||
|
||||
@@ -137,20 +133,57 @@ struct CircleGridView: View {
|
||||
|
||||
self.selectedFriendIndex = nil
|
||||
|
||||
self.innerCircleVM.stopRecording(senderId: self.authSessionStore.user!.id!, receiver: friend)
|
||||
self.innerCircleVM.stopRecording(senderId: self.authSessionStore.user!.id!, receiver: self.authSessionStore.relevantUsersDict[friendId]!)
|
||||
|
||||
// self.recordingGestureDeactived()
|
||||
}
|
||||
)
|
||||
// .simultaneousGesture(
|
||||
// DragGesture(minimumDistance: 0, coordinateSpace: .local)
|
||||
// .onChanged {_ in
|
||||
// self.recordingGestureActive(friendIndex: value, friend: friend)
|
||||
// }
|
||||
// .onEnded {_ in
|
||||
// self.recordingGestureDeactived()
|
||||
// }
|
||||
// )
|
||||
.animation(Animation.spring())
|
||||
}
|
||||
|
||||
// ForEach(Array(self.authSessionStore.getInboxUsersIds.enumerated()), id: \.offset) { inboxValue, inboxUser in
|
||||
// for (inboxValue, inboxUserId) in inboxUsers.enumerated() {
|
||||
ForEach(0..<inboxUsers.count, id: \.self) {inboxValue in
|
||||
let inboxUserId = inboxUsers[inboxValue]
|
||||
let adjustedValue = inboxValue + activeFriends.count
|
||||
GeometryReader {gridProxy in
|
||||
// IMPORTANT: accounting for the active friends iterations
|
||||
|
||||
let scale = getScale(proxy: gridProxy, itemNumber: adjustedValue, userId: inboxUserId)
|
||||
|
||||
ZStack(alignment: .topLeading) {
|
||||
// check if the last message in the conversation between me and my friend was me talking or him
|
||||
// also check if I have listened to it once or twice
|
||||
if self.haveNewMessageFromFriend(friendDbId: inboxUserId) { // him talking
|
||||
Image(systemName: "wave.3.right.circle.fill")
|
||||
.foregroundColor(Color.orange)
|
||||
.font(.title)
|
||||
}
|
||||
|
||||
Circle()
|
||||
.foregroundColor(self.getBubbleTint(friendIndex: adjustedValue, friendDbId: inboxUserId)) // different color for a selected user
|
||||
.blur(radius: 8)
|
||||
.cornerRadius(100)
|
||||
|
||||
Image(self.authSessionStore.relevantUsersDict[inboxUserId]?.avatar ?? Avatars.avatarSystemNames[0])
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.shadow(color: Color.black.opacity(0.2), radius: 10, x: 0, y: 20)
|
||||
}
|
||||
.scaleEffect(scale)
|
||||
.padding(scale * 5)
|
||||
} // geometry reader
|
||||
.offset(
|
||||
x: honeycombOffSetX(adjustedValue),
|
||||
y: 0
|
||||
)
|
||||
.id(inboxUserId) // id for scrollviewreader
|
||||
.frame(height: Self.size)
|
||||
.onTapGesture {
|
||||
// action to open alert to add this person to circle or reject
|
||||
|
||||
// create user_friend with isActive = false
|
||||
}
|
||||
.animation(Animation.spring())
|
||||
}
|
||||
} // TODO: add padding based on if we are on any cornering item to allow the bubble to enlargen
|
||||
@@ -161,16 +194,19 @@ struct CircleGridView: View {
|
||||
//TODO: was causing problems so commenting out
|
||||
//may not need anymore with bottom nav activation
|
||||
// scrollReaderValue.scrollTo(Self.numberOfItems / 2)
|
||||
|
||||
}
|
||||
} // scrollview reader
|
||||
}
|
||||
|
||||
|
||||
|
||||
private func haveNewMessageFromFriend(friendDbId: String) -> 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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user