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

Enhance PollProtocol for poll history (PSG-1043) #1691

Merged
merged 13 commits into from
Jan 24, 2023
22 changes: 15 additions & 7 deletions MatrixSDK/Room/Polls/PollAggregator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ public class PollAggregator {
private let pollStartEventId: String
private let pollBuilder: PollBuilder

private var pollStartedEvent: MXEvent!
private var pollStartEventContent: MXEventContentPollStart!

private var referenceEventsListener: Any?
Expand All @@ -59,7 +60,7 @@ public class PollAggregator {
}
}

public var delegate: PollAggregatorDelegate?
public weak var delegate: PollAggregatorDelegate?

deinit {
if let referenceEventsListener = referenceEventsListener {
Expand All @@ -71,7 +72,7 @@ public class PollAggregator {
}
}

public convenience init(session: MXSession, room: MXRoom, pollEvent: MXEvent) throws {
public convenience init(session: MXSession, room: MXRoom, pollEvent: MXEvent, delegate: PollAggregatorDelegate? = nil) throws {
var pollStartEventId: String?

switch pollEvent.eventType {
Expand All @@ -87,14 +88,15 @@ public class PollAggregator {
throw PollAggregatorError.invalidPollStartEvent
}

try self.init(session: session, room: room, pollStartEventId: pollStartEventId)
try self.init(session: session, room: room, pollStartEventId: pollStartEventId, delegate: delegate)
}

public init(session: MXSession, room: MXRoom, pollStartEventId: String) throws {
public init(session: MXSession, room: MXRoom, pollStartEventId: String, delegate: PollAggregatorDelegate? = nil) throws {
self.session = session
self.room = room
self.pollStartEventId = pollStartEventId
self.pollBuilder = PollBuilder()
self.delegate = delegate

NotificationCenter.default.addObserver(self, selector: #selector(handleRoomDataFlush), name: .mxRoomDidFlushData, object: self.room)
setupEditListener()
Expand Down Expand Up @@ -125,11 +127,13 @@ public class PollAggregator {
throw PollAggregatorError.invalidPollStartEvent
}

pollStartedEvent = event
pollStartEventContent = eventContent

hasBeenEdited = (event.unsignedData.relations?.replace != nil)

poll = pollBuilder.build(pollStartEventContent: eventContent,
pollStartEvent: pollStartedEvent,
events: events,
currentUserIdentifier: session.myUserId,
hasBeenEdited: hasBeenEdited)
Expand Down Expand Up @@ -159,21 +163,25 @@ public class PollAggregator {

let eventTypes = [kMXEventTypeStringPollResponse, kMXEventTypeStringPollResponseMSC3381, kMXEventTypeStringPollEnd, kMXEventTypeStringPollEndMSC3381]
self.referenceEventsListener = self.room.listen(toEventsOfTypes: eventTypes) { [weak self] event, direction, state in
guard let self = self,
let relatedEventId = event.relatesTo?.eventId,
relatedEventId == self.pollStartEventId else {
guard
let self = self,
let relatedEventId = event.relatesTo?.eventId,
relatedEventId == self.pollStartEventId
else {
return
}

self.events.append(event)

self.poll = self.pollBuilder.build(pollStartEventContent: self.pollStartEventContent,
pollStartEvent: self.pollStartedEvent,
events: self.events,
currentUserIdentifier: self.session.myUserId,
hasBeenEdited: self.hasBeenEdited)
} as Any

self.poll = self.pollBuilder.build(pollStartEventContent: self.pollStartEventContent,
pollStartEvent: self.pollStartedEvent,
events: self.events,
currentUserIdentifier: self.session.myUserId,
hasBeenEdited: self.hasBeenEdited)
Expand Down
14 changes: 11 additions & 3 deletions MatrixSDK/Room/Polls/PollBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,15 @@ struct PollBuilder {
static let maxAnswerOptionCount = 20
}

func build(pollStartEventContent: MXEventContentPollStart, events: [MXEvent], currentUserIdentifier: String, hasBeenEdited: Bool = false) -> PollProtocol {
func build(pollStartEventContent: MXEventContentPollStart,
pollStartEvent: MXEvent,
events: [MXEvent],
currentUserIdentifier: String,
hasBeenEdited: Bool = false) -> PollProtocol {

let poll = Poll()
poll.id = pollStartEvent.eventId
poll.startDate = Date(timeIntervalSince1970: Double(pollStartEvent.originServerTs) / 1000)
poll.hasBeenEdited = hasBeenEdited
poll.hasDecryptionError = events.contains(where: { $0.isEncrypted && $0.clear == nil })

Expand Down Expand Up @@ -55,9 +61,11 @@ struct PollBuilder {

var filteredEvents = events.filter { event in
guard
let eventContent = event.content, event.eventType == __MXEventType.pollResponse,
let eventContent = event.content,
event.eventType == .pollResponse,
let response = pollResponseFromEventContent(eventContent),
let _ = response[kMXMessageContentKeyExtensiblePollAnswers] else {
let _ = response[kMXMessageContentKeyExtensiblePollAnswers]
else {
return false
}

Expand Down
8 changes: 5 additions & 3 deletions MatrixSDK/Room/Polls/PollModels.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,11 @@ public protocol PollAnswerOptionProtocol {
}

public protocol PollProtocol {
var id: String { get }
var text: String { get }
var answerOptions: [PollAnswerOptionProtocol] { get }
var kind: PollKind { get }
var startDate: Date { get }
var maxAllowedSelections: UInt { get }
var isClosed: Bool { get }
var totalAnswerCount: UInt { get }
Expand All @@ -43,23 +45,23 @@ public protocol PollProtocol {
class PollAnswerOption: PollAnswerOptionProtocol {
var id: String = ""
var text: String = ""

var count: UInt = 0
var isWinner: Bool = false
var isCurrentUserSelection: Bool = false
}

class Poll: PollProtocol {
var id: String = ""
var text: String = ""
var answerOptions: [PollAnswerOptionProtocol] = []

var kind: PollKind = .disclosed
var startDate: Date = .distantPast
var maxAllowedSelections: UInt = 1
var isClosed: Bool = false
var hasBeenEdited: Bool = false
var hasDecryptionError: Bool = false

var totalAnswerCount: UInt {
return self.answerOptions.reduce(0) { $0 + $1.count}
answerOptions.reduce(0) { $0 + $1.count }
}
}
79 changes: 44 additions & 35 deletions MatrixSDKTests/MXPollAggregatorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,27 +18,35 @@ import Foundation

class MXPollAggregatorTest: XCTestCase {
private var matrixSDKTestsData: MatrixSDKTestsData!

private var matrixSDKTestsE2EData: MatrixSDKTestsE2EData!

private var pollAggregator: PollAggregator!
private var delegate: PollAggregatorBlockWrapper!
private var isFirstDelegateUpdate: Bool = true

override func setUp() {
super.setUp()
matrixSDKTestsData = MatrixSDKTestsData()
matrixSDKTestsE2EData = MatrixSDKTestsE2EData(matrixSDKTestsData: matrixSDKTestsData)
isFirstDelegateUpdate = true
}

override func tearDown() {
matrixSDKTestsData = nil
matrixSDKTestsE2EData = nil
delegate = nil
super.tearDown()
}

func testAggregations() {
self.createScenarioForBobAndAlice { bobSession, aliceSession, bobRoom, aliceRoom, pollStartEvent, expectation in
self.pollAggregator = try! PollAggregator(session: bobSession, room: bobRoom, pollStartEventId: pollStartEvent.eventId)
self.delegate = PollAggregatorBlockWrapper(dataUpdateCallback: { pollAggregator in
XCTAssertEqual(self.pollAggregator.poll.answerOptions.first!.count, 2)
XCTAssertEqual(self.pollAggregator.poll.answerOptions.last!.count, 0)
expectation.fulfill()
})

self.pollAggregator = try! PollAggregator(session: bobSession, room: bobRoom, pollStartEventId: pollStartEvent.eventId)

let dispatchGroup = DispatchGroup()

for _ in 1...5 {
Expand All @@ -58,29 +66,26 @@ class MXPollAggregatorTest: XCTestCase {
}

dispatchGroup.notify(queue: .main) {
self.pollAggregator.delegate = PollAggregatorBlockWrapper(dataUpdateCallback: {
XCTAssertEqual(self.pollAggregator.poll.answerOptions.first!.count, 2)
XCTAssertEqual(self.pollAggregator.poll.answerOptions.last!.count, 0)

expectation.fulfill()
})
self.pollAggregator.delegate = self.delegate
}
}
}

func testSessionPausing() {
self.createScenarioForBobAndAlice { bobSession, aliceSession, bobRoom, aliceRoom, pollStartEvent, expectation in
let delegate = PollAggregatorBlockWrapper(dataUpdateCallback: { aggregator in
XCTAssertEqual(aggregator.poll.answerOptions.first!.count, 2)
XCTAssertEqual(aggregator.poll.answerOptions.last!.count, 0)
})

self.pollAggregator = try! PollAggregator(session: bobSession, room: bobRoom, pollStartEventId: pollStartEvent.eventId)

XCTAssertEqual(self.pollAggregator.poll.answerOptions.first!.count, 1) // One from Alice
XCTAssertEqual(self.pollAggregator.poll.answerOptions.last!.count, 0)

bobSession.pause()

self.pollAggregator.delegate = PollAggregatorBlockWrapper(dataUpdateCallback: {
XCTAssertEqual(self.pollAggregator.poll.answerOptions.first!.count, 2)
XCTAssertEqual(self.pollAggregator.poll.answerOptions.last!.count, 0)
})
self.pollAggregator.delegate = delegate

bobRoom.sendPollResponse(for: pollStartEvent, withAnswerIdentifiers: ["1"], threadId:nil, localEcho: nil) { _ in
bobSession.resume {
Expand All @@ -96,6 +101,12 @@ class MXPollAggregatorTest: XCTestCase {
self.createScenarioForBobAndAlice { bobSession, aliceSession, bobRoom, aliceRoom, pollStartEvent, expectation in
self.pollAggregator = try! PollAggregator(session: bobSession, room: bobRoom, pollStartEventId: pollStartEvent.eventId)

self.delegate = PollAggregatorBlockWrapper(dataUpdateCallback: { aggregator in
XCTAssertEqual(aggregator.poll.answerOptions.first!.count, 2) // One from Bob and one from Alice
XCTAssertEqual(aggregator.poll.answerOptions.last!.count, 1) // One from Alice
expectation.fulfill()
})

XCTAssertEqual(self.pollAggregator.poll.answerOptions.first!.count, 1) // One from Alice
XCTAssertEqual(self.pollAggregator.poll.answerOptions.last!.count, 0)

Expand All @@ -105,12 +116,7 @@ class MXPollAggregatorTest: XCTestCase {
bobRoom.sendPollResponse(for: pollStartEvent, withAnswerIdentifiers: ["1"], threadId:nil, localEcho: nil) { _ in
aliceRoom.sendPollResponse(for: pollStartEvent, withAnswerIdentifiers: ["1", "2"], threadId:nil, localEcho: nil) { _ in
self.matrixSDKTestsData.for(aliceSession.matrixRestClient, andRoom: aliceRoom.roomId, sendMessages: 50, testCase: self) {

self.pollAggregator.delegate = PollAggregatorBlockWrapper(dataUpdateCallback: {
XCTAssertEqual(self.pollAggregator.poll.answerOptions.first!.count, 2) // One from Bob and one from Alice
XCTAssertEqual(self.pollAggregator.poll.answerOptions.last!.count, 1) // One from Alice
expectation.fulfill()
})
self.pollAggregator.delegate = self.delegate

bobSession.resume {

Expand All @@ -131,24 +137,28 @@ class MXPollAggregatorTest: XCTestCase {
self.createScenarioForBobAndAlice { bobSession, aliceSession, bobRoom, aliceRoom, pollStartEvent, expectation in
self.pollAggregator = try! PollAggregator(session: bobSession, room: bobRoom, pollStartEventId: pollStartEvent.eventId)

self.delegate = PollAggregatorBlockWrapper(dataUpdateCallback: { aggregator in
defer {
self.isFirstDelegateUpdate = false
}
guard self.isFirstDelegateUpdate else {
return
}
XCTAssertEqual(aggregator.poll.text, "Some other question")
XCTAssertEqual(aggregator.poll.answerOptions.count, 2)
XCTAssertTrue(aggregator.poll.hasBeenEdited)
expectation.fulfill()
})

let oldContent = MXEventContentPollStart(fromJSON: pollStartEvent.content)!
let newContent = MXEventContentPollStart(question: "Some other question",
kind: oldContent.kind,
maxSelections: oldContent.maxSelections,
answerOptions: [])

self.pollAggregator.delegate = PollAggregatorBlockWrapper(dataUpdateCallback: {

XCTAssertEqual(self.pollAggregator.poll.text, "Some other question")
XCTAssertEqual(self.pollAggregator.poll.answerOptions.count, 0)
XCTAssertTrue(self.pollAggregator.poll.hasBeenEdited)

expectation.fulfill()
self.pollAggregator.delegate = nil
})
answerOptions: oldContent.answerOptions)


bobRoom.sendPollUpdate(for: pollStartEvent, oldContent: oldContent, newContent: newContent, localEcho: nil) { result in

self.pollAggregator.delegate = self.delegate
} failure: { error in
XCTFail("The operation should not fail - NSError: \(String(describing: error))")
}
Expand Down Expand Up @@ -200,10 +210,9 @@ class MXPollAggregatorTest: XCTestCase {
}

private class PollAggregatorBlockWrapper: PollAggregatorDelegate {
let dataUpdateCallback: (PollAggregator) -> (Void)

let dataUpdateCallback: ()->(Void)

internal init(dataUpdateCallback: @escaping () -> (Void)) {
internal init(dataUpdateCallback: @escaping (PollAggregator) -> (Void)) {
self.dataUpdateCallback = dataUpdateCallback
}

Expand All @@ -220,6 +229,6 @@ private class PollAggregatorBlockWrapper: PollAggregatorDelegate {
}

func pollAggregatorDidUpdateData(_ aggregator: PollAggregator) {
dataUpdateCallback()
dataUpdateCallback(aggregator)
}
}
Loading