Skip to content

Commit 6cbfd3e

Browse files
committed
Add thread selection logic to iPad
1 parent 428e917 commit 6cbfd3e

File tree

6 files changed

+85
-15
lines changed

6 files changed

+85
-15
lines changed

Sources/StreamChatSwiftUI/ChatThreadList/ChatThreadList.swift

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ public struct ThreadList<Factory: ViewFactory, HeaderView: View, FooterView: Vie
1111
var threads: LazyCachedMapCollection<ChatThread>
1212
private var factory: Factory
1313
private var threadDestination: (ChatThread) -> Factory.ThreadDestination
14+
@Binding private var selectedThread: ThreadSelectionInfo?
15+
16+
private var onItemTap: (ChatThread) -> Void
1417
private var onItemAppear: (Int) -> Void
1518

1619
@ViewBuilder
@@ -23,13 +26,17 @@ public struct ThreadList<Factory: ViewFactory, HeaderView: View, FooterView: Vie
2326
factory: Factory,
2427
threads: LazyCachedMapCollection<ChatThread>,
2528
threadDestination: @escaping (ChatThread) -> Factory.ThreadDestination,
29+
selectedThread: Binding<ThreadSelectionInfo?>,
30+
onItemTap: @escaping (ChatThread) -> Void,
2631
onItemAppear: @escaping (Int) -> Void,
2732
headerView: @escaping () -> HeaderView,
2833
footerView: @escaping () -> FooterView
2934
) {
3035
self.factory = factory
3136
self.threads = threads
3237
self.threadDestination = threadDestination
38+
self._selectedThread = selectedThread
39+
self.onItemTap = onItemTap
3340
self.onItemAppear = onItemAppear
3441
self.headerView = headerView
3542
self.footerView = footerView
@@ -42,6 +49,8 @@ public struct ThreadList<Factory: ViewFactory, HeaderView: View, FooterView: Vie
4249
factory: factory,
4350
threads: threads,
4451
threadDestination: threadDestination,
52+
selectedThread: $selectedThread,
53+
onItemTap: onItemTap,
4554
onItemAppear: onItemAppear
4655
)
4756
footerView()
@@ -54,27 +63,38 @@ public struct ThreadsLazyVStack<Factory: ViewFactory>: View {
5463
private var factory: Factory
5564
var threads: LazyCachedMapCollection<ChatThread>
5665
private var threadDestination: (ChatThread) -> Factory.ThreadDestination
66+
@Binding private var selectedThread: ThreadSelectionInfo?
67+
private var onItemTap: (ChatThread) -> Void
5768
private var onItemAppear: (Int) -> Void
5869

5970
public init(
6071
factory: Factory,
6172
threads: LazyCachedMapCollection<ChatThread>,
6273
threadDestination: @escaping (ChatThread) -> Factory.ThreadDestination,
74+
selectedThread: Binding<ThreadSelectionInfo?>,
75+
onItemTap: @escaping (ChatThread) -> Void,
6376
onItemAppear: @escaping (Int) -> Void
6477
) {
6578
self.factory = factory
6679
self.threads = threads
6780
self.threadDestination = threadDestination
81+
self.onItemTap = onItemTap
6882
self.onItemAppear = onItemAppear
83+
self._selectedThread = selectedThread
6984
}
7085

7186
public var body: some View {
7287
LazyVStack(spacing: 0) {
7388
ForEach(threads) { thread in
7489
factory.makeThreadListItem(
7590
thread: thread,
76-
threadDestination: threadDestination
91+
threadDestination: threadDestination,
92+
selectedThread: $selectedThread
7793
)
94+
.contentShape(Rectangle())
95+
.onTapGesture {
96+
onItemTap(thread)
97+
}
7898
.onAppear {
7999
if let index = threads.firstIndex(where: { chatThread in
80100
chatThread.id == thread.id

Sources/StreamChatSwiftUI/ChatThreadList/ChatThreadListNavigatableItem.swift

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,31 +12,60 @@ public struct ChatThreadListNavigatableItem<ThreadListItem: View, ThreadDestinat
1212
private var threadListItem: ThreadListItem
1313
private var threadDestination: (ChatThread) -> ThreadDestination
1414
private var handleTabBarVisibility: Bool
15+
@Binding private var selectedThread: ThreadSelectionInfo?
1516

1617
public init(
1718
thread: ChatThread,
1819
threadListItem: ThreadListItem,
1920
threadDestination: @escaping (ChatThread) -> ThreadDestination,
21+
selectedThread: Binding<ThreadSelectionInfo?>,
2022
handleTabBarVisibility: Bool
2123
) {
2224
self.thread = thread
2325
self.threadListItem = threadListItem
2426
self.threadDestination = threadDestination
27+
self._selectedThread = selectedThread
2528
self.handleTabBarVisibility = handleTabBarVisibility
2629
}
2730

2831
public var body: some View {
29-
NavigationLink(
30-
destination: {
31-
threadDestination(thread)
32-
.modifier(HideTabBarModifier(
33-
handleTabBarVisibility: handleTabBarVisibility
34-
))
35-
},
36-
label: {
37-
threadListItem
32+
ZStack {
33+
threadListItem
34+
NavigationLink(
35+
tag: ThreadSelectionInfo(thread: thread),
36+
selection: $selectedThread
37+
) {
38+
LazyView(
39+
threadDestination(thread)
40+
.modifier(HideTabBarModifier(
41+
handleTabBarVisibility: handleTabBarVisibility
42+
))
43+
)
44+
} label: {
45+
EmptyView()
3846
}
39-
)
47+
}
4048
.foregroundColor(.black)
4149
}
4250
}
51+
52+
public struct ThreadSelectionInfo: Identifiable {
53+
public let id: String
54+
public let thread: ChatThread
55+
56+
public init(thread: ChatThread) {
57+
self.thread = thread
58+
self.id = thread.id
59+
}
60+
}
61+
62+
extension ThreadSelectionInfo: Hashable, Equatable {
63+
64+
public static func == (lhs: ThreadSelectionInfo, rhs: ThreadSelectionInfo) -> Bool {
65+
lhs.id == rhs.id
66+
}
67+
68+
public func hash(into hasher: inout Hasher) {
69+
hasher.combine(id)
70+
}
71+
}

Sources/StreamChatSwiftUI/ChatThreadList/ChatThreadListView.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,11 @@ public struct ChatThreadListContentView<Factory: ViewFactory>: View {
111111
ThreadList(
112112
factory: viewFactory,
113113
threads: viewModel.threads,
114-
threadDestination: viewFactory.makeThreadDestination(),
114+
threadDestination: viewFactory.makeThreadDestination(),
115+
selectedThread: $viewModel.selectedThread,
116+
onItemTap: { thread in
117+
viewModel.selectedThread = .init(thread: thread)
118+
},
115119
onItemAppear: { index in
116120
viewModel.didAppearThread(at: index)
117121
},

Sources/StreamChatSwiftUI/ChatThreadList/ChatThreadListViewModel.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ open class ChatThreadListViewModel: ObservableObject, ChatThreadListControllerDe
2323
/// A boolean value indicating if the initial threads have been loaded.
2424
public private(set) var hasLoadedThreads = false
2525

26+
/// The current selected thread.
27+
@Published public var selectedThread: ThreadSelectionInfo?
28+
2629
/// The list of threads.
2730
@Published public var threads = LazyCachedMapCollection<ChatThread>()
2831

@@ -108,12 +111,14 @@ open class ChatThreadListViewModel: ObservableObject, ChatThreadListControllerDe
108111
isLoading = threadListController.threads.isEmpty == true
109112
failedToLoadThreads = false
110113
isReloading = !isEmpty
114+
preselectThreadIfNeeded()
111115
threadListController.synchronize { [weak self] error in
112116
self?.isLoading = false
113117
self?.isReloading = false
114118
self?.hasLoadedThreads = error == nil
115119
self?.failedToLoadThreads = error != nil
116120
self?.isEmpty = self?.threadListController.threads.isEmpty == true
121+
self?.preselectThreadIfNeeded()
117122
self?.hasLoadedAllThreads = self?.threadListController.hasLoadedAllThreads ?? false
118123
if error == nil {
119124
self?.newAvailableThreadIds = []
@@ -172,4 +177,12 @@ open class ChatThreadListViewModel: ObservableObject, ChatThreadListControllerDe
172177
private func makeDefaultEventsController() {
173178
eventsController = chatClient.eventsController()
174179
}
180+
181+
private func preselectThreadIfNeeded() {
182+
guard isIPad else { return }
183+
guard let firstThread = threads.first else { return }
184+
guard selectedThread == nil else { return }
185+
186+
selectedThread = .init(thread: firstThread)
187+
}
175188
}

Sources/StreamChatSwiftUI/DefaultViewFactory.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -981,12 +981,14 @@ extension ViewFactory {
981981

982982
public func makeThreadListItem(
983983
thread: ChatThread,
984-
threadDestination: @escaping (ChatThread) -> ThreadDestination
984+
threadDestination: @escaping (ChatThread) -> ThreadDestination,
985+
selectedThread: Binding<ThreadSelectionInfo?>
985986
) -> some View {
986987
ChatThreadListNavigatableItem(
987988
thread: thread,
988989
threadListItem: ChatThreadListItem(thread: thread),
989-
threadDestination: threadDestination,
990+
threadDestination: threadDestination,
991+
selectedThread: selectedThread,
990992
handleTabBarVisibility: true
991993
)
992994
}

Sources/StreamChatSwiftUI/ViewFactory.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -997,9 +997,11 @@ public protocol ViewFactory: AnyObject {
997997
/// - Parameters:
998998
/// - thread: The thread being displayed.
999999
/// - threadDestination: A closure that creates the thread destination.
1000+
/// - selectedThread: The binding of the currently selected thread.
10001001
func makeThreadListItem(
10011002
thread: ChatThread,
1002-
threadDestination: @escaping (ChatThread) -> ThreadDestination
1003+
threadDestination: @escaping (ChatThread) -> ThreadDestination,
1004+
selectedThread: Binding<ThreadSelectionInfo?>
10031005
) -> ThreadListItemType
10041006

10051007
associatedtype NoThreads: View

0 commit comments

Comments
 (0)