diff --git a/Examples/xx-messenger/Sources/ChatFeature/ChatComponent.swift b/Examples/xx-messenger/Sources/ChatFeature/ChatComponent.swift index 66585eddbfa3747940dd76e9ff9293103f187587..def3f14cdbae265ed27077c7e88a96416b924a49 100644 --- a/Examples/xx-messenger/Sources/ChatFeature/ChatComponent.swift +++ b/Examples/xx-messenger/Sources/ChatFeature/ChatComponent.swift @@ -19,6 +19,7 @@ public struct ChatComponent: ReducerProtocol { id: Int64, date: Date, senderId: Data, + senderName: String?, text: String, status: XXModels.Message.Status, fileTransfer: XXModels.FileTransfer? = nil @@ -26,6 +27,7 @@ public struct ChatComponent: ReducerProtocol { self.id = id self.date = date self.senderId = senderId + self.senderName = senderName self.text = text self.status = status self.fileTransfer = fileTransfer @@ -34,6 +36,7 @@ public struct ChatComponent: ReducerProtocol { public var id: Int64 public var date: Date public var senderId: Data + public var senderName: String? public var text: String public var status: XXModels.Message.Status public var fileTransfer: XXModels.FileTransfer? @@ -120,20 +123,21 @@ public struct ChatComponent: ReducerProtocol { let messagesQuery = XXModels.Message.Query(chat: queryChat) return Publishers.CombineLatest3( try db().fetchMessagesPublisher(messagesQuery), - receivedFileTransfersPublisher, - sentFileTransfersPublisher + try db().fetchContactsPublisher(.init()), + Publishers.CombineLatest( + receivedFileTransfersPublisher, + sentFileTransfersPublisher + ).map(+) ) - .map { messages, receivedFileTransfers, sentFileTransfers in - (messages, receivedFileTransfers + sentFileTransfers) - } .assertNoFailure() - .map { messages, fileTransfers in - messages.compactMap { message in + .map { messages, contacts, fileTransfers -> [State.Message] in + messages.compactMap { message -> State.Message? in guard let id = message.id else { return nil } return State.Message( id: id, date: message.date, senderId: message.senderId, + senderName: contacts.first { $0.id == message.senderId }?.username, text: message.text, status: message.status, fileTransfer: fileTransfers.first { $0.id == message.fileTransferId } diff --git a/Examples/xx-messenger/Sources/ChatFeature/ChatView.swift b/Examples/xx-messenger/Sources/ChatFeature/ChatView.swift index ac0d088440faf4d8b381c9aec05be9968fc1ba4e..a7272385a3241f5d46890a88039edefeb7b41d80 100644 --- a/Examples/xx-messenger/Sources/ChatFeature/ChatView.swift +++ b/Examples/xx-messenger/Sources/ChatFeature/ChatView.swift @@ -147,6 +147,13 @@ public struct ChatView: View { var body: some View { VStack { + if let sender = message.senderName { + Text(sender) + .foregroundColor(.secondary) + .font(.footnote) + .frame(maxWidth: .infinity, alignment: alignment) + } + Text("\(message.date.formatted()), \(statusText)") .foregroundColor(.secondary) .font(.footnote) @@ -216,6 +223,7 @@ public struct ChatView_Previews: PreviewProvider { id: 1, date: Date(), senderId: "contact-id".data(using: .utf8)!, + senderName: "Contact", text: "Hello!", status: .received ), @@ -223,6 +231,7 @@ public struct ChatView_Previews: PreviewProvider { id: 2, date: Date(), senderId: "my-contact-id".data(using: .utf8)!, + senderName: "Me", text: "Hi!", status: .sent ), @@ -230,6 +239,7 @@ public struct ChatView_Previews: PreviewProvider { id: 3, date: Date(), senderId: "contact-id".data(using: .utf8)!, + senderName: "Contact", text: "", status: .received, fileTransfer: .init( @@ -245,6 +255,7 @@ public struct ChatView_Previews: PreviewProvider { id: 4, date: Date(), senderId: "my-contact-id".data(using: .utf8)!, + senderName: "Me", text: "", status: .sent, fileTransfer: .init( diff --git a/Examples/xx-messenger/Tests/ChatFeatureTests/ChatComponentTests.swift b/Examples/xx-messenger/Tests/ChatFeatureTests/ChatComponentTests.swift index fb77001cdecd6b590d5760d6421b4cdaa8259b61..ba7ed23792f71c169b42daffc10fec988caef3a5 100644 --- a/Examples/xx-messenger/Tests/ChatFeatureTests/ChatComponentTests.swift +++ b/Examples/xx-messenger/Tests/ChatFeatureTests/ChatComponentTests.swift @@ -22,6 +22,8 @@ final class ChatComponentTests: XCTestCase { let messagesPublisher = PassthroughSubject<[XXModels.Message], Error>() var didFetchFileTransfersWithQuery: [XXModels.FileTransfer.Query] = [] let fileTransfersPublisher = PassthroughSubject<[XXModels.FileTransfer], Error>() + var didFetchContactsWithQuery: [XXModels.Contact.Query] = [] + let contactsPublisher = PassthroughSubject<[XXModels.Contact], Error>() store.dependencies.app.mainQueue = .immediate store.dependencies.app.bgQueue = .immediate @@ -40,6 +42,10 @@ final class ChatComponentTests: XCTestCase { didFetchMessagesWithQuery.append(query) return messagesPublisher.eraseToAnyPublisher() } + db.fetchContactsPublisher.run = { query in + didFetchContactsWithQuery.append(query) + return contactsPublisher.eraseToAnyPublisher() + } db.fetchFileTransfersPublisher.run = { query in didFetchFileTransfersWithQuery.append(query) return fileTransfersPublisher.eraseToAnyPublisher() @@ -58,6 +64,9 @@ final class ChatComponentTests: XCTestCase { .init(contactId: contactId, isIncoming: true), .init(contactId: myContactId, isIncoming: false), ]) + XCTAssertNoDifference(didFetchContactsWithQuery, [ + .init(), + ]) let receivedFileTransfer = FileTransfer( id: "file-transfer-1-id".data(using: .utf8)!, @@ -111,12 +120,17 @@ final class ChatComponentTests: XCTestCase { receivedFileTransfer, sentFileTransfer, ]) + contactsPublisher.send([ + .init(id: myContactId, username: "My username"), + .init(id: contactId, username: "Contact username"), + ]) let expectedMessages = IdentifiedArrayOf<ChatComponent.State.Message>(uniqueElements: [ .init( id: 1, date: Date(timeIntervalSince1970: 1), senderId: contactId, + senderName: "Contact username", text: "Message 1", status: .received, fileTransfer: receivedFileTransfer @@ -125,6 +139,7 @@ final class ChatComponentTests: XCTestCase { id: 2, date: Date(timeIntervalSince1970: 2), senderId: myContactId, + senderName: "My username", text: "Message 2", status: .sent, fileTransfer: sentFileTransfer @@ -137,6 +152,7 @@ final class ChatComponentTests: XCTestCase { messagesPublisher.send(completion: .finished) fileTransfersPublisher.send(completion: .finished) + contactsPublisher.send(completion: .finished) } func testStartGroupChat() { @@ -152,6 +168,8 @@ final class ChatComponentTests: XCTestCase { var didFetchMessagesWithQuery: [XXModels.Message.Query] = [] let messagesPublisher = PassthroughSubject<[XXModels.Message], Error>() + var didFetchContactsWithQuery: [XXModels.Contact.Query] = [] + let contactsPublisher = PassthroughSubject<[XXModels.Contact], Error>() store.dependencies.app.mainQueue = .immediate store.dependencies.app.bgQueue = .immediate @@ -170,6 +188,10 @@ final class ChatComponentTests: XCTestCase { didFetchMessagesWithQuery.append(query) return messagesPublisher.eraseToAnyPublisher() } + db.fetchContactsPublisher.run = { query in + didFetchContactsWithQuery.append(query) + return contactsPublisher.eraseToAnyPublisher() + } return db } @@ -180,6 +202,9 @@ final class ChatComponentTests: XCTestCase { XCTAssertNoDifference(didFetchMessagesWithQuery, [ .init(chat: .group(groupId)) ]) + XCTAssertNoDifference(didFetchContactsWithQuery, [ + .init(), + ]) messagesPublisher.send([ .init( @@ -213,12 +238,18 @@ final class ChatComponentTests: XCTestCase { text: "Message 2" ), ]) + contactsPublisher.send([ + .init(id: myContactId, username: "My username"), + .init(id: firstMemberId, username: "First username"), + .init(id: secondMemberId, username: "Second username"), + ]) let expectedMessages = IdentifiedArrayOf<ChatComponent.State.Message>(uniqueElements: [ .init( id: 0, date: Date(timeIntervalSince1970: 0), senderId: myContactId, + senderName: "My username", text: "Message 0", status: .sent ), @@ -226,6 +257,7 @@ final class ChatComponentTests: XCTestCase { id: 1, date: Date(timeIntervalSince1970: 1), senderId: firstMemberId, + senderName: "First username", text: "Message 1", status: .received ), @@ -233,6 +265,7 @@ final class ChatComponentTests: XCTestCase { id: 2, date: Date(timeIntervalSince1970: 2), senderId: secondMemberId, + senderName: "Second username", text: "Message 2", status: .received ), @@ -243,6 +276,7 @@ final class ChatComponentTests: XCTestCase { } messagesPublisher.send(completion: .finished) + contactsPublisher.send(completion: .finished) } func testStartFailure() {