Add max selections support for polls (#5726)

This commit is contained in:
svetoslav-sportinno
2026-06-22 14:39:12 +03:00
committed by GitHub
parent 173a927bf8
commit 30e4e6339d
49 changed files with 506 additions and 251 deletions
@@ -248,9 +248,9 @@ class TimelineInteractionHandler {
// MARK: Polls
func sendPollResponse(pollStartID: String, optionID: String) {
func sendPollResponse(pollStartID: String, answerIDs: [String]) {
Task {
let sendPollResponseResult = await pollInteractionHandler.sendPollResponse(pollStartID: pollStartID, optionID: optionID)
let sendPollResponseResult = await pollInteractionHandler.sendPollResponse(pollStartID: pollStartID, answerIDs: answerIDs)
switch sendPollResponseResult {
case .success:
@@ -36,7 +36,7 @@ enum TimelineViewModelAction {
}
enum TimelineViewPollAction {
case selectOption(pollStartID: String, optionID: String)
case sendResponse(pollStartID: String, answerIDs: [String])
case end(pollStartID: String)
case edit(pollStartID: String, poll: Poll)
}
@@ -376,8 +376,8 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
private func handlePollAction(_ action: TimelineViewPollAction) {
switch action {
case let .selectOption(pollStartID, optionID):
timelineInteractionHandler.sendPollResponse(pollStartID: pollStartID, optionID: optionID)
case let .sendResponse(pollStartID, answerIDs):
timelineInteractionHandler.sendPollResponse(pollStartID: pollStartID, answerIDs: answerIDs)
case let .end(pollStartID):
displayAlert(.pollEndConfirmation(pollStartID))
case .edit(let eventID, let poll):
@@ -10,7 +10,7 @@ import Compound
import SwiftUI
enum PollViewAction {
case selectOption(optionID: String)
case sendResponse(answerIDs: [String])
case edit
case end
}
@@ -100,13 +100,13 @@ struct PollView: View {
}
private func isRemovePreviousSelectionHintEnabled(option: Poll.Option) -> Bool {
!poll.hasEnded && poll.hasMaxSelections && !option.isSelected
!poll.hasEnded && poll.maxSelections == 1 && poll.hasMaxSelections && !option.isSelected
}
private func pollOption(option: Poll.Option) -> some View {
Button {
guard !option.isSelected else { return }
actionHandler(.selectOption(optionID: option.id))
guard let answerIDs = poll.answerIDsAfterSelecting(optionID: option.id) else { return }
actionHandler(.sendResponse(answerIDs: answerIDs))
feedbackGenerator.impactOccurred()
} label: {
PollOptionView(pollOption: option,
@@ -114,7 +114,25 @@ struct PollView: View {
isFinalResult: poll.hasEnded)
.foregroundColor(progressBarColor(for: option))
}
.disabled(poll.hasEnded)
.disabled(isOptionDisabled(option))
}
private func isOptionDisabled(_ option: Poll.Option) -> Bool {
if poll.hasEnded {
return true
}
// Single-choice polls replace the current answer; multi-choice polls keep
// at least one answer while enforcing the max.
if poll.maxSelections == 1 {
return option.isSelected
}
if option.isSelected {
return poll.options.filter(\.isSelected).count == 1
}
return poll.hasMaxSelections
}
@ViewBuilder
@@ -26,9 +26,9 @@ struct PollRoomTimelineView: View {
state: state,
sender: timelineItem.sender) { action in
switch action {
case .selectOption(let optionID):
guard let eventID, let option = poll.options.first(where: { $0.id == optionID }), !option.isSelected else { return }
context.send(viewAction: .handlePollAction(.selectOption(pollStartID: eventID, optionID: option.id)))
case .sendResponse(let answerIDs):
guard let eventID else { return }
context.send(viewAction: .handlePollAction(.sendResponse(pollStartID: eventID, answerIDs: answerIDs)))
case .edit:
guard let eventID else { return }
context.send(viewAction: .handlePollAction(.edit(pollStartID: eventID, poll: poll)))