mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-03 16:10:20 +00:00
Use the Emoji/Map/Poll view models. (#4458)
* Toggle emojis in the EmojiPickerScreenViewModel. * Send locations in the StaticLocationScreen. * Send polls in the PollFormScreen.
This commit is contained in:
@@ -12,12 +12,13 @@ struct PollFormScreenCoordinatorParameters {
|
||||
let mode: PollFormMode
|
||||
/// The max number of allowed options, if no value provided the default value of the view model will be used.
|
||||
var maxNumberOfOptions: Int?
|
||||
let timelineController: TimelineControllerProtocol
|
||||
let analytics: AnalyticsService
|
||||
let userIndicatorController: UserIndicatorControllerProtocol
|
||||
}
|
||||
|
||||
enum PollFormScreenCoordinatorAction {
|
||||
case cancel
|
||||
case delete
|
||||
case submit(question: String, options: [String], pollKind: Poll.Kind)
|
||||
case close
|
||||
}
|
||||
|
||||
final class PollFormScreenCoordinator: CoordinatorProtocol {
|
||||
@@ -30,7 +31,11 @@ final class PollFormScreenCoordinator: CoordinatorProtocol {
|
||||
}
|
||||
|
||||
init(parameters: PollFormScreenCoordinatorParameters) {
|
||||
viewModel = PollFormScreenViewModel(mode: parameters.mode, maxNumberOfOptions: parameters.maxNumberOfOptions)
|
||||
viewModel = PollFormScreenViewModel(mode: parameters.mode,
|
||||
maxNumberOfOptions: parameters.maxNumberOfOptions,
|
||||
timelineController: parameters.timelineController,
|
||||
analytics: parameters.analytics,
|
||||
userIndicatorController: parameters.userIndicatorController)
|
||||
}
|
||||
|
||||
func start() {
|
||||
@@ -39,12 +44,8 @@ final class PollFormScreenCoordinator: CoordinatorProtocol {
|
||||
|
||||
guard let self else { return }
|
||||
switch action {
|
||||
case .cancel:
|
||||
self.actionsSubject.send(.cancel)
|
||||
case .delete:
|
||||
self.actionsSubject.send(.delete)
|
||||
case let .submit(question, options, pollKind):
|
||||
self.actionsSubject.send(.submit(question: question, options: options, pollKind: pollKind))
|
||||
case .close:
|
||||
self.actionsSubject.send(.close)
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
@@ -8,9 +8,7 @@
|
||||
import Foundation
|
||||
|
||||
enum PollFormScreenViewModelAction: Equatable {
|
||||
case cancel
|
||||
case delete
|
||||
case submit(question: String, options: [String], pollKind: Poll.Kind)
|
||||
case close
|
||||
}
|
||||
|
||||
struct PollFormScreenViewState: BindableState {
|
||||
|
||||
@@ -11,13 +11,24 @@ import SwiftUI
|
||||
typealias PollFormScreenViewModelType = StateStoreViewModelV2<PollFormScreenViewState, PollFormScreenViewAction>
|
||||
|
||||
class PollFormScreenViewModel: PollFormScreenViewModelType, PollFormScreenViewModelProtocol {
|
||||
private var actionsSubject: PassthroughSubject<PollFormScreenViewModelAction, Never> = .init()
|
||||
private let timelineController: TimelineControllerProtocol
|
||||
private let analytics: AnalyticsService
|
||||
private let userIndicatorController: UserIndicatorControllerProtocol
|
||||
|
||||
private var actionsSubject: PassthroughSubject<PollFormScreenViewModelAction, Never> = .init()
|
||||
var actions: AnyPublisher<PollFormScreenViewModelAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init(mode: PollFormMode, maxNumberOfOptions: Int? = nil) {
|
||||
init(mode: PollFormMode,
|
||||
maxNumberOfOptions: Int? = nil,
|
||||
timelineController: TimelineControllerProtocol,
|
||||
analytics: AnalyticsService,
|
||||
userIndicatorController: UserIndicatorControllerProtocol) {
|
||||
self.timelineController = timelineController
|
||||
self.analytics = analytics
|
||||
self.userIndicatorController = userIndicatorController
|
||||
|
||||
super.init(initialViewState: .init(mode: mode, maxNumberOfOptions: maxNumberOfOptions ?? 20))
|
||||
}
|
||||
|
||||
@@ -26,24 +37,33 @@ class PollFormScreenViewModel: PollFormScreenViewModelType, PollFormScreenViewMo
|
||||
override func process(viewAction: PollFormScreenViewAction) {
|
||||
switch viewAction {
|
||||
case .submit:
|
||||
actionsSubject.send(.submit(question: state.bindings.question,
|
||||
options: state.bindings.options.map(\.text),
|
||||
pollKind: state.bindings.isUndisclosed ? .undisclosed : .disclosed))
|
||||
let question = state.bindings.question
|
||||
let options = state.bindings.options.map(\.text)
|
||||
let pollKind = state.bindings.isUndisclosed ? Poll.Kind.undisclosed : .disclosed
|
||||
|
||||
Task {
|
||||
switch state.mode {
|
||||
case .new:
|
||||
await createPoll(question: question, options: options, pollKind: pollKind)
|
||||
case .edit(let eventID, _):
|
||||
await editPoll(pollStartID: eventID, question: question, options: options, pollKind: pollKind)
|
||||
}
|
||||
}
|
||||
case .delete:
|
||||
state.bindings.alertInfo = .init(id: .init(),
|
||||
title: L10n.screenEditPollDeleteConfirmationTitle,
|
||||
message: L10n.screenEditPollDeleteConfirmation,
|
||||
primaryButton: .init(title: L10n.actionCancel, role: .cancel, action: nil),
|
||||
secondaryButton: .init(title: L10n.actionOk) { self.actionsSubject.send(.delete) })
|
||||
secondaryButton: .init(title: L10n.actionOk) { Task { await self.deletePoll() } })
|
||||
case .cancel:
|
||||
if state.formContentHasChanged {
|
||||
state.bindings.alertInfo = .init(id: .init(),
|
||||
title: L10n.screenCreatePollCancelConfirmationTitleIos,
|
||||
message: L10n.screenCreatePollCancelConfirmationContentIos,
|
||||
primaryButton: .init(title: L10n.actionCancel, role: .cancel, action: nil),
|
||||
secondaryButton: .init(title: L10n.actionOk) { self.actionsSubject.send(.cancel) })
|
||||
secondaryButton: .init(title: L10n.actionOk) { self.actionsSubject.send(.close) })
|
||||
} else {
|
||||
actionsSubject.send(.cancel)
|
||||
actionsSubject.send(.close)
|
||||
}
|
||||
case .deleteOption(let index):
|
||||
// fixes a crash that caused an index out of range when an option with the keyboard focus was deleted
|
||||
@@ -61,4 +81,45 @@ class PollFormScreenViewModel: PollFormScreenViewModelType, PollFormScreenViewMo
|
||||
state.bindings.options.append(.init())
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func createPoll(question: String, options: [String], pollKind: Poll.Kind) async {
|
||||
guard case .success = await timelineController.createPoll(question: question, answers: options, pollKind: pollKind) else {
|
||||
userIndicatorController.submitIndicator(UserIndicator(title: L10n.errorUnknown))
|
||||
return
|
||||
}
|
||||
|
||||
actionsSubject.send(.close)
|
||||
|
||||
analytics.trackComposer(inThread: false,
|
||||
isEditing: false,
|
||||
isReply: false,
|
||||
messageType: .Poll,
|
||||
startsThread: nil)
|
||||
|
||||
analytics.trackPollCreated(isUndisclosed: pollKind == .undisclosed, numberOfAnswers: options.count)
|
||||
}
|
||||
|
||||
private func editPoll(pollStartID: String, question: String, options: [String], pollKind: Poll.Kind) async {
|
||||
switch await timelineController.editPoll(original: pollStartID, question: question, answers: options, pollKind: pollKind) {
|
||||
case .success:
|
||||
actionsSubject.send(.close)
|
||||
case .failure:
|
||||
userIndicatorController.submitIndicator(UserIndicator(title: L10n.errorUnknown))
|
||||
}
|
||||
}
|
||||
|
||||
private func deletePoll() async {
|
||||
// There aren't any local echoes for redactions, so dismiss the screen early
|
||||
// until we have them: https://github.com/matrix-org/matrix-rust-sdk/issues/4162
|
||||
actionsSubject.send(.close)
|
||||
|
||||
guard case .edit(let pollStartID, _) = state.mode else {
|
||||
userIndicatorController.submitIndicator(UserIndicator(title: L10n.errorUnknown))
|
||||
return
|
||||
}
|
||||
|
||||
await timelineController.redact(.eventID(pollStartID))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,8 +197,8 @@ private struct PollFormOptionRow: View {
|
||||
// MARK: - Previews
|
||||
|
||||
struct PollFormScreen_Previews: PreviewProvider, TestablePreview {
|
||||
static let viewModel = PollFormScreenViewModel(mode: .new)
|
||||
static let editViewModel = PollFormScreenViewModel(mode: .edit(eventID: "1234", poll: poll))
|
||||
static let viewModel = makeViewModel(mode: .new)
|
||||
static let editViewModel = makeViewModel(mode: .edit(eventID: "1234", poll: poll))
|
||||
static let poll = Poll(question: "Cats or Dogs?",
|
||||
kind: .disclosed,
|
||||
maxSelections: 1,
|
||||
@@ -222,6 +222,13 @@ struct PollFormScreen_Previews: PreviewProvider, TestablePreview {
|
||||
}
|
||||
.previewDisplayName("Edit")
|
||||
}
|
||||
|
||||
static func makeViewModel(mode: PollFormMode) -> PollFormScreenViewModel {
|
||||
PollFormScreenViewModel(mode: mode,
|
||||
timelineController: MockTimelineController(),
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: UserIndicatorControllerMock())
|
||||
}
|
||||
}
|
||||
|
||||
private extension Binding where Value == String {
|
||||
|
||||
Reference in New Issue
Block a user