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
62 changes: 34 additions & 28 deletions MatrixSDK/Room/Polls/PollAggregator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ public class PollAggregator {

private let session: MXSession
private let room: MXRoom
private let pollStartEventId: String
private let pollBuilder: PollBuilder

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

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

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

deinit {
if let referenceEventsListener = referenceEventsListener {
Expand All @@ -71,7 +71,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,18 +87,28 @@ 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

guard
let event = session.store.event(withEventId: pollStartEventId, inRoom: room.roomId),
let eventContent = MXEventContentPollStart(fromJSON: event.content),
eventContent.answerOptions.count >= Constants.minAnswerOptionCount
else {
throw PollAggregatorError.invalidPollStartEvent
}

pollStartedEvent = event
pollStartEventContent = eventContent
NotificationCenter.default.addObserver(self, selector: #selector(handleRoomDataFlush), name: .mxRoomDidFlushData, object: self.room)
setupEditListener()
try buildPollStartContent()
buildPoll()
}

private func setupEditListener() {
Expand All @@ -109,27 +119,19 @@ public class PollAggregator {
return
}

do {
try self.buildPollStartContent()
} catch {
self.delegate?.pollAggregator(self, didFailWithError: PollAggregatorError.invalidPollStartEvent)
}
self.buildPoll()
}
}

private func buildPollStartContent() throws {
guard let event = session.store.event(withEventId: pollStartEventId, inRoom: room.roomId),
let eventContent = MXEventContentPollStart(fromJSON: event.content),
eventContent.answerOptions.count >= Constants.minAnswerOptionCount
else {
throw PollAggregatorError.invalidPollStartEvent
}

pollStartEventContent = eventContent

hasBeenEdited = (event.unsignedData.relations?.replace != nil)
private var pollStartEventId: String {
pollStartedEvent.eventId
}

private func buildPoll() {
hasBeenEdited = (pollStartedEvent.unsignedData.relations?.replace != nil)

poll = pollBuilder.build(pollStartEventContent: eventContent,
poll = pollBuilder.build(pollStartEventContent: pollStartEventContent,
pollStartEvent: pollStartedEvent,
events: events,
currentUserIdentifier: session.myUserId,
hasBeenEdited: hasBeenEdited)
Expand Down Expand Up @@ -159,21 +161,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 }
}
}
53 changes: 28 additions & 25 deletions MatrixSDKTests/MXPollAggregatorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ class MXPollAggregatorTest: XCTestCase {
self.createScenarioForBobAndAlice { bobSession, aliceSession, bobRoom, aliceRoom, pollStartEvent, expectation in
self.pollAggregator = try! PollAggregator(session: bobSession, room: bobRoom, pollStartEventId: pollStartEvent.eventId)

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

let dispatchGroup = DispatchGroup()

for _ in 1...5 {
Expand All @@ -58,29 +64,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 = delegate
}
}
}

func testSessionPausing() {
self.createScenarioForBobAndAlice { bobSession, aliceSession, bobRoom, aliceRoom, pollStartEvent, expectation in
let delegate = PollAggregatorBlockWrapper(dataUpdateCallback: {
XCTAssertEqual(self.pollAggregator.poll.answerOptions.first!.count, 2)
XCTAssertEqual(self.pollAggregator.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 +99,12 @@ class MXPollAggregatorTest: XCTestCase {
self.createScenarioForBobAndAlice { bobSession, aliceSession, bobRoom, aliceRoom, pollStartEvent, expectation in
self.pollAggregator = try! PollAggregator(session: bobSession, room: bobRoom, pollStartEventId: pollStartEvent.eventId)

let 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()
})

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

Expand All @@ -105,12 +114,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 = delegate

bobSession.resume {

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

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: {

let delegate = PollAggregatorBlockWrapper(dataUpdateCallback: {
XCTAssertEqual(self.pollAggregator.poll.text, "Some other question")
XCTAssertEqual(self.pollAggregator.poll.answerOptions.count, 0)
XCTAssertTrue(self.pollAggregator.poll.hasBeenEdited)
Expand All @@ -147,6 +144,13 @@ class MXPollAggregatorTest: XCTestCase {
self.pollAggregator.delegate = nil
})

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

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

} failure: { error in
Expand Down Expand Up @@ -200,7 +204,6 @@ class MXPollAggregatorTest: XCTestCase {
}

private class PollAggregatorBlockWrapper: PollAggregatorDelegate {

let dataUpdateCallback: ()->(Void)

internal init(dataUpdateCallback: @escaping () -> (Void)) {
Expand Down
Loading