Skip to content

Commit

Permalink
Added docs for inline replies in the SwiftUI SDK (#1694)
Browse files Browse the repository at this point in the history
  • Loading branch information
martinmitrevski committed Dec 16, 2021
1 parent 7319bc3 commit 74e0873
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 40 deletions.
9 changes: 6 additions & 3 deletions docusaurus/docs/iOS/swiftui/components/attachments.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,13 @@ class CustomFactory: ViewFactory {
func makeMessageTextView(
for message: ChatMessage,
isFirst: Bool,
availableWidth: CGFloat
availableWidth: CGFloat,
scrolledId: Binding<String?>
) -> some View {
CustomMessageTextView(
message: message,
isFirst: isFirst
isFirst: isFirst,
scrolledId: scrolledId
)
}

Expand Down Expand Up @@ -146,7 +148,8 @@ Next, in our `CustomFactory`, we need to return the new view we have created abo
func makeCustomAttachmentViewType(
for message: ChatMessage,
isFirst: Bool,
availableWidth: CGFloat
availableWidth: CGFloat,
scrolledId: Binding<String?>
) -> some View {
CustomAttachmentView(
message: message,
Expand Down
47 changes: 47 additions & 0 deletions docusaurus/docs/iOS/swiftui/components/inline-replies.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---
title: Message inline replies
---

## Inline Replies Overview

The SwiftUI SDK has support for inline message replies. These replies are invoked either by swiping left on a message, or via long pressing a message and selecting the "Reply" action. When a message is quoted, it appears in the message composer, as an indication which message the user is replying to.

## Customizing the Quoted Message

When a message appears as quoted in the composer, you can customize two parts. First, there's a header at the top of the composer, which by default has a title and a button to remove the quoted message from the composer. To swap this view with your own implementation, you need to implement the `makeQuotedMessageHeaderView` method in the `ViewFactory`, which passes a binding of the quoted chat message.

```swift
public func makeQuotedMessageHeaderView(
quotedMessage: Binding<ChatMessage?>
) -> some View {
CustomQuotedMessageHeaderView(quotedMessage: quotedMessage)
}
```

The second customization that can be done is to swap the preview of the message shown in the composer. The default implementation is able to present several different attachments, such as text, image, video, gifs, links and files. The UI consists of small image and either the message text (if present), or a description for the attachment.

In order to swap this UI, you need to implement the `makeQuotedMessageComposerView` method in the `ViewFactory`. The method passes the quoted message as a parameter.

```swift
public func makeQuotedMessageComposerView(
quotedMessage: ChatMessage
) -> some View {
QuotedMessageViewContainer(
quotedMessage: quotedMessage,
fillAvailableSpace: true,
forceLeftToRight: true,
scrolledId: .constant(nil)
)
}
```


Finally, we need to inject the your `CustomFactory` in our view hierarchy.

```swift
var body: some Scene {
WindowGroup {
ChatChannelListView(viewFactory: CustomFactory.shared)
}
}
```
129 changes: 93 additions & 36 deletions docusaurus/docs/iOS/swiftui/components/message-reactions.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,17 @@ The reactions overlay view (shown on long press of a message), also provides acc
public func supportedMessageActions(
for message: ChatMessage,
channel: ChatChannel,
onDismiss: @escaping () -> Void,
onFinish: @escaping (MessageActionInfo) -> Void,
onError: @escaping (Error) -> Void
) -> [MessageAction] {
MessageAction.defaultActions(
factory: self,
for: message,
channel: channel,
chatClient: chatClient,
onDismiss: onDismiss,
onError: onError
)
factory: self,
for: message,
channel: channel,
chatClient: chatClient,
onFinish: onFinish,
onError: onError
)
}

extension MessageAction {
Expand All @@ -61,23 +61,24 @@ extension MessageAction {
for message: ChatMessage,
channel: ChatChannel,
chatClient: ChatClient,
onDismiss: @escaping () -> Void,
onFinish: @escaping (MessageActionInfo) -> Void,
onError: @escaping (Error) -> Void
) -> [MessageAction] {
var messageActions = [MessageAction]()

let replyAction = replyAction(
for: message,
channel: channel,
onFinish: onFinish
)
messageActions.append(replyAction)

if !message.isPartOfThread {
var replyThread = MessageAction(
title: L10n.Message.Actions.threadReply,
iconName: "icn_thread_reply",
action: onDismiss,
confirmationPopup: nil,
isDestructive: false
let replyThread = threadReplyAction(
factory: factory,
for: message,
channel: channel
)

let destination = factory.makeMessageThreadDestination()
replyThread.navigationDestination = AnyView(destination(channel, message))

messageActions.append(replyThread)
}

Expand All @@ -86,7 +87,7 @@ extension MessageAction {
for: message,
channel: channel,
chatClient: chatClient,
onDismiss: onDismiss,
onFinish: onFinish,
onError: onError
)

Expand All @@ -96,7 +97,7 @@ extension MessageAction {
for: message,
channel: channel,
chatClient: chatClient,
onDismiss: onDismiss,
onFinish: onFinish,
onError: onError
)

Expand All @@ -106,11 +107,54 @@ extension MessageAction {
return messageActions
}

// MARK: - private

private static func replyAction(
for message: ChatMessage,
channel: ChatChannel,
onFinish: @escaping (MessageActionInfo) -> Void
) -> MessageAction {
let replyAction = MessageAction(
title: L10n.Message.Actions.inlineReply,
iconName: "icn_inline_reply",
action: {
onFinish(
MessageActionInfo(
message: message,
identifier: "inlineReply"
)
)
},
confirmationPopup: nil,
isDestructive: false
)

return replyAction
}

private static func threadReplyAction<Factory: ViewFactory>(
factory: Factory,
for message: ChatMessage,
channel: ChatChannel
) -> MessageAction {
var replyThread = MessageAction(
title: L10n.Message.Actions.threadReply,
iconName: "icn_thread_reply",
action: {},
confirmationPopup: nil,
isDestructive: false
)

let destination = factory.makeMessageThreadDestination()
replyThread.navigationDestination = AnyView(destination(channel, message))
return replyThread
}

private static func deleteMessageAction(
for message: ChatMessage,
channel: ChatChannel,
chatClient: ChatClient,
onDismiss: @escaping () -> Void,
onFinish: @escaping (MessageActionInfo) -> Void,
onError: @escaping (Error) -> Void
) -> MessageAction {
let messageController = chatClient.messageController(
Expand All @@ -123,7 +167,12 @@ extension MessageAction {
if let error = error {
onError(error)
} else {
onDismiss()
onFinish(
MessageActionInfo(
message: message,
identifier: "delete"
)
)
}
}
}
Expand All @@ -149,7 +198,7 @@ extension MessageAction {
for message: ChatMessage,
channel: ChatChannel,
chatClient: ChatClient,
onDismiss: @escaping () -> Void,
onFinish: @escaping (MessageActionInfo) -> Void,
onError: @escaping (Error) -> Void
) -> MessageAction {
let messageController = chatClient.messageController(
Expand All @@ -162,7 +211,12 @@ extension MessageAction {
if let error = error {
onError(error)
} else {
onDismiss()
onFinish(
MessageActionInfo(
message: message,
identifier: "flag"
)
)
}
}
}
Expand All @@ -183,24 +237,25 @@ extension MessageAction {

return flagMessage
}
}
```

Alternatively, you can swap the whole `MessageActionsView` with your own implementation, by implementing the `makeMessageActionsView` method in the `ViewFactory`.

```swift
public func makeMessageActionsView(
for message: ChatMessage,
channel: ChatChannel,
onDismiss: @escaping () -> Void,
onError: @escaping (Error) -> Void
for message: ChatMessage,
channel: ChatChannel,
onFinish: @escaping (MessageActionInfo) -> Void,
onError: @escaping (Error) -> Void
) -> some View {
let messageActions = supportedMessageActions(
for: message,
channel: channel,
onDismiss: onDismiss,
onFinish: onFinish,
onError: onError
)

return MessageActionsView(messageActions: messageActions)
}
```
Expand All @@ -209,17 +264,19 @@ Additionally, you can swap the whole `ReactionsOverlayView` with your own implem

```swift
public func makeReactionsOverlayView(
channel: ChatChannel,
currentSnapshot: UIImage,
messageDisplayInfo: MessageDisplayInfo,
onBackgroundTap: @escaping () -> Void
channel: ChatChannel,
currentSnapshot: UIImage,
messageDisplayInfo: MessageDisplayInfo,
onBackgroundTap: @escaping () -> Void,
onActionExecuted: @escaping (MessageActionInfo) -> Void
) -> some View {
ReactionsOverlayView(
factory: self,
channel: channel,
currentSnapshot: currentSnapshot,
messageDisplayInfo: messageDisplayInfo,
onBackgroundTap: onBackgroundTap
onBackgroundTap: onBackgroundTap,
onActionExecuted: onActionExecuted
)
}
```
3 changes: 2 additions & 1 deletion docusaurus/sidebars-ios.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@
"swiftui/components/attachments",
"swiftui/components/message-composer",
"swiftui/components/message-reactions",
"swiftui/components/message-threads"
"swiftui/components/message-threads",
"swiftui/components/inline-replies"
]
}
]
Expand Down

0 comments on commit 74e0873

Please sign in to comment.