Skip to content

Commit c126fa6

Browse files
Add support for starting thread from oldest replies (#2682)
* Add support for starting from oldest thread replies * Move `ChatMessageController` Actions to inside class so that it can be mockable * Add test coverage * Improve Channel List preloading speed * Rename `scrollToMostRecentMessage -> scrollToBottom` for consistency * Rename ScrollToLatestMessageButton -> ScrollToBottomButton * Update CHANGELOG.md --------- Co-authored-by: Alexey Alter-Pesotskiy <[email protected]>
1 parent 79ecf3e commit c126fa6

File tree

18 files changed

+265
-107
lines changed

18 files changed

+265
-107
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
1313
- Fix not being able to resend failed attachments [#2680](https://github.com/GetStream/stream-chat-swift/pull/2680)
1414

1515
## StreamChatUI
16+
### ✅ Added
17+
- Add support for starting thread from oldest replies by enabling `Components.threadRepliesStartFromOldest` [#2682](https://github.com/GetStream/stream-chat-swift/pull/2682)
1618
### 🐞 Fixed
1719
- Fix custom `ImageLoading` functions with default arguments not being called [#2695](https://github.com/GetStream/stream-chat-swift/pull/2695)
20+
- Improve Channel List prefetching when loading more channels [#2682](https://github.com/GetStream/stream-chat-swift/pull/2682)
21+
### 🔄 Changed
22+
- Renamed `scrollToMostRecentMessage()` -> `scrollToBottom()` [#2682](https://github.com/GetStream/stream-chat-swift/pull/2682)
23+
- Renamed `ScrollToLatestButton` -> `ScrollToBottomButton` [#2682](https://github.com/GetStream/stream-chat-swift/pull/2682)
1824

1925
# [4.33.0](https://github.com/GetStream/stream-chat-swift/releases/tag/4.33.0)
2026
_June 08, 2023_

DemoApp/Screens/AppConfigViewController/AppConfigViewController.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ class AppConfigViewController: UITableViewController {
128128
enum ComponentsConfigOption: String, CaseIterable {
129129
case isUniqueReactionsEnabled
130130
case shouldMessagesStartAtTheTop
131+
case threadRepliesStartFromOldest
131132
case threadRendersParentMessageEnabled
132133
case isVoiceRecordingEnabled
133134
case isVoiceRecordingConfirmationRequiredEnabled
@@ -355,6 +356,10 @@ class AppConfigViewController: UITableViewController {
355356
cell.accessoryView = makeSwitchButton(Components.default.shouldMessagesStartAtTheTop) { newValue in
356357
Components.default.shouldMessagesStartAtTheTop = newValue
357358
}
359+
case .threadRepliesStartFromOldest:
360+
cell.accessoryView = makeSwitchButton(Components.default.threadRepliesStartFromOldest) { newValue in
361+
Components.default.threadRepliesStartFromOldest = newValue
362+
}
358363
case .threadRendersParentMessageEnabled:
359364
cell.accessoryView = makeSwitchButton(Components.default.threadRendersParentMessageEnabled) { newValue in
360365
Components.default.threadRendersParentMessageEnabled = newValue

Sources/StreamChat/Controllers/ChannelController/ChannelController.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ public class ChatChannelController: DataController, DelegateCallable, DataStoreP
218218
/// `true` if the channel supports uploading files/images. Defaults to `false` if the channel doesn't exist yet.
219219
public var areUploadsEnabled: Bool { channel?.config.uploadsEnabled == true }
220220

221-
// MARK: - Channel actions
221+
// MARK: - Actions
222222

223223
/// Updated channel with new data.
224224
///

Sources/StreamChat/Controllers/MessageController/MessageController.swift

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -236,11 +236,9 @@ public class ChatMessageController: DataController, DelegateCallable, DataStoreP
236236
state = .localDataFetchFailed(ClientError(with: error))
237237
}
238238
}
239-
}
240239

241-
// MARK: - Actions
240+
// MARK: - Actions
242241

243-
public extension ChatMessageController {
244242
/// Edits the message this controller manages with the provided values.
245243
///
246244
/// - Parameters:
@@ -250,7 +248,7 @@ public extension ChatMessageController {
250248
/// - completion: The completion. Will be called on a **callbackQueue** when the network request is finished.
251249
/// If request fails, the completion will be called with an error.
252250
///
253-
func editMessage(
251+
public func editMessage(
254252
text: String,
255253
attachments: [AnyAttachmentPayload] = [],
256254
extraData: [String: RawJSON]? = nil,
@@ -277,7 +275,7 @@ public extension ChatMessageController {
277275
/// - completion: The completion. Will be called on a **callbackQueue** when the network request is finished.
278276
/// If request fails, the completion will be called with an error.
279277
///
280-
func deleteMessage(hard: Bool = false, completion: ((Error?) -> Void)? = nil) {
278+
public func deleteMessage(hard: Bool = false, completion: ((Error?) -> Void)? = nil) {
281279
messageUpdater.deleteMessage(messageId: messageId, hard: hard) { error in
282280
self.callback {
283281
completion?(error)
@@ -302,7 +300,7 @@ public extension ChatMessageController {
302300
/// - extraData: Additional extra data of the message object.
303301
/// - completion: Called when saving the message to the local DB finishes.
304302
///
305-
func createNewReply(
303+
public func createNewReply(
306304
messageId: MessageId? = nil,
307305
text: String,
308306
pinning: MessagePinning? = nil,
@@ -353,7 +351,7 @@ public extension ChatMessageController {
353351
/// - completion: The completion. Will be called on a **callbackQueue** when the network request is finished.
354352
/// If request fails, the completion will be called with an error.
355353
///
356-
func loadPreviousReplies(
354+
public func loadPreviousReplies(
357355
before replyId: MessageId? = nil,
358356
limit: Int? = nil,
359357
completion: ((Error?) -> Void)? = nil
@@ -413,7 +411,7 @@ public extension ChatMessageController {
413411
/// - replyId: The reply id of the message to jump to.
414412
/// - limit: The number of replies to load in total, including the message to jump to.
415413
/// - completion: Callback when the API call is completed.
416-
func loadPageAroundReplyId(
414+
public func loadPageAroundReplyId(
417415
_ replyId: MessageId,
418416
limit: Int? = nil,
419417
completion: ((Error?) -> Void)? = nil
@@ -448,7 +446,7 @@ public extension ChatMessageController {
448446
/// - completion: The completion. Will be called on a **callbackQueue** when the network request is finished.
449447
/// If request fails, the completion will be called with an error.
450448
///
451-
func loadNextReplies(
449+
public func loadNextReplies(
452450
after replyId: MessageId? = nil,
453451
limit: Int? = nil,
454452
completion: ((Error?) -> Void)? = nil
@@ -483,7 +481,7 @@ public extension ChatMessageController {
483481
/// Cleans the current state and loads the first page again.
484482
/// - Parameter limit: Limit for page size
485483
/// - Parameter completion: Callback when the API call is completed.
486-
func loadFirstPage(limit: Int? = nil, _ completion: ((_ error: Error?) -> Void)? = nil) {
484+
public func loadFirstPage(limit: Int? = nil, _ completion: ((_ error: Error?) -> Void)? = nil) {
487485
let pageSize = limit ?? repliesPageSize
488486
messageUpdater.loadReplies(
489487
cid: cid,
@@ -501,7 +499,7 @@ public extension ChatMessageController {
501499
/// - completion: The completion is called when the network request is finished.
502500
/// If the request fails, the completion will be called with an error, if it succeeds it is
503501
/// called without an error and the delegate is notified of reactions changes.
504-
func loadNextReactions(
502+
public func loadNextReactions(
505503
limit: Int = 25,
506504
completion: ((Error?) -> Void)? = nil
507505
) {
@@ -548,7 +546,7 @@ public extension ChatMessageController {
548546
/// - offset: The starting position from the desired range to be fetched.
549547
/// - completion: The completion is called when the network request is finished.
550548
/// It is called with the reactions if the request succeeds or error if the request fails.
551-
func loadReactions(
549+
public func loadReactions(
552550
limit: Int,
553551
offset: Int = 0,
554552
completion: @escaping (Result<[ChatMessageReaction], Error>) -> Void
@@ -571,7 +569,7 @@ public extension ChatMessageController {
571569
///
572570
/// - Parameter completion: The completion. Will be called on a **callbackQueue** when the network request is finished.
573571
///
574-
func flag(completion: ((Error?) -> Void)? = nil) {
572+
public func flag(completion: ((Error?) -> Void)? = nil) {
575573
messageUpdater.flagMessage(true, with: messageId, in: cid) { error in
576574
self.callback {
577575
completion?(error)
@@ -583,7 +581,7 @@ public extension ChatMessageController {
583581
///
584582
/// - Parameter completion: The completion. Will be called on a **callbackQueue** when the network request is finished.
585583
///
586-
func unflag(completion: ((Error?) -> Void)? = nil) {
584+
public func unflag(completion: ((Error?) -> Void)? = nil) {
587585
messageUpdater.flagMessage(false, with: messageId, in: cid) { error in
588586
self.callback {
589587
completion?(error)
@@ -598,7 +596,7 @@ public extension ChatMessageController {
598596
/// - enforceUnique: If set to `true`, new reaction will replace all reactions the user has (if any) on this message.
599597
/// - extraData: The reaction extra data.
600598
/// - completion: The completion. Will be called on a **callbackQueue** when the network request is finished.
601-
func addReaction(
599+
public func addReaction(
602600
_ type: MessageReactionType,
603601
score: Int = 1,
604602
enforceUnique: Bool = false,
@@ -622,7 +620,7 @@ public extension ChatMessageController {
622620
/// - Parameters:
623621
/// - type: The reaction type.
624622
/// - completion: The completion. Will be called on a **callbackQueue** when the network request is finished.
625-
func deleteReaction(
623+
public func deleteReaction(
626624
_ type: MessageReactionType,
627625
completion: ((Error?) -> Void)? = nil
628626
) {
@@ -637,7 +635,7 @@ public extension ChatMessageController {
637635
/// - Parameters:
638636
/// - pinning: The pinning expiration information. It supports setting an infinite expiration, setting a date, or the amount of time a message is pinned.
639637
/// - completion: A completion block with an error if the request was failed.
640-
func pin(_ pinning: MessagePinning, completion: ((Error?) -> Void)? = nil) {
638+
public func pin(_ pinning: MessagePinning, completion: ((Error?) -> Void)? = nil) {
641639
messageUpdater.pinMessage(messageId: messageId, pinning: pinning) { result in
642640
self.callback {
643641
completion?(result)
@@ -648,7 +646,7 @@ public extension ChatMessageController {
648646
/// Unpins the message this controller manages.
649647
/// - Parameters:
650648
/// - completion: A completion block with an error if the request was failed.
651-
func unpin(completion: ((Error?) -> Void)? = nil) {
649+
public func unpin(completion: ((Error?) -> Void)? = nil) {
652650
messageUpdater.unpinMessage(messageId: messageId) { result in
653651
self.callback {
654652
completion?(result)
@@ -661,7 +659,7 @@ public extension ChatMessageController {
661659
/// - id: The attachment identifier.
662660
/// - completion: The completion. Will be called on a **callbackQueue** when the database operation is finished.
663661
/// If operation fails, the completion will be called with an error.
664-
func restartFailedAttachmentUploading(
662+
public func restartFailedAttachmentUploading(
665663
with id: AttachmentId,
666664
completion: ((Error?) -> Void)? = nil
667665
) {
@@ -675,7 +673,7 @@ public extension ChatMessageController {
675673
/// Changes local message from `.sendingFailed` to `.pendingSend` so it is enqueued by message sender worker.
676674
/// - Parameter completion: The completion. Will be called on a **callbackQueue** when the database operation is finished.
677675
/// If operation fails, the completion will be called with an error.
678-
func resendMessage(completion: ((Error?) -> Void)? = nil) {
676+
public func resendMessage(completion: ((Error?) -> Void)? = nil) {
679677
messageUpdater.resendMessage(with: messageId) { error in
680678
self.callback {
681679
completion?(error)
@@ -688,7 +686,7 @@ public extension ChatMessageController {
688686
/// - action: The action to take.
689687
/// - completion: The completion. Will be called on a **callbackQueue** when the operation is finished.
690688
/// If operation fails, the completion is called with the error.
691-
func dispatchEphemeralMessageAction(_ action: AttachmentAction, completion: ((Error?) -> Void)? = nil) {
689+
public func dispatchEphemeralMessageAction(_ action: AttachmentAction, completion: ((Error?) -> Void)? = nil) {
692690
messageUpdater.dispatchEphemeralMessageAction(cid: cid, messageId: messageId, action: action) { error in
693691
self.callback {
694692
completion?(error)
@@ -703,7 +701,7 @@ public extension ChatMessageController {
703701
/// - language: The language message text should be translated to.
704702
/// - completion: The completion. Will be called on a **callbackQueue** when the operation is finished.
705703
/// If operation fails, the completion is called with the error.
706-
func translate(to language: TranslationLanguage, completion: ((Error?) -> Void)? = nil) {
704+
public func translate(to language: TranslationLanguage, completion: ((Error?) -> Void)? = nil) {
707705
messageUpdater.translate(messageId: messageId, to: language) { error in
708706
self.callback {
709707
completion?(error)

Sources/StreamChatUI/ChatChannel/ChatChannelVC.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ open class ChatChannelVC: _ViewController,
233233
/// Marks the channel read and updates the UI optimistically.
234234
public func markRead() {
235235
channelController.markRead()
236-
messageListVC.scrollToLatestMessageButton.content = .noUnread
236+
messageListVC.scrollToBottomButton.content = .noUnread
237237
}
238238

239239
/// Jump to a given message.
@@ -415,7 +415,7 @@ open class ChatChannelVC: _ViewController,
415415
didUpdateChannel channel: EntityChange<ChatChannel>
416416
) {
417417
let channelUnreadCount = channelController.channel?.unreadCount ?? .noUnread
418-
messageListVC.scrollToLatestMessageButton.content = channelUnreadCount
418+
messageListVC.scrollToBottomButton.content = channelUnreadCount
419419

420420
guard channelController.firstUnreadMessageId != firstUnreadMessageId else { return }
421421
let previousUnreadMessageId = firstUnreadMessageId

Sources/StreamChatUI/ChatChannelList/ChatChannelListVC.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ open class ChatChannelListVC: _ViewController,
151151
self?.channelListErrorView.hide()
152152
}
153153

154+
viewPaginationHandler.bottomThreshold = 800
154155
viewPaginationHandler.onNewBottomPage = { [weak self] in
155156
self?.loadMoreChannels()
156157
}

0 commit comments

Comments
 (0)