Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Details screen of a poll (PSG-1039) #7314

Merged
merged 19 commits into from
Feb 1, 2023
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Config/BuildSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,6 @@ final class BuildSettings: NSObject {
// MARK: - Polls

static let pollsEnabled = true
static var pollsHistoryEnabled: Bool = false

// MARK: - Location Sharing

Expand Down
1 change: 1 addition & 0 deletions Riot/Assets/en.lproj/Vector.strings
Original file line number Diff line number Diff line change
Expand Up @@ -2303,6 +2303,7 @@ Tap the + to start adding people.";
"poll_history_no_past_poll_text" = "There are no past polls in this room";
"poll_history_no_active_poll_period_text" = "There are no active polls for the past %@ days. Load more polls to view polls for previous months";
"poll_history_no_past_poll_period_text" = "There are no past polls for the past %@ days. Load more polls to view polls for previous months";
"poll_history_detail_view_in_timeline" = "View poll in timeline";
"poll_history_load_more" = "Load more polls";
"poll_history_fetching_error" = "Error fetching polls.";

Expand Down
4 changes: 4 additions & 0 deletions Riot/Generated/Strings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4823,6 +4823,10 @@ public class VectorL10n: NSObject {
public static var pollHistoryActiveSegmentTitle: String {
return VectorL10n.tr("Vector", "poll_history_active_segment_title")
}
/// View poll in timeline
public static var pollHistoryDetailViewInTimeline: String {
return VectorL10n.tr("Vector", "poll_history_detail_view_in_timeline")
}
/// Error fetching polls.
public static var pollHistoryFetchingError: String {
return VectorL10n.tr("Vector", "poll_history_fetching_error")
Expand Down
6 changes: 5 additions & 1 deletion Riot/Modules/Room/RoomInfo/RoomInfoCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,12 @@ final class RoomInfoCoordinator: NSObject, RoomInfoCoordinatorType {
coordinator.start()
push(coordinator: coordinator)
case .pollHistory:
let coordinator: PollHistoryCoordinator = .init(parameters: .init(mode: .active, room: self.room))
let coordinator: PollHistoryCoordinator = .init(parameters: .init(mode: .active, room: room, navigationRouter: navigationRouter))
coordinator.start()
coordinator.completion = { [weak self] event in
guard let self else { return }
self.delegate?.roomInfoCoordinator(self, viewEventInTimeline: event)
}
push(coordinator: coordinator)
default:
guard let tabIndex = target.tabIndex else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@
*/

import Foundation
import MatrixSDK

@objc protocol RoomInfoCoordinatorBridgePresenterDelegate {
func roomInfoCoordinatorBridgePresenterDelegateDidComplete(_ coordinatorBridgePresenter: RoomInfoCoordinatorBridgePresenter)
func roomInfoCoordinatorBridgePresenter(_ coordinatorBridgePresenter: RoomInfoCoordinatorBridgePresenter, didRequestMentionForMember member: MXRoomMember)
func roomInfoCoordinatorBridgePresenterDelegateDidLeaveRoom(_ coordinatorBridgePresenter: RoomInfoCoordinatorBridgePresenter)
func roomInfoCoordinatorBridgePresenter(_ coordinatorBridgePresenter: RoomInfoCoordinatorBridgePresenter, didReplaceRoomWithReplacementId roomId: String)
func roomInfoCoordinatorBridgePresenter(_ coordinator: RoomInfoCoordinatorBridgePresenter, viewEventInTimeline event: MXEvent)
}

/// RoomInfoCoordinatorBridgePresenter enables to start RoomInfoCoordinator from a view controller.
Expand Down Expand Up @@ -129,6 +131,9 @@ extension RoomInfoCoordinatorBridgePresenter: RoomInfoCoordinatorDelegate {
func roomInfoCoordinator(_ coordinator: RoomInfoCoordinatorType, didReplaceRoomWithReplacementId roomId: String) {
self.delegate?.roomInfoCoordinatorBridgePresenter(self, didReplaceRoomWithReplacementId: roomId)
}
func roomInfoCoordinator(_ coordinator: RoomInfoCoordinatorType, viewEventInTimeline event: MXEvent) {
self.delegate?.roomInfoCoordinatorBridgePresenter(self, viewEventInTimeline: event)
}
}

// MARK: - UIAdaptivePresentationControllerDelegate
Expand Down
2 changes: 2 additions & 0 deletions Riot/Modules/Room/RoomInfo/RoomInfoCoordinatorType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@
*/

import Foundation
import MatrixSDK

protocol RoomInfoCoordinatorDelegate: AnyObject {
func roomInfoCoordinatorDidComplete(_ coordinator: RoomInfoCoordinatorType)
func roomInfoCoordinator(_ coordinator: RoomInfoCoordinatorType, didRequestMentionForMember member: MXRoomMember)
func roomInfoCoordinatorDidLeaveRoom(_ coordinator: RoomInfoCoordinatorType)
func roomInfoCoordinator(_ coordinator: RoomInfoCoordinatorType, didReplaceRoomWithReplacementId roomId: String)
func roomInfoCoordinator(_ coordinator: RoomInfoCoordinatorType, viewEventInTimeline event: MXEvent)
}

/// `RoomInfoCoordinatorType` is a protocol describing a Coordinator that handle keybackup setup navigation flow.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ final class RoomInfoListViewController: UIViewController {
}
rows.append(rowMembers)

if BuildSettings.pollsHistoryEnabled {
if BuildSettings.pollsEnabled {
rows.append(rowPollHistory)
}

Expand Down
51 changes: 32 additions & 19 deletions Riot/Modules/Room/RoomViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -5257,25 +5257,9 @@ - (IBAction)onButtonPressed:(id)sender
{
// Dismiss potential keyboard.
[self dismissKeyboard];

// Jump to the last unread event by using a temporary room data source initialized with the last unread event id.
MXWeakify(self);
[RoomDataSource loadRoomDataSourceWithRoomId:self.roomDataSource.roomId
initialEventId:self.roomDataSource.room.accountData.readMarkerEventId
threadId:self.roomDataSource.threadId
andMatrixSession:self.mainSession
onComplete:^(id roomDataSource) {
MXStrongifyAndReturnIfNil(self);

[roomDataSource finalizeInitialization];

// Center the bubbles table content on the bottom of the read marker event in order to display correctly the read marker view.
self.centerBubblesTableViewContentOnTheInitialEventBottom = YES;
[self displayRoom:roomDataSource];

// Give the data source ownership to the room view controller.
self.hasRoomDataSourceOwnership = YES;
}];
NSString *eventId = self.roomDataSource.room.accountData.readMarkerEventId;
NSString *threadId = self.roomDataSource.threadId;
[self reloadRoomWihtEventId:eventId threadId:threadId];
}
else if (sender == self.resetReadMarkerButton)
{
Expand Down Expand Up @@ -7871,6 +7855,35 @@ - (void)roomInfoCoordinatorBridgePresenter:(RoomInfoCoordinatorBridgePresenter *
[[AppDelegate theDelegate] showRoomWithParameters:parameters];
}
}
- (void)roomInfoCoordinatorBridgePresenter:(RoomInfoCoordinatorBridgePresenter *)coordinator
viewEventInTimeline:(MXEvent *)event
{
[self.navigationController popToViewController:self animated:true];
[self reloadRoomWihtEventId:event.eventId threadId:event.threadId];
}

-(void)reloadRoomWihtEventId:(NSString *)eventId
threadId:(NSString *)threadId
flescio marked this conversation as resolved.
Show resolved Hide resolved
{
// Jump to the last unread event by using a temporary room data source initialized with the last unread event id.
MXWeakify(self);
[RoomDataSource loadRoomDataSourceWithRoomId:self.roomDataSource.roomId
initialEventId:eventId
threadId:threadId
andMatrixSession:self.mainSession
onComplete:^(id roomDataSource) {
MXStrongifyAndReturnIfNil(self);

[roomDataSource finalizeInitialization];

// Center the bubbles table content on the bottom of the read marker event in order to display correctly the read marker view.
self.centerBubblesTableViewContentOnTheInitialEventBottom = YES;
[self displayRoom:roomDataSource];

// Give the data source ownership to the room view controller.
self.hasRoomDataSourceOwnership = YES;
}];
}

#pragma mark - RemoveJitsiWidgetViewDelegate

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
*/

import UIKit
import MatrixSDK

@objcMembers
final class ExploreRoomCoordinator: NSObject, ExploreRoomCoordinatorType {
Expand Down Expand Up @@ -519,5 +520,8 @@ extension ExploreRoomCoordinator: RoomInfoCoordinatorDelegate {
self.remove(childCoordinator: coordinator)
}
}
func roomInfoCoordinator(_ coordinator: RoomInfoCoordinatorType, viewEventInTimeline event: MXEvent) {

}

}
3 changes: 2 additions & 1 deletion RiotSwiftUI/Modules/Common/Mock/MockAppScreens.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ enum MockAppScreens {
MockComposerCreateActionListScreenState.self,
MockComposerLinkActionScreenState.self,
MockVoiceBroadcastPlaybackScreenState.self,
MockPollHistoryScreenState.self
MockPollHistoryScreenState.self,
MockPollHistoryDetailScreenState.self
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,40 +15,94 @@
//

import CommonKit
import MatrixSDK
import SwiftUI

struct PollHistoryCoordinatorParameters {
let mode: PollHistoryMode
let room: MXRoom
let navigationRouter: NavigationRouterType
}

final class PollHistoryCoordinator: Coordinator, Presentable {
final class PollHistoryCoordinator: NSObject, Coordinator, Presentable {
private let parameters: PollHistoryCoordinatorParameters
private let pollHistoryHostingController: UIViewController
private var pollHistoryViewModel: PollHistoryViewModelProtocol
private let navigationRouter: NavigationRouterType

// Must be used only internally
var childCoordinators: [Coordinator] = []
var completion: (() -> Void)?
var completion: ((MXEvent) -> Void)?

init(parameters: PollHistoryCoordinatorParameters) {
self.parameters = parameters
let viewModel = PollHistoryViewModel(mode: parameters.mode, pollService: PollHistoryService(room: parameters.room, chunkSizeInDays: PollHistoryConstants.chunkSizeInDays))
let view = PollHistory(viewModel: viewModel.context)
pollHistoryViewModel = viewModel
pollHistoryHostingController = VectorHostingController(rootView: view)
navigationRouter = parameters.navigationRouter
}

// MARK: - Public

func start() {
MXLog.debug("[PollHistoryCoordinator] did start.")
pollHistoryViewModel.completion = { _ in

pollHistoryViewModel.completion = { [weak self] result in
switch result {
case .showPollDetail(let poll):
self?.showPollDetail(poll)
}
}
}

func showPollDetail(_ poll: TimelinePollDetails) {
guard let event = parameters.room.mxSession.store.event(withEventId: poll.id, inRoom: parameters.room.roomId),
let detailCoordinator: PollHistoryDetailCoordinator = try? .init(parameters: .init(event: event, room: parameters.room)) else {
pollHistoryViewModel.context.alertInfo = .init(id: true, title: VectorL10n.settingsDiscoveryErrorMessage)
return
}
detailCoordinator.toPresentable().presentationController?.delegate = self
detailCoordinator.completion = { [weak self, weak detailCoordinator, weak event] result in
guard let self, let coordinator = detailCoordinator, let event = event else { return }
self.handlePollDetailResult(result, coordinator: coordinator, event: event, poll: poll)
}

add(childCoordinator: detailCoordinator)
detailCoordinator.start()
toPresentable().present(detailCoordinator.toPresentable(), animated: true)
}

func toPresentable() -> UIViewController {
pollHistoryHostingController
}

private func handlePollDetailResult(_ result: PollHistoryDetailViewModelResult, coordinator: Coordinator, event: MXEvent, poll: TimelinePollDetails) {
switch result {
case .dismiss:
toPresentable().dismiss(animated: true)
remove(childCoordinator: coordinator)
case .viewInTimeline:
toPresentable().dismiss(animated: false)
remove(childCoordinator: coordinator)
var event = event
if poll.closed {
let room = parameters.room
let relatedEvents = room.mxSession.store.relations(forEvent: event.eventId, inRoom: room.roomId, relationType: MXEventRelationTypeReference)
let pollEndedEvent = relatedEvents.first(where: { $0.eventType == .pollEnd })
event = pollEndedEvent ?? event
}
completion?(event)
}
}
}

// MARK: UIAdaptivePresentationControllerDelegate

extension PollHistoryCoordinator: UIAdaptivePresentationControllerDelegate {
func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
guard let coordinator = childCoordinators.last else {
return
}
remove(childCoordinator: coordinator)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import Combine
import CommonKit
import MatrixSDK
import SwiftUI

struct PollHistoryDetailCoordinatorParameters {
let event: MXEvent
let room: MXRoom
}

final class PollHistoryDetailCoordinator: Coordinator, Presentable {
private let parameters: PollHistoryDetailCoordinatorParameters
private let pollHistoryDetailHostingController: UIViewController
private var pollHistoryDetailViewModel: PollHistoryDetailViewModelProtocol

// Must be used only internally
var childCoordinators: [Coordinator] = []
var completion: ((PollHistoryDetailViewModelResult) -> Void)?

init(parameters: PollHistoryDetailCoordinatorParameters) throws {
self.parameters = parameters
let timelinePollCoordinator = try TimelinePollCoordinator(parameters: .init(session: parameters.room.mxSession, room: parameters.room, pollEvent: parameters.event))

let viewModel = PollHistoryDetailViewModel(timelineViewModel: timelinePollCoordinator.viewModel)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the idea behind constructing a viewModel with another viewModel? There should be a 1to1 relationship between a coordinator a view model and the view it controls

let view = PollHistoryDetail(viewModel: viewModel.context)
pollHistoryDetailViewModel = viewModel
pollHistoryDetailHostingController = VectorHostingController(rootView: view)
add(childCoordinator: timelinePollCoordinator)
}

// MARK: - Public

func start() {
MXLog.debug("[PollHistoryDetailCoordinator] did start.")
pollHistoryDetailViewModel.completion = { [weak self] result in
guard let self = self else { return }
switch result {
case .dismiss:
self.completion?(.dismiss)
case .viewInTimeline:
self.completion?(.viewInTimeline)
}
}
}

func toPresentable() -> UIViewController {
pollHistoryDetailHostingController
}
}
Loading