/
DataStorageManager.swift
128 lines (114 loc) · 4.65 KB
/
DataStorageManager.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
//
// DataStorageManager.swift
// ChatFirestoreExample
//
// Created by Alisa Mylnikova on 13.07.2023.
//
import Foundation
import FirebaseFirestore
class DataStorageManager: ObservableObject {
static var shared = DataStorageManager()
@Published var users: [User] = [] // not including current user
@Published var allUsers: [User] = []
@Published var conversations: [Conversation] = []
func getUsers() async {
let snapshot = try? await Firestore.firestore()
.collection(Collection.users)
.getDocuments()
storeUsers(snapshot)
}
func getConversations() async {
let snapshot = try? await Firestore.firestore()
.collection(Collection.conversations)
.whereField("users", arrayContains: SessionManager.currentUserId)
.getDocuments()
storeConversations(snapshot)
}
func subscribeToUpdates() {
Firestore.firestore()
.collection(Collection.users)
.addSnapshotListener { [weak self] (snapshot, _) in
guard let self else { return }
self.storeUsers(snapshot)
Task {
await self.getConversations() // update in case some new user didn't make it in time for conversations subscription
}
}
Firestore.firestore()
.collection(Collection.conversations)
.whereField("users", arrayContains: SessionManager.currentUserId)
.addSnapshotListener() { [weak self] (snapshot, _) in
self?.storeConversations(snapshot)
}
}
private func storeUsers(_ snapshot: QuerySnapshot?) {
guard let currentUser = SessionManager.currentUser else { return }
DispatchQueue.main.async { [weak self] in
let users: [User] = snapshot?.documents
.compactMap { document in
let dict = document.data()
if document.documentID == currentUser.id {
return nil // skip current user
}
if let name = dict["nickname"] as? String {
let avatarURL = dict["avatarURL"] as? String
return User(id: document.documentID, name: name, avatarURL: URL(string: avatarURL ?? ""), isCurrentUser: false)
}
return nil
} ?? []
self?.users = users
self?.allUsers = users + [currentUser]
}
}
private func storeConversations(_ snapshot: QuerySnapshot?) {
DispatchQueue.main.async { [weak self] in
self?.conversations = snapshot?.documents
.compactMap { [weak self] document in
do {
let firestoreConversation = try document.data(as: FirestoreConversation.self)
return self?.makeConversation(document.documentID, firestoreConversation)
} catch {
print(error)
}
return nil
}.sorted {
if let date1 = $0.latestMessage?.createdAt, let date2 = $1.latestMessage?.createdAt {
return date1 > date2
}
return $0.displayTitle < $1.displayTitle
}
?? []
}
}
private func makeConversation(_ id: String, _ firestoreConversation: FirestoreConversation) -> Conversation {
var message: LatestMessageInChat? = nil
if let flm = firestoreConversation.latestMessage,
let user = allUsers.first(where: { $0.id == flm.userId }) {
var subtext: String?
if !flm.attachments.isEmpty, let first = flm.attachments.first {
subtext = first.type.title
} else if flm.recording != nil {
subtext = "Voice recording"
}
message = LatestMessageInChat(
senderName: user.name,
createdAt: flm.createdAt,
text: flm.text.isEmpty ? nil : flm.text,
subtext: subtext
)
}
let users = firestoreConversation.users.compactMap { id in
allUsers.first(where: { $0.id == id })
}
let conversation = Conversation(
id: id,
users: users,
usersUnreadCountInfo: firestoreConversation.usersUnreadCountInfo,
isGroup: firestoreConversation.isGroup,
pictureURL: firestoreConversation.pictureURL?.toURL(),
title: firestoreConversation.title,
latestMessage: message
)
return conversation
}
}