Skip to content

Commit 6df7a70

Browse files
authored
Add inline error message banners for polls (#504)
1 parent 0091ed9 commit 6df7a70

File tree

14 files changed

+190
-35
lines changed

14 files changed

+190
-35
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
1111
- Dismiss keyboard when tapping on the empty message list [#513](https://github.com/GetStream/stream-chat-swiftui/pull/513)
1212
- Reset composer text when there is provisional text (e.g. Japanese - kana keyboard) but the text is reset to empty string [#512](https://github.com/GetStream/stream-chat-swiftui/pull/512)
1313

14+
### 🔄 Changed
15+
- Show inline alert banner when encountering a failure while interacting with polls [#504](https://github.com/GetStream/stream-chat-swiftui/pull/504)
16+
1417
# [4.57.0](https://github.com/GetStream/stream-chat-swiftui/releases/tag/4.57.0)
1518
_June 07, 2024_
1619

Sources/StreamChatSwiftUI/ChatChannel/ChatChannelView.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ public struct ChatChannelView<Factory: ViewFactory>: View, KeyboardReadable {
193193
)
194194
.padding(.bottom, keyboardShown || !tabBarAvailable || generatingSnapshot ? 0 : bottomPadding)
195195
.ignoresSafeArea(.container, edges: tabBarAvailable ? .bottom : [])
196+
.alertBanner(isPresented: $viewModel.showAlertBanner)
196197
.accessibilityElement(children: .contain)
197198
.accessibilityIdentifier("ChatChannelView")
198199
}

Sources/StreamChatSwiftUI/ChatChannel/ChatChannelViewModel.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ open class ChatChannelViewModel: ObservableObject, MessagesDataSource {
5858
@Published public var listId = UUID().uuidString
5959

6060
@Published public var showScrollToLatestButton = false
61+
@Published var showAlertBanner = false
6162

6263
@Published public var currentDateString: String?
6364
@Published public var messages = LazyCachedMapCollection<ChatMessage>() {
@@ -191,6 +192,13 @@ open class ChatChannelViewModel: ObservableObject, MessagesDataSource {
191192
object: nil
192193
)
193194

195+
NotificationCenter.default.addObserver(
196+
self,
197+
selector: #selector(onShowChannelAlertBanner),
198+
name: .showChannelAlertBannerNotification,
199+
object: nil
200+
)
201+
194202
if messageController == nil {
195203
NotificationCenter.default.addObserver(
196204
self,
@@ -213,6 +221,11 @@ open class ChatChannelViewModel: ObservableObject, MessagesDataSource {
213221
}
214222
}
215223

224+
@objc
225+
private func onShowChannelAlertBanner() {
226+
showAlertBanner = true
227+
}
228+
216229
@objc
217230
private func didReceiveMemoryWarning() {
218231
ImageCache.shared.removeAll()
@@ -811,8 +824,12 @@ let firstMessageKey = "firstMessage"
811824
let lastMessageKey = "lastMessage"
812825

813826
extension Notification.Name {
827+
/// A notification for notifying when an error occured and an alert banner should be shown at the top of the message list.
828+
static let showChannelAlertBannerNotification = Notification.Name("showChannelAlertBannerNotification")
829+
814830
/// A notification for notifying when message dismissed a sheet.
815831
static let messageSheetHiddenNotification = Notification.Name("messageSheetHiddenNotification")
832+
816833
/// A notification for notifying when message view displays a sheet.
817834
///
818835
/// When a sheet is presented, the message cell is not reloaded.

Sources/StreamChatSwiftUI/ChatChannel/MessageList/Polls/PollAllOptionsView.swift

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,6 @@ struct PollAllOptionsView: View {
3939
}
4040
.padding()
4141
}
42-
.alert(isPresented: $viewModel.errorShown) {
43-
Alert.defaultErrorAlert
44-
}
4542
.toolbar {
4643
ToolbarItem(placement: .principal) {
4744
Text(L10n.Message.Polls.Toolbar.optionsTitle)

Sources/StreamChatSwiftUI/ChatChannel/MessageList/Polls/PollAttachmentView.swift

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,9 +145,6 @@ public struct PollAttachmentView<Factory: ViewFactory>: View {
145145
}
146146
}
147147
}
148-
.alert(isPresented: $viewModel.errorShown) {
149-
Alert.defaultErrorAlert
150-
}
151148
.disabled(!viewModel.canInteract)
152149
.padding()
153150
.modifier(

Sources/StreamChatSwiftUI/ChatChannel/MessageList/Polls/PollAttachmentViewModel.swift

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ public class PollAttachmentViewModel: ObservableObject, PollControllerDelegate {
1010

1111
static let numberOfVisibleOptionsShown = 10
1212
private var isCastingVote = false
13-
private var isClosingPoll = false
13+
@Published private var isClosingPoll = false
1414

1515
@Injected(\.chatClient) var chatClient
1616

@@ -62,6 +62,8 @@ public class PollAttachmentViewModel: ObservableObject, PollControllerDelegate {
6262

6363
/// If true, an action sheet is shown for closing the poll, otherwise hidden.
6464
@Published public var endVoteConfirmationShown = false
65+
66+
@available(*, deprecated, message: "Replaced with inline alert banners displayed by the showChannelAlertBannerNotification")
6567
@Published public var errorShown = false
6668

6769
/// If true, poll controls are in enabled state, otherwise disabled.
@@ -154,10 +156,10 @@ public class PollAttachmentViewModel: ObservableObject, PollControllerDelegate {
154156
pollController.castPollVote(
155157
answerText: comment,
156158
optionId: nil
157-
) { [weak self] error in
159+
) { error in
158160
if let error {
159161
log.error("Error casting a vote \(error.localizedDescription)")
160-
self?.errorShown = true
162+
NotificationCenter.default.post(name: .showChannelAlertBannerNotification, object: nil)
161163
}
162164
}
163165
commentText = ""
@@ -189,7 +191,7 @@ public class PollAttachmentViewModel: ObservableObject, PollControllerDelegate {
189191
self?.isClosingPoll = false
190192
if let error {
191193
log.error("Error closing the poll \(error.localizedDescription)")
192-
self?.errorShown = true
194+
NotificationCenter.default.post(name: .showChannelAlertBannerNotification, object: nil)
193195
}
194196
}
195197
}
@@ -205,10 +207,10 @@ public class PollAttachmentViewModel: ObservableObject, PollControllerDelegate {
205207
suggestOptionText = ""
206208
let isDuplicate = poll.options.contains(where: { $0.text.trimmed.caseInsensitiveCompare(option.trimmed) == .orderedSame })
207209
guard !isDuplicate else { return }
208-
pollController.suggestPollOption(text: option) { [weak self] error in
210+
pollController.suggestPollOption(text: option) { error in
209211
if let error {
210212
log.error("Error closing the poll \(error.localizedDescription)")
211-
self?.errorShown = true
213+
NotificationCenter.default.post(name: .showChannelAlertBannerNotification, object: nil)
212214
}
213215
}
214216
}

Sources/StreamChatSwiftUI/ChatChannel/MessageList/Polls/PollCommentsView.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,10 @@ struct PollCommentsView: View {
6565
}
6666
.padding()
6767
}
68-
.alert(isPresented: $viewModel.errorShown) {
69-
Alert.defaultErrorAlert
70-
}
68+
.alertBanner(
69+
isPresented: $viewModel.errorShown,
70+
action: viewModel.refresh
71+
)
7172
.toolbar {
7273
ToolbarItem(placement: .principal) {
7374
Text(L10n.Message.Polls.Toolbar.commentsTitle)

Sources/StreamChatSwiftUI/ChatChannel/MessageList/Polls/PollCommentsViewModel.swift

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,19 @@ class PollCommentsViewModel: ObservableObject, PollVoteListControllerDelegate {
4242
) {
4343
self.commentsController = commentsController
4444
self.pollController = pollController
45-
4645
commentsController.delegate = self
46+
refresh()
47+
48+
// No animation for initial load
49+
$comments
50+
.dropFirst()
51+
.map { _ in true }
52+
.assignWeakly(to: \.animateChanges, on: self)
53+
.store(in: &cancellables)
54+
}
55+
56+
func refresh() {
57+
loadingComments = true
4758
commentsController.synchronize { [weak self] error in
4859
guard let self else { return }
4960
self.loadingComments = false
@@ -52,12 +63,6 @@ class PollCommentsViewModel: ObservableObject, PollVoteListControllerDelegate {
5263
self.errorShown = true
5364
}
5465
}
55-
// No animation for initial load
56-
$comments
57-
.dropFirst()
58-
.map { _ in true }
59-
.assignWeakly(to: \.animateChanges, on: self)
60-
.store(in: &cancellables)
6166
}
6267

6368
var showsAddCommentButton: Bool {

Sources/StreamChatSwiftUI/ChatChannel/MessageList/Polls/PollOptionAllVotesView.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,10 @@ struct PollOptionAllVotesView: View {
2929
)
3030
}
3131
}
32-
.alert(isPresented: $viewModel.errorShown) {
33-
Alert.defaultErrorAlert
34-
}
32+
.alertBanner(
33+
isPresented: $viewModel.errorShown,
34+
action: viewModel.refresh
35+
)
3536
.toolbar {
3637
ToolbarItem(placement: .principal) {
3738
Text(viewModel.option.text)

Sources/StreamChatSwiftUI/ChatChannel/MessageList/Polls/PollOptionAllVotesViewModel.swift

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,17 @@ class PollOptionAllVotesViewModel: ObservableObject, PollVoteListControllerDeleg
2828
)
2929
controller = InjectedValues[\.chatClient].pollVoteListController(query: query)
3030
controller.delegate = self
31+
refresh()
32+
33+
// No animation for initial load
34+
$pollVotes
35+
.dropFirst()
36+
.map { _ in true }
37+
.assignWeakly(to: \.animateChanges, on: self)
38+
.store(in: &cancellables)
39+
}
40+
41+
func refresh() {
3142
controller.synchronize { [weak self] error in
3243
guard let self else { return }
3344
self.pollVotes = Array(self.controller.votes)
@@ -38,12 +49,6 @@ class PollOptionAllVotesViewModel: ObservableObject, PollVoteListControllerDeleg
3849
self.errorShown = true
3950
}
4051
}
41-
// No animation for initial load
42-
$pollVotes
43-
.dropFirst()
44-
.map { _ in true }
45-
.assignWeakly(to: \.animateChanges, on: self)
46-
.store(in: &cancellables)
4752
}
4853

4954
func onAppear(vote: PollVote) {

0 commit comments

Comments
 (0)