Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
stream-ci-bot committed Jul 9, 2024
1 parent 1438e72 commit 2578565
Show file tree
Hide file tree
Showing 12 changed files with 321 additions and 3 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
316 changes: 316 additions & 0 deletions docusaurus/docs/iOS/uikit/components/thread-list.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,316 @@
---
title: Thread List
---

The `ChatThreadListVC` is the UI component that displays the list of threads that the current user is participating in.

:::note
The Thread List component is available on the UIKit SDK since version [4.59.0](https://github.com/GetStream/stream-chat-swift/releases/tag/4.59.0).
:::

## Basic Usage

You can show this component in your app by creating a `ChatThreadListVC` instance:

```swift
// Default thread list query with watch enabled.
// Which means the list will be updated in real-time from thread events.
let threadListQuery = ThreadListQuery(watch: true)
let threadListController = ChatClient.shared.threadListController(query: threadListQuery)
let eventsController = ChatClient.shared.eventsController()
let threadListVC = ChatThreadListVC(
threadListController: threadListController,
eventsController: eventsController
)

// Present the thread list VC
let navigationThreadListVC = UINavigationController(rootViewController: threadListVC)
navigationController?.pushViewController(navigationThreadListVC, animated: true)
```

You can present the Thread List in a `UINavigationController` or in a `UITabBarController` depending on your app design. The `ChatThreadListVC` has two required dependencies: a `ThreadListController` and an `EventsController`.

## Thread List Query

The `ThreadListQuery` is responsible to configure the list of threads that will be displayed in the `ChatThreadListController`. These are the available parameters:

- `watch`: A boolean indicating whether to watch for changes in the thread list or not.
- `limit`: The amount of threads fetched per page. The default is 20.
- `replyLimit`: The amount of replies fetched per thread. The default is 3.
- `participantLimit`: The amount of participants fetched per thread. The default is 10.
- `next`: The pagination token from the previous response to fetch the next page.

All the parameters are customizable and you can change them according to your needs. The default values are a good compromise between performance and user experience.

If you are using the `ChatThreadListVC` component, you don't need to worry about the `next` parameter, since pagination is handled for you. If not, you can use the `next` parameter from the previous response to fetch the next page of threads.

## UI Customization

You can customize the Thread List UI by overriding the `ChatThreadListVC` component and the `ChatThreadListItemView`. In the `ChatThreadListVC` you can change, for example, the header and state views, where in the `ChatThreadListItemView` you can change how each thread is displayed.

### Thread List Navigation Header

The navigation header of the thread list can be configured the same way you would configure it on a `UIViewController` object if it is part of a `UINavigationController`. You can change the `title` and the `navigationItem` properties. Here is an example:

```swift
class CustomThreadListVC: ChatThreadListVC {

override open func setUpAppearance() {
super.setUpAppearance()
title = "Threads"
}
}
```

### Thread List Header Banner View

The `ChatThreadListHeaderBannerView` is the view that is displayed by default as a `tableHeaderView` in the `ChatThreadListVC`. This view is shown whenever the user is on the Thread List and new threads are available to fetch. New threads are not added automatically to the Thread List on purpose, allowing users to manually decide when to fetch them after reading all current unread threads.

You can customize this view by overriding the `Components.default.threadListHeaderBannerView` property in the `ChatThreadListVC`.

```swift
class CustomThreadListHeaderBannerView: ChatThreadListHeaderBannerView {
override func setUpAppearance() {
super.setUpAppearance()

bannerView.backgroundColor = .systemGray4
bannerView.textLabel.textColor = .systemBlue
refreshButton.tintColor = .systemBlue
}
}

Components.default.threadListHeaderBannerView = CustomThreadListHeaderBannerView.self
```

**Result:**

| Before| After |
| ------------- | ------------- |
| ![Before](../../assets/thread-list/ThreadListHeaderBannerView.png) | ![After](../../assets/thread-list/ThreadListHeaderBannerView_CustomBasic.png) |

In case you want to customize how the banner is shown and how the user interacts with it, you can override the `ChatThreadListVC` and, for example, change the banner view to a floating button on the top of the table view. Here is an example of how you can do it:

```swift
class CustomThreadListHeaderBannerView: ChatThreadListHeaderBannerView {
override func setUp() {
super.setUp()

let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(didTapBannerView))
bannerView.addGestureRecognizer(tapGestureRecognizer)
}

override func setUpAppearance() {
super.setUpAppearance()

bannerView.backgroundColor = .systemBlue
bannerView.textLabel.font = .systemFont(ofSize: 11, weight: .bold)
bannerView.actionButton.setImage(UIImage(systemName: "arrow.triangle.2.circlepath"), for: .normal)
bannerView.spacer.isHidden = true
bannerView.container.spacing = 6
}

override open func layoutSubviews() {
super.layoutSubviews()

bannerView.layer.cornerRadius = bounds.height / 2
}

@objc func didTapBannerView() {
onAction?()
}
}

Components.default.threadListHeaderBannerView = CustomThreadListHeaderBannerView.self
```

```swift
class CustomChatThreadListVC: ChatThreadListVC {

override func setUpLayout() {
super.setUpLayout()

view.addSubview(headerBannerView)
NSLayoutConstraint.activate([
headerBannerView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 8),
headerBannerView.centerXAnchor.constraint(equalTo: tableView.centerXAnchor),
headerBannerView.heightAnchor.constraint(equalToConstant: 30)
])
}

override func showHeaderBannerView() {
headerBannerView.isHidden = false
}

override func hideHeaderBannerView() {
headerBannerView.isHidden = true
}
}
```

When showing the Thread List in your app, you will then need to use the `CustomChatThreadListVC` instead of the default `ChatThreadListVC`.

**Result:**

| Before| After |
| ------------- | ------------- |
| ![Before](../../assets/thread-list/ThreadListHeaderBannerView.png) | ![After](../../assets/thread-list/ThreadListHeaderBannerView_CustomAdvanced.png) |


### Thread List States

The `ChatThreadListVC` comes with three states: **loading**, **empty**, and **error**. The state logic is handled automatically by this component. You can customize the appearance of these states by overriding the `ChatThreadListVC` and/or overriding the `Components.default.threadListLoadingView`, `Components.default.threadListEmptyView`, and `Components.default.threadListErrorView`.

| Empty | Error | Loading |
| ------------- | ------------- | ------------- |
| ![Empty View](../../assets/thread-list/ChatThreadListEmptyView.png) | ![Error View](../../assets/thread-list/ChatThreadListErrorView.png) | ![Loading View](../../assets/thread-list/ChatThreadListLoadingView.png) |

The example below shows how you can customize the appearance of the error view:

```swift
class CustomThreadListErrorView: ChatThreadListErrorView {

override func setUpAppearance() {
super.setUpAppearance()

bannerView.backgroundColor = .red
}

override func layoutSubviews() {
super.layoutSubviews()

bannerView.layer.cornerRadius = 12
}
}

class CustomChatThreadListVC: ChatThreadListVC {
override func setUpLayout() {
super.setUpLayout()

NSLayoutConstraint.activate([
errorView.bottomAnchor.constraint(equalTo: view.layoutMarginsGuide.bottomAnchor, constant: -16),
errorView.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor),
errorView.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor)
])
}
}

Components.default.threadListErrorView = CustomThreadListErrorView.self
```

**Result:**

| Before| After |
| ------------- | ------------- |
| ![Before](../../assets/thread-list/ChatThreadListErrorView.png) | ![After](../../assets/thread-list/ChatThreadListErrorView_Custom.png) |

### Thread List Item View

The `ChatThreadListItemView` is the view that is displayed for each thread in the `ChatThreadListVC`. You can customize this view by overriding the `Components.default.threadListItemView` property.

This view is composed by the following subviews:

- `mainContainer`: Holds the `threadContainer` and the `replyContainer`.
- `threadContainer`: By default displays the information related to the thread.
- `threadIconView`: Displays the thread icon.
- `threadTitleLabel`: Displays the channel name.
- `threadDescriptionLabel`: Displays the parent message text or thread title.
- `threadUnreadCountView`: Displays the unread replies count.
- `replyContainer`: By default displays the information related to the last reply.
- `replyAuthorAvatarView`: Displays the last reply author avatar.
- `replyTitleLabel`: Displays the last reply author name.
- `replyDescriptionLabel`: Displays the last reply text.
- `replyTimestampLabel`: Displays the last reply timestamp.

Let's see an example of how you can customize the `ChatThreadListItemView` to display the information in a different way:

```swift
class CustomChatThreadListItemView: ChatThreadListItemView {

lazy var channelNameLabel: UILabel = {
let label = UILabel()
label.font = .preferredFont(forTextStyle: .footnote)
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()

override func setUpLayout() {
super.setUpLayout()

// Changes the margins of the item view
directionalLayoutMargins = .init(
top: 8,
leading: 8,
bottom: 8,
trailing: 8
)

// Remove the bottom reply container
replyContainer.isHidden = true

// Moves the unread badge to the top right corner
threadTitleContainer.addArrangedSubview(UIView())
threadTitleContainer.addArrangedSubview(threadUnreadCountView)
threadUnreadCountView.heightAnchor.constraint(greaterThanOrEqualToConstant: 20).isActive = true

// Adds a new text label to display the channel name between the thread title and latest reply
threadContainer.insertArrangedSubview(channelNameLabel, at: 1)

// Moves the avatar view to the left of the latest reply
threadDescriptionContainer.insertArrangedSubview(replyAuthorAvatarView, at: 0)
threadDescriptionContainer.spacing = 6
// Overrides the avatar view size
NSLayoutConstraint.activate([
replyAuthorAvatarView.heightAnchor.constraint(equalToConstant: 20),
replyAuthorAvatarView.widthAnchor.constraint(equalToConstant: 20)
])

// Moves the timestamp label to the right of the latest reply
threadDescriptionContainer.addArrangedSubview(UIView())
threadDescriptionContainer.addArrangedSubview(replyTimestampLabel)
}

override func updateContent() {
super.updateContent()

// Displays the parent message in the thread title label
threadTitleLabel.text = parentMessagePreviewText
// Displays the latest reply text in the thread description label
threadDescriptionLabel.text = replyPreviewText
// Displays the channel name in the new channel name label
channelNameLabel.text = "# \(channelNameText ?? "")"
}
}

Components.default.threadListItemView = CustomChatThreadListItemView.self
```

**Result:**

| Before| After |
| ------------- | ------------- |
| ![Before](../../assets/thread-list/ChatThreadListItemView.png) | ![After](../../assets/thread-list/ChatThreadListItemView_Custom.png) |

## Navigation

This component uses the `ChatThreadListRouter` to handle navigation, you can customize the navigation behavior by overriding the `Components.default.threadListRouter`.

By default, the `ChatThreadListRouter` uses the `ChatThreadVC` to show the thread replies, it is the same component used in the `ChatChannelVC` when tapping the replies of a message.

```swift
class CustomThreadListRouter: ChatThreadListRouter {
override func showThread(_ thread: ChatThread) {
// Custom implementation
}
}

Components.default.threadListRouter = CustomThreadListRouter.self
```

## Thread Events

By default the UI components handle automatically the thread events, like new replies and thread updates. But, in case your app needs additional logic, these are the available thread events:

- `ThreadMessageNewEvent`: Triggered when a new reply is added to a thread.
- `ThreadUpdatedEvent`: Triggered when a thread is updated, like the thread title or custom data.

You can override the `ChatThreadListVC.eventsController(controller:didReceiveEvent:)` method to handle these events. For more information about how to handle events, check the [Events](../../client/controllers/events.md) documentation.
1 change: 1 addition & 0 deletions docusaurus/docs/iOS/uikit/navigation.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ The navigation of StreamChatUI SDK is handled by `NavigationRouter`'s. Currently

- `ChatChannelListRouter`: The Channel List Router is responsible to handle the navigation of the Channel List, like showing the Channel View.
- `ChatMessageListRouter`: The Message List Router is responsible to handle the navigation of the Message List, like showing the User Profile View, the Gallery View, the Message Actions Popup, etc.
- `ChatThreadListRouter`: The Thread List Router is responsible to handle the navigation of the Thread List, like showing the Thread View.
- `AlertsRouter`: The Alerts Router is responsible to show alerts in the Chat SDK, like confirmation dialogues.

### Presenting a custom user profile view
Expand Down
7 changes: 4 additions & 3 deletions docusaurus/sidebars-ios.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
{
"Basics": [
"basics/overview",
"basics/integration"
"basics/integration",
"basics/logs"
]
},
{
Expand Down Expand Up @@ -40,7 +41,8 @@
"uikit/guides/customize-message-composer",
"uikit/guides/message-composer-custom-attachments"
]
},
},
"uikit/components/thread-list",
"uikit/navigation",
"uikit/localization",
"uikit/data-formatting",
Expand Down Expand Up @@ -117,7 +119,6 @@
"Advanced Guides": [
"client/push-notifications",
"guides/video-integration",
"basics/logs",
"client/custom-cdn",
"guides/moderation",
"guides/go-live-checklist",
Expand Down

0 comments on commit 2578565

Please sign in to comment.