Skip to content
Snippets Groups Projects
Commit 3a280205 authored by Dariusz Rybicki's avatar Dariusz Rybicki
Browse files

Implement sending image in ChatFeature

parent b0361af7
No related branches found
No related tags found
2 merge requests!124File transfers example,!102Release 1.0.0
...@@ -99,6 +99,11 @@ extension AppEnvironment { ...@@ -99,6 +99,11 @@ extension AppEnvironment {
db: dbManager.getDB, db: dbManager.getDB,
now: Date.init now: Date.init
), ),
sendImage: .live(
messenger: messenger,
db: dbManager.getDB,
now: Date.init
),
mainQueue: mainQueue, mainQueue: mainQueue,
bgQueue: bgQueue bgQueue: bgQueue
) )
......
...@@ -66,6 +66,7 @@ public enum ChatAction: Equatable, BindableAction { ...@@ -66,6 +66,7 @@ public enum ChatAction: Equatable, BindableAction {
case didFetchMessages(IdentifiedArrayOf<ChatState.Message>) case didFetchMessages(IdentifiedArrayOf<ChatState.Message>)
case sendTapped case sendTapped
case sendFailed(String) case sendFailed(String)
case imagePicked(Data)
case dismissSendFailureTapped case dismissSendFailureTapped
case binding(BindingAction<ChatState>) case binding(BindingAction<ChatState>)
} }
...@@ -75,12 +76,14 @@ public struct ChatEnvironment { ...@@ -75,12 +76,14 @@ public struct ChatEnvironment {
messenger: Messenger, messenger: Messenger,
db: DBManagerGetDB, db: DBManagerGetDB,
sendMessage: SendMessage, sendMessage: SendMessage,
sendImage: SendImage,
mainQueue: AnySchedulerOf<DispatchQueue>, mainQueue: AnySchedulerOf<DispatchQueue>,
bgQueue: AnySchedulerOf<DispatchQueue> bgQueue: AnySchedulerOf<DispatchQueue>
) { ) {
self.messenger = messenger self.messenger = messenger
self.db = db self.db = db
self.sendMessage = sendMessage self.sendMessage = sendMessage
self.sendImage = sendImage
self.mainQueue = mainQueue self.mainQueue = mainQueue
self.bgQueue = bgQueue self.bgQueue = bgQueue
} }
...@@ -88,6 +91,7 @@ public struct ChatEnvironment { ...@@ -88,6 +91,7 @@ public struct ChatEnvironment {
public var messenger: Messenger public var messenger: Messenger
public var db: DBManagerGetDB public var db: DBManagerGetDB
public var sendMessage: SendMessage public var sendMessage: SendMessage
public var sendImage: SendImage
public var mainQueue: AnySchedulerOf<DispatchQueue> public var mainQueue: AnySchedulerOf<DispatchQueue>
public var bgQueue: AnySchedulerOf<DispatchQueue> public var bgQueue: AnySchedulerOf<DispatchQueue>
} }
...@@ -98,6 +102,7 @@ extension ChatEnvironment { ...@@ -98,6 +102,7 @@ extension ChatEnvironment {
messenger: .unimplemented, messenger: .unimplemented,
db: .unimplemented, db: .unimplemented,
sendMessage: .unimplemented, sendMessage: .unimplemented,
sendImage: .unimplemented,
mainQueue: .unimplemented, mainQueue: .unimplemented,
bgQueue: .unimplemented bgQueue: .unimplemented
) )
...@@ -196,6 +201,28 @@ public let chatReducer = Reducer<ChatState, ChatAction, ChatEnvironment> ...@@ -196,6 +201,28 @@ public let chatReducer = Reducer<ChatState, ChatAction, ChatEnvironment>
state.sendFailure = failure state.sendFailure = failure
return .none return .none
case .imagePicked(let data):
let chatId = state.id
return Effect.run { subscriber in
switch chatId {
case .contact(let recipientId):
env.sendImage(
data,
to: recipientId,
onError: { error in
subscriber.send(.sendFailed(error.localizedDescription))
},
completion: {
subscriber.send(completion: .finished)
}
)
}
return AnyCancellable {}
}
.subscribe(on: env.bgQueue)
.receive(on: env.mainQueue)
.eraseToEffect()
case .dismissSendFailureTapped: case .dismissSendFailureTapped:
state.sendFailure = nil state.sendFailure = nil
return .none return .none
......
...@@ -8,6 +8,7 @@ public struct ChatView: View { ...@@ -8,6 +8,7 @@ public struct ChatView: View {
} }
let store: Store<ChatState, ChatAction> let store: Store<ChatState, ChatAction>
@State var isPresentingImagePicker = false
struct ViewState: Equatable { struct ViewState: Equatable {
var myContactId: Data? var myContactId: Data?
...@@ -87,12 +88,28 @@ public struct ChatView: View { ...@@ -87,12 +88,28 @@ public struct ChatView: View {
)) ))
.textFieldStyle(.roundedBorder) .textFieldStyle(.roundedBorder)
Button { if viewStore.text.isEmpty == false {
viewStore.send(.sendTapped) Button {
} label: { viewStore.send(.sendTapped)
Image(systemName: "paperplane.fill") } label: {
Image(systemName: "paperplane.fill")
}
.buttonStyle(.borderedProminent)
} else {
Button {
isPresentingImagePicker = true
} label: {
Image(systemName: "photo.on.rectangle.angled")
}
.buttonStyle(.borderedProminent)
.sheet(isPresented: $isPresentingImagePicker) {
ImagePicker { image in
if let data = image.jpegData(compressionQuality: 0.7) {
viewStore.send(.imagePicked(data))
}
}
}
} }
.buttonStyle(.borderedProminent)
} }
.padding() .padding()
} }
......
...@@ -240,4 +240,70 @@ final class ChatFeatureTests: XCTestCase { ...@@ -240,4 +240,70 @@ final class ChatFeatureTests: XCTestCase {
$0.sendFailure = nil $0.sendFailure = nil
} }
} }
func testSendImage() {
struct SendImageParams: Equatable {
var image: Data
var recipientId: Data
}
var didSendImageWithParams: [SendImageParams] = []
var sendImageCompletion: SendImage.Completion?
let store = TestStore(
initialState: ChatState(id: .contact("contact-id".data(using: .utf8)!)),
reducer: chatReducer,
environment: .unimplemented
)
store.environment.mainQueue = .immediate
store.environment.bgQueue = .immediate
store.environment.sendImage.run = { image, recipientId, _, completion in
didSendImageWithParams.append(.init(image: image, recipientId: recipientId))
sendImageCompletion = completion
}
let image = "image-data".data(using: .utf8)!
store.send(.imagePicked(image))
XCTAssertNoDifference(didSendImageWithParams, [
.init(image: image, recipientId: "contact-id".data(using: .utf8)!)
])
sendImageCompletion?()
}
func testSendImageFailure() {
var sendImageOnError: SendImage.OnError?
var sendImageCompletion: SendImage.Completion?
let store = TestStore(
initialState: ChatState(
id: .contact("contact-id".data(using: .utf8)!)
),
reducer: chatReducer,
environment: .unimplemented
)
store.environment.mainQueue = .immediate
store.environment.bgQueue = .immediate
store.environment.sendImage.run = { _, _, onError, completion in
sendImageOnError = onError
sendImageCompletion = completion
}
store.send(.imagePicked(Data()))
let error = NSError(domain: "test", code: 123)
sendImageOnError?(error)
store.receive(.sendFailed(error.localizedDescription)) {
$0.sendFailure = error.localizedDescription
}
sendImageCompletion?()
store.send(.dismissSendFailureTapped) {
$0.sendFailure = nil
}
}
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment