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
Branches
Tags
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)
if viewStore.text.isEmpty == false {
Button { Button {
viewStore.send(.sendTapped) viewStore.send(.sendTapped)
} label: { } label: {
Image(systemName: "paperplane.fill") Image(systemName: "paperplane.fill")
} }
.buttonStyle(.borderedProminent) .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))
}
}
}
}
} }
.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.
Please register or to comment