vector-im/element-ios/issues/5114 - Polls in the timeline.

This commit is contained in:
Stefan Ceriu
2021-11-19 17:40:52 +02:00
parent ef4abe6ed2
commit ac6561a3d3
47 changed files with 1717 additions and 223 deletions

View File

@@ -22,6 +22,7 @@ import SwiftUI
struct PollEditFormCoordinatorParameters {
let navigationRouter: NavigationRouterType?
let room: MXRoom
}
final class PollEditFormCoordinator: Coordinator {
@@ -60,12 +61,10 @@ final class PollEditFormCoordinator: Coordinator {
// MARK: - Public
func start() {
guard #available(iOS 14.0, *) else {
MXLog.debug("[PollEditFormCoordinator] start: Invalid iOS version, returning.")
MXLog.error("[PollEditFormCoordinator] start: Invalid iOS version, returning.")
return
}
MXLog.debug("[PollEditFormCoordinator] did start.")
parameters.navigationRouter?.present(pollEditFormHostingController, animated: true)
pollEditFormViewModel.completion = { [weak self] result in
@@ -73,8 +72,30 @@ final class PollEditFormCoordinator: Coordinator {
switch result {
case .cancel:
self.parameters.navigationRouter?.dismissModule(animated: true, completion: nil)
case .create(_, _):
break
case .create(let question, let answerOptions):
var options = [MXEventContentPollStartAnswerOption]()
for answerOption in answerOptions {
options.append(MXEventContentPollStartAnswerOption(uuid: UUID().uuidString, text: answerOption))
}
let pollStartContent = MXEventContentPollStart(question: question,
kind: kMXMessageContentKeyExtensiblePollKindDisclosed,
maxSelections: 1,
answerOptions: options)
self.pollEditFormViewModel.dispatch(action: .startLoading)
self.parameters.room.sendPollStart(withContent: pollStartContent, localEcho: nil) { [weak self] result in
guard let self = self else { return }
self.parameters.navigationRouter?.dismissModule(animated: true, completion: nil)
self.pollEditFormViewModel.dispatch(action: .stopLoading(nil))
} failure: { [weak self] error in
guard let self = self else { return }
MXLog.error("Failed creating poll with error: \(String(describing: error))")
self.pollEditFormViewModel.dispatch(action: .stopLoading(error))
}
}
}
}

View File

@@ -21,6 +21,8 @@ import SwiftUI
enum PollEditFormStateAction {
case viewAction(PollEditFormViewAction)
case startLoading
case stopLoading(Error?)
}
enum PollEditFormViewAction {
@@ -58,7 +60,7 @@ struct PollEditFormAnswerOption: Identifiable, Equatable {
}
struct PollEditFormViewState: BindableState {
let maxAnswerOptionsCount: Int
var maxAnswerOptionsCount: Int
var bindings: PollEditFormViewStateBindings
var confirmationButtonEnabled: Bool {
@@ -69,9 +71,13 @@ struct PollEditFormViewState: BindableState {
var addAnswerOptionButtonEnabled: Bool {
bindings.answerOptions.count < maxAnswerOptionsCount
}
var showLoadingIndicator: Bool = false
}
struct PollEditFormViewStateBindings {
var question: PollEditFormQuestion
var answerOptions: [PollEditFormAnswerOption]
var showsFailureAlert: Bool = false
}

View File

@@ -28,8 +28,8 @@ class PollEditFormViewModel: PollEditFormViewModelType {
private struct Constants {
static let maxAnswerOptionsCount = 20
static let maxQuestionLength = 200
static let maxAnswerOptionLength = 200
static let maxQuestionLength = 340
static let maxAnswerOptionLength = 340
}
// MARK: - Properties
@@ -86,6 +86,16 @@ class PollEditFormViewModel: PollEditFormViewModelType {
default:
break
}
case .startLoading:
state.showLoadingIndicator = true
break
case .stopLoading(let error):
state.showLoadingIndicator = false
if error != nil {
state.bindings.showsFailureAlert = true
}
break
}
}
}

View File

@@ -59,14 +59,18 @@ struct PollEditForm: View {
ForEach(0..<viewModel.answerOptions.count, id: \.self) { index in
SafeBindingCollectionEnumerator($viewModel.answerOptions, index: index) { binding in
AnswerOptionGroup(text: binding.text, index: index) {
viewModel.send(viewAction: .deleteAnswerOption(viewModel.answerOptions[index]))
withAnimation(.easeInOut(duration: 0.2)) {
viewModel.send(viewAction: .deleteAnswerOption(viewModel.answerOptions[index]))
}
}
}
}
}
Button(VectorL10n.pollEditFormAddOption) {
viewModel.send(viewAction: .addAnswerOption)
withAnimation(.easeInOut(duration: 0.2)) {
viewModel.send(viewAction: .addAnswerOption)
}
}
.disabled(!viewModel.viewState.addAnswerOptionButtonEnabled)
@@ -78,8 +82,13 @@ struct PollEditForm: View {
.buttonStyle(PrimaryActionButtonStyle(enabled: viewModel.viewState.confirmationButtonEnabled))
.disabled(!viewModel.viewState.confirmationButtonEnabled)
}
.animation(.easeInOut(duration: 0.2))
.padding()
.activityIndicator(show: viewModel.viewState.showLoadingIndicator)
.alert(isPresented: $viewModel.showsFailureAlert) {
Alert(title: Text(VectorL10n.pollEditFormPostFailureTitle),
message: Text(VectorL10n.pollEditFormPostFailureSubtitle),
dismissButton: .default(Text(VectorL10n.pollEditFormPostFailureAction)))
}
.frame(minHeight: proxy.size.height) // Make the VStack fill the ScrollView's parent
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
@@ -98,6 +107,7 @@ struct PollEditForm: View {
}
}
.accentColor(theme.colors.accent)
.navigationViewStyle(StackNavigationViewStyle())
}
}