-
-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #33 from jaesung-0o0/feature/jaesung/next-message-…
…field Added `NextMessageField` to provide easy-customizable contents
- Loading branch information
Showing
4 changed files
with
346 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
184 changes: 184 additions & 0 deletions
184
Sources/ChatUI/ChatInChannel/MessageField/NextMessageField.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
// | ||
// NextMessageField.swift | ||
// | ||
// | ||
// Created by Jaesung Lee on 2023/03/28. | ||
// | ||
|
||
import SwiftUI | ||
|
||
// TODO: Provide pre-implemented send button | ||
|
||
/** | ||
The view for sending messages. | ||
|
||
When creating a ``NextMessageField``, you can provide an action for how to handle a new ``MessageStyle`` information in the `onSend` parameter. ``MessageStyle`` contains different types of messages, such as text, media (photo, video, document, contact) and voice. you can also provide a message-sending button by using ``sendMessagePublisher`` in the `rightLabel`. ``sendMessagePublisher`` will invoke `onSend` handler. | ||
|
||
```swift | ||
@State private var text: String = "" | ||
|
||
NextMessageField(text) { messageStyle in | ||
guard !text.isEmpty else { return } | ||
viewModel.sendMessage($0) | ||
text = "" | ||
} rightLabel: { | ||
Button { | ||
// send message by using `sendMessagePublisher`. This will invoke `onSend` handler. | ||
sendMessagePublisher.send(.text(text)) | ||
} label: { | ||
// send button icon | ||
Image.send.medium | ||
} | ||
.frame(width: 36, height: 36) | ||
} | ||
``` | ||
|
||
To add some button on the left of the text field, | ||
```swift | ||
NextMessageField(text) { messageStyle in | ||
... | ||
} leftLabel: { | ||
HStack { | ||
Button(aciton: showCamera) { | ||
Image.camera.medium | ||
} | ||
.frame(width: 36, height: 36) | ||
|
||
Button(action: showLibrary) { | ||
Image.photoLibrary.medium | ||
} | ||
.frame(width: 36, height: 36) | ||
} | ||
``` | ||
|
||
To add some button on the right of the text field, | ||
```swift | ||
NextMessageField(text) { messageStyle in | ||
... | ||
} rightLabel: { | ||
HStack { | ||
Button(aciton: { sendMessagePublisher.send(.text(text)) }) { | ||
Image.send.medium | ||
} | ||
.frame(width: 36, height: 36) | ||
} | ||
} | ||
``` | ||
|
||
To publish a new message, you can create a new `MessageStyle` object and send it using `send(_:)`. | ||
|
||
```swift | ||
// Create `MessageStyle` object | ||
let style = MessageStyle.text("{TEXT}") | ||
// Publish the created style object via `send(_:)` | ||
sendMessagePublisher.send(style) | ||
``` | ||
|
||
You can make other views to subscribe to `sendMessagePublisher` to handle new messages. | ||
|
||
```swift | ||
.onReceive(sendMessagePublisher) { messageStyle in | ||
// Handle `messageStyle` here (e.g., sending message with the style) | ||
} | ||
``` | ||
*/ | ||
public struct NextMessageField<LeftLabel: View, RightLabel: View>: View { | ||
@EnvironmentObject private var configuration: ChatConfiguration | ||
|
||
@Environment(\.appearance) var appearance | ||
|
||
@Binding public var text: String | ||
|
||
@FocusState private var isTextFieldFocused: Bool | ||
@State private var textFieldHeight: CGFloat = 20 | ||
|
||
let leftLabel: (() -> LeftLabel)? | ||
let rightLabel: (() -> RightLabel)? | ||
let showsSendButtonAlways: Bool = false | ||
let characterLimit: Int? | ||
let onSend: (_ messageStyle: MessageStyle) -> () | ||
|
||
public var body: some View { | ||
HStack(alignment: .bottom) { | ||
if let leftLabel { | ||
leftLabel() | ||
.tint(appearance.tint) | ||
} | ||
|
||
// TextField | ||
HStack(alignment: .bottom) { | ||
MessageTextField(text: $text, height: $textFieldHeight, characterLimit: characterLimit) | ||
.frame(height: textFieldHeight < 90 ? textFieldHeight : 90) | ||
.padding(.leading, 9) | ||
.padding(.trailing, 4) | ||
.focused($isTextFieldFocused) | ||
} | ||
.padding(6) | ||
.background { | ||
appearance.secondaryBackground | ||
.clipShape(RoundedRectangle(cornerRadius: 18)) | ||
} | ||
|
||
if let rightLabel { | ||
rightLabel() | ||
.tint(appearance.tint) | ||
} | ||
} | ||
.padding(16) | ||
.onReceive(sendMessagePublisher) { messageStyle in | ||
onSend(messageStyle) | ||
} | ||
} | ||
|
||
public init( | ||
_ text: Binding<String>, | ||
characterLimit: Int? = nil, | ||
onSend: @escaping (_ messageStyle: MessageStyle) -> (), | ||
@ViewBuilder leftLabel: @escaping () -> LeftLabel, | ||
@ViewBuilder rightLabel: @escaping () -> RightLabel | ||
) { | ||
self._text = text | ||
self.characterLimit = characterLimit | ||
self.onSend = onSend | ||
self.leftLabel = leftLabel | ||
self.rightLabel = rightLabel | ||
} | ||
|
||
public init( | ||
_ text: Binding<String>, | ||
characterLimit: Int? = nil, | ||
onSend: @escaping (_ messageStyle: MessageStyle) -> (), | ||
@ViewBuilder rightLabel: @escaping () -> RightLabel | ||
) where LeftLabel == EmptyView { | ||
self._text = text | ||
self.characterLimit = characterLimit | ||
self.onSend = onSend | ||
self.leftLabel = nil | ||
self.rightLabel = rightLabel | ||
} | ||
|
||
public init( | ||
_ text: Binding<String>, | ||
characterLimit: Int? = nil, | ||
onSend: @escaping (_ messageStyle: MessageStyle) -> (), | ||
@ViewBuilder leftLabel: @escaping () -> LeftLabel | ||
) where RightLabel == EmptyView { | ||
self._text = text | ||
self.characterLimit = characterLimit | ||
self.onSend = onSend | ||
self.leftLabel = leftLabel | ||
self.rightLabel = nil | ||
} | ||
|
||
public init( | ||
_ text: Binding<String>, | ||
characterLimit: Int? = nil, | ||
onSend: @escaping (_ messageStyle: MessageStyle) -> () | ||
) where LeftLabel == EmptyView, RightLabel == EmptyView { | ||
self._text = text | ||
self.characterLimit = characterLimit | ||
self.onSend = onSend | ||
self.leftLabel = nil | ||
self.rightLabel = nil | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
70 changes: 70 additions & 0 deletions
70
Sources/ChatUI/Previews/ChatInChannel/NextMessageField.Previews.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
// | ||
// NextMessageField.Previews.swift | ||
// | ||
// | ||
// Created by Jaesung Lee on 2023/08/02. | ||
// | ||
|
||
import SwiftUI | ||
|
||
struct NextMessageField_Previews: PreviewProvider { | ||
static var previews: some View { | ||
Preview() | ||
.previewDisplayName("Next Message Field") | ||
} | ||
|
||
struct Preview: View { | ||
@State private var pendingMessage: Message? | ||
@State private var text: String = "" | ||
|
||
var body: some View { | ||
VStack { | ||
if let pendingMessage = pendingMessage { | ||
Text(pendingMessage.id) | ||
|
||
Text(String(describing: pendingMessage.style)) | ||
} | ||
|
||
Spacer() | ||
|
||
NextMessageField($text) { messageStyle in | ||
pendingMessage = Message( | ||
id: UUID().uuidString, | ||
sender: User.user1, | ||
sentAt: Date().timeIntervalSince1970, | ||
readReceipt: .sending, | ||
style: messageStyle | ||
) | ||
} leftLabel: { | ||
HStack { | ||
Button(action: {}) { | ||
Image.camera.medium | ||
} | ||
.frame(width: 36, height: 36) | ||
|
||
Button(action: {}) { | ||
Image.photoLibrary.medium | ||
} | ||
.frame(width: 36, height: 36) | ||
|
||
Button(action: {}) { | ||
Image.mic.medium | ||
} | ||
.frame(width: 36, height: 36) | ||
} | ||
} rightLabel: { | ||
Button { | ||
sendMessagePublisher.send(.text(text)) | ||
} label: { | ||
Image.send.medium | ||
} | ||
.frame(width: 36, height: 36) | ||
} | ||
.environment(\.appearance, Appearance()) | ||
|
||
} | ||
} | ||
} | ||
|
||
} | ||
|