Forward current composer text to the new poll form to serve as the question/topic

This commit is contained in:
Stefan Ceriu
2026-05-22 13:01:22 +03:00
parent ac20ff0fc3
commit 77c5aba326
11 changed files with 26 additions and 21 deletions
@@ -22,8 +22,8 @@ struct PollFormScreenViewState: BindableState {
self.maxNumberOfOptions = maxNumberOfOptions self.maxNumberOfOptions = maxNumberOfOptions
switch mode { switch mode {
case .new: case .new(let topic):
bindings = .init() bindings = .init(question: topic ?? "")
case .edit(_, let poll): case .edit(_, let poll):
bindings = .init(poll: poll) bindings = .init(poll: poll)
} }
@@ -71,7 +71,7 @@ struct PollFormScreenViewState: BindableState {
} }
enum PollFormMode: Hashable { enum PollFormMode: Hashable {
case new case new(topic: String?)
case edit(eventID: String, poll: Poll) case edit(eventID: String, poll: Poll)
} }
@@ -198,7 +198,7 @@ private struct PollFormOptionRow: View {
// MARK: - Previews // MARK: - Previews
struct PollFormScreen_Previews: PreviewProvider, TestablePreview { struct PollFormScreen_Previews: PreviewProvider, TestablePreview {
static let viewModel = makeViewModel(mode: .new) static let viewModel = makeViewModel(mode: .new(topic: nil))
static let editViewModel = makeViewModel(mode: .edit(eventID: "1234", poll: poll)) static let editViewModel = makeViewModel(mode: .edit(eventID: "1234", poll: poll))
static let poll = Poll(question: "Cats or Dogs?", static let poll = Poll(question: "Cats or Dogs?",
kind: .disclosed, kind: .disclosed,
@@ -72,7 +72,7 @@ class MediaEventsTimelineScreenViewModel: MediaEventsTimelineScreenViewModelType
case .displayMediaDetails(item: let item): case .displayMediaDetails(item: let item):
displayMediaPreviewSheet(for: item) displayMediaPreviewSheet(for: item)
case .displayEmojiPicker, .displayReportContent, .displayCameraPicker, .displayMediaPicker, case .displayEmojiPicker, .displayReportContent, .displayCameraPicker, .displayMediaPicker,
.displayDocumentPicker, .displayLocationPicker, .displayLiveLocation, .displayPollForm, .displayMediaUploadPreviewScreen, .displayDocumentPicker, .displayLocationPicker, .displayLiveLocation, .displayNewPollForm, .displayEditPollForm, .displayMediaUploadPreviewScreen,
.displaySenderDetails, .displayMessageForwarding, .displayLocation, .displayResolveSendFailure, .displaySenderDetails, .displayMessageForwarding, .displayLocation, .displayResolveSendFailure,
.displayThread, .composer, .hasScrolled, .viewInRoomTimeline, .displayRoom: .displayThread, .composer, .hasScrolled, .viewInRoomTimeline, .displayRoom:
break break
@@ -97,7 +97,7 @@ class MediaEventsTimelineScreenViewModel: MediaEventsTimelineScreenViewModelType
case .displayMediaDetails(item: let item): case .displayMediaDetails(item: let item):
displayMediaPreviewSheet(for: item) displayMediaPreviewSheet(for: item)
case .displayEmojiPicker, .displayReportContent, .displayCameraPicker, .displayMediaPicker, case .displayEmojiPicker, .displayReportContent, .displayCameraPicker, .displayMediaPicker,
.displayDocumentPicker, .displayLocationPicker, .displayLiveLocation, .displayPollForm, .displayMediaUploadPreviewScreen, .displayDocumentPicker, .displayLocationPicker, .displayLiveLocation, .displayNewPollForm, .displayEditPollForm, .displayMediaUploadPreviewScreen,
.displaySenderDetails, .displayMessageForwarding, .displayLocation, .displayResolveSendFailure, .displaySenderDetails, .displayMessageForwarding, .displayLocation, .displayResolveSendFailure,
.displayThread, .composer, .hasScrolled, .viewInRoomTimeline, .displayRoom: .displayThread, .composer, .hasScrolled, .viewInRoomTimeline, .displayRoom:
break break
@@ -96,7 +96,7 @@ final class PinnedEventsTimelineScreenCoordinator: CoordinatorProtocol {
actionsSubject.send(.displayRoomScreenWithFocussedPin(eventID: eventID, threadRootEventID: threadRootEventID)) actionsSubject.send(.displayRoomScreenWithFocussedPin(eventID: eventID, threadRootEventID: threadRootEventID))
// These other actions will not be handled in this view // These other actions will not be handled in this view
case .displayEmojiPicker, .displayReportContent, .displayCameraPicker, .displayMediaPicker, case .displayEmojiPicker, .displayReportContent, .displayCameraPicker, .displayMediaPicker,
.displayDocumentPicker, .displayLocationPicker, .displayPollForm, .displayMediaUploadPreviewScreen, .displayDocumentPicker, .displayLocationPicker, .displayNewPollForm, .displayEditPollForm, .displayMediaUploadPreviewScreen,
.displayResolveSendFailure, .displayThread, .composer, .hasScrolled, .displayRoom, .displayMediaDetails: .displayResolveSendFailure, .displayThread, .composer, .hasScrolled, .displayRoom, .displayMediaDetails:
// These actions are not handled in this coordinator // These actions are not handled in this coordinator
break break
@@ -134,8 +134,10 @@ final class RoomScreenCoordinator: CoordinatorProtocol {
roomViewModel.displayMediaPreview(mediaPreviewViewModel) roomViewModel.displayMediaPreview(mediaPreviewViewModel)
case .displayLocationPicker: case .displayLocationPicker:
actionsSubject.send(.presentLocationPicker) actionsSubject.send(.presentLocationPicker)
case .displayPollForm(let mode): case .displayNewPollForm:
actionsSubject.send(.presentPollForm(mode: mode)) actionsSubject.send(.presentPollForm(mode: .new(topic: composerViewModel.context.plainComposerText.string)))
case .displayEditPollForm(let eventID, let poll):
actionsSubject.send(.presentPollForm(mode: .edit(eventID: eventID, poll: poll)))
case .displayMediaUploadPreviewScreen(let mediaURLs): case .displayMediaUploadPreviewScreen(let mediaURLs):
actionsSubject.send(.presentMediaUploadPreviewScreen(mediaURLs: mediaURLs, actionsSubject.send(.presentMediaUploadPreviewScreen(mediaURLs: mediaURLs,
caption: composerViewModel.context.plainComposerText)) caption: composerViewModel.context.plainComposerText))
@@ -126,8 +126,10 @@ final class ThreadTimelineScreenCoordinator: CoordinatorProtocol {
actionsSubject.send(.presentLocationViewer(location)) actionsSubject.send(.presentLocationViewer(location))
case .displayLiveLocation(let sender, let initialLiveLocationShare): case .displayLiveLocation(let sender, let initialLiveLocationShare):
actionsSubject.send(.presentLiveLocationViewer(sender: sender, initialLiveLocationShare: initialLiveLocationShare)) actionsSubject.send(.presentLiveLocationViewer(sender: sender, initialLiveLocationShare: initialLiveLocationShare))
case .displayPollForm(let mode): case .displayNewPollForm:
actionsSubject.send(.presentPollForm(mode: mode)) actionsSubject.send(.presentPollForm(mode: .new(topic: composerViewModel.context.plainComposerText.string)))
case .displayEditPollForm(let eventID, let poll):
actionsSubject.send(.presentPollForm(mode: .edit(eventID: eventID, poll: poll)))
case .displayMediaUploadPreviewScreen(let mediaURLs): case .displayMediaUploadPreviewScreen(let mediaURLs):
actionsSubject.send(.presentMediaUploadPreviewScreen(mediaURLs: mediaURLs, actionsSubject.send(.presentMediaUploadPreviewScreen(mediaURLs: mediaURLs,
caption: composerViewModel.context.plainComposerText)) caption: composerViewModel.context.plainComposerText))
@@ -16,7 +16,7 @@ enum TimelineInteractionHandlerAction {
case displayReportContent(itemID: TimelineItemIdentifier, senderID: String) case displayReportContent(itemID: TimelineItemIdentifier, senderID: String)
case displayMessageForwarding(itemID: TimelineItemIdentifier) case displayMessageForwarding(itemID: TimelineItemIdentifier)
case displayMediaUploadPreviewScreen(mediaURLs: [URL]) case displayMediaUploadPreviewScreen(mediaURLs: [URL])
case displayPollForm(mode: PollFormMode) case displayEditPollForm(eventID: String, poll: Poll)
case showActionMenu(TimelineItemActionMenuInfo) case showActionMenu(TimelineItemActionMenuInfo)
case showDebugInfo(TimelineItemDebugInfo) case showDebugInfo(TimelineItemDebugInfo)
@@ -130,7 +130,7 @@ class TimelineInteractionHandler {
MXLog.error("Cannot edit poll with id: \(timelineItem.id)") MXLog.error("Cannot edit poll with id: \(timelineItem.id)")
return return
} }
actionsSubject.send(.displayPollForm(mode: .edit(eventID: eventID, poll: pollTimelineItem.poll))) actionsSubject.send(.displayEditPollForm(eventID: eventID, poll: pollTimelineItem.poll))
default: default:
MXLog.error("Cannot edit item with id: \(timelineItem.id)") MXLog.error("Cannot edit item with id: \(timelineItem.id)")
} }
@@ -18,7 +18,8 @@ enum TimelineViewModelAction {
case displayMediaPicker case displayMediaPicker
case displayDocumentPicker case displayDocumentPicker
case displayLocationPicker case displayLocationPicker
case displayPollForm(mode: PollFormMode) case displayNewPollForm
case displayEditPollForm(eventID: String, poll: Poll)
case displayMediaUploadPreviewScreen(mediaURLs: [URL]) case displayMediaUploadPreviewScreen(mediaURLs: [URL])
case displaySenderDetails(userID: String) case displaySenderDetails(userID: String)
case displayMessageForwarding(forwardingItem: MessageForwardingItem) case displayMessageForwarding(forwardingItem: MessageForwardingItem)
@@ -370,7 +370,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
case .location: case .location:
actionsSubject.send(.displayLocationPicker) actionsSubject.send(.displayLocationPicker)
case .poll: case .poll:
actionsSubject.send(.displayPollForm(mode: .new)) actionsSubject.send(.displayNewPollForm)
} }
} }
@@ -380,8 +380,8 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
timelineInteractionHandler.sendPollResponse(pollStartID: pollStartID, optionID: optionID) timelineInteractionHandler.sendPollResponse(pollStartID: pollStartID, optionID: optionID)
case let .end(pollStartID): case let .end(pollStartID):
displayAlert(.pollEndConfirmation(pollStartID)) displayAlert(.pollEndConfirmation(pollStartID))
case .edit(let pollStartID, let poll): case .edit(let eventID, let poll):
actionsSubject.send(.displayPollForm(mode: .edit(eventID: pollStartID, poll: poll))) actionsSubject.send(.displayEditPollForm(eventID: eventID, poll: poll))
} }
} }
@@ -514,8 +514,8 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
actionsSubject.send(.displayEmojiPicker(itemID: itemID, selectedEmojis: selectedEmojis)) actionsSubject.send(.displayEmojiPicker(itemID: itemID, selectedEmojis: selectedEmojis))
case .displayMessageForwarding(let itemID): case .displayMessageForwarding(let itemID):
Task { await self.forwardMessage(itemID: itemID) } Task { await self.forwardMessage(itemID: itemID) }
case .displayPollForm(let mode): case .displayEditPollForm(let eventID, let poll):
actionsSubject.send(.displayPollForm(mode: mode)) actionsSubject.send(.displayEditPollForm(eventID: eventID, poll: poll))
case .displayReportContent(let itemID, let senderID): case .displayReportContent(let itemID, let senderID):
actionsSubject.send(.displayReportContent(itemID: itemID, senderID: senderID)) actionsSubject.send(.displayReportContent(itemID: itemID, senderID: senderID))
case .displayMediaUploadPreviewScreen(let mediaURLs): case .displayMediaUploadPreviewScreen(let mediaURLs):
@@ -722,7 +722,7 @@ class MockScreen: Identifiable {
return PlaceholderScreenCoordinator(hideBrandChrome: false) return PlaceholderScreenCoordinator(hideBrandChrome: false)
case .createPoll: case .createPoll:
let navigationStackCoordinator = NavigationStackCoordinator() let navigationStackCoordinator = NavigationStackCoordinator()
let coordinator = PollFormScreenCoordinator(parameters: .init(mode: .new, let coordinator = PollFormScreenCoordinator(parameters: .init(mode: .new(topic: nil),
maxNumberOfOptions: 10, maxNumberOfOptions: 10,
timelineController: MockTimelineController(), timelineController: MockTimelineController(),
analytics: analytics, analytics: analytics,
@@ -176,7 +176,7 @@ struct PollFormScreenViewModelTests {
// MARK: - Helpers // MARK: - Helpers
private mutating func setupViewModel(mode: PollFormMode = .new) { private mutating func setupViewModel(mode: PollFormMode = .new(topic: nil)) {
viewModel = PollFormScreenViewModel(mode: mode, viewModel = PollFormScreenViewModel(mode: mode,
timelineController: MockTimelineController(timelineProxy: timelineProxy), timelineController: MockTimelineController(timelineProxy: timelineProxy),
analytics: AnalyticsServiceMock.default, analytics: AnalyticsServiceMock.default,