Skip to content

Commit 043725f

Browse files
authored
[Polls] Comments + Suggestions + Anonymous Polls + LLC Fixes (#3398)
* Refactor PollResultsVC Section views to be more simple and flat hierarchy * Fix bottom spacing in Poll Results Section Header * Add comment buttons to `PollAttachmentView` * Add comment to poll implementation * Fix `poll.latestAnswers` being reset after poll event did not retreive the `latestAnswers` data We should not treat optional `latestAnswers` as empty, since they have different meanings * Add `PollCommentListVC` implementation * Fix `PollVoteListQueryDTO.filterHash` using filterHash only instead of queryHash This was causing the pollId to not be considered when fetching the DB votes * Make diffable data sources more stable * Fix first page of poll vote list resetting all votes * Fix the default vote list sorting * Add poll suggestions feature * Add anonymous polls support * Fix AlertsRouter docs * Add test coverage to `PollCommentListVC` * Add test coverage when poll comments are 0 * Add test coverage to allow suggestions * Add test coverage to anonymous polls * Re-record snapshots after renaming test file * Fix snapshot on CI * Update CHANGELOG.md * Update CHANGELOG.md * Update CHANGELOG.md * Update Sources/StreamChatUI/ChatMessageList/Attachments/Poll/PollAttachmentView.swift * Update Sources/StreamChatUI/ChatMessageList/Attachments/Poll/PollCommentListVC/PollCommentListSectionFooterView.swift * Fix comments and suggestion buttons showing when poll is closed * Update CHANGELOG.md * Change alert titles to follow Apples guideline * Improve comments view spacing * Optimize a bit to check if a user has already a comment
1 parent c3367cb commit 043725f

File tree

68 files changed

+1289
-272
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+1289
-272
lines changed

CHANGELOG.md

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
1111
- Fix `PollOption.latestVotes` sorting [#3374](https://github.com/GetStream/stream-chat-swift/pull/3374)
1212
- Fix `Poll.latestAnswers` sorting [#3374](https://github.com/GetStream/stream-chat-swift/pull/3374)
1313
- Fix `Poll` updates not triggering message updates in `ChannelController` [#3374](https://github.com/GetStream/stream-chat-swift/pull/3374)
14+
- Fix `Poll.latestAnswers` being reset on events, causing "Add a comment" button to not update in the UI SDKs [#3398](https://github.com/GetStream/stream-chat-swift/pull/3398)
15+
- Fix `PollVoteListController` resetting the first page when loading a new page [#3398](https://github.com/GetStream/stream-chat-swift/pull/3398)
16+
- Fix `PollVoteListController` default sorting being from oldest to newest from the server response [#3398](https://github.com/GetStream/stream-chat-swift/pull/3398)
17+
- Fix `PollVoteListQuery.pollId` not limiting the votes query to the given poll id [#3398](https://github.com/GetStream/stream-chat-swift/pull/3398)
1418
### 🔄 Changed
1519
- Deprecates `PollVoteListQuery(pollId:optionId:pagination:filter:)` initializer in favor of `(pollId:filter:pagination:)` [#3381](https://github.com/GetStream/stream-chat-swift/pull/3381)
1620

@@ -19,23 +23,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
1923
- ✨ Introducing `ViewContainerBuilder`, a new, easier way to customize views [#3374](https://github.com/GetStream/stream-chat-swift/pull/3374) (Learn more by reading the docs [here](https://getstream.io/chat/docs/sdk/ios/uikit/custom-components/))
2024
- Add `PollAttachmentView` component to render polls in the message list [#3374](https://github.com/GetStream/stream-chat-swift/pull/3374)
2125
- Add `PollResultsVC` component to show the results of a poll [#3381](https://github.com/GetStream/stream-chat-swift/pull/3381)
26+
- Add `PollCommentListVC` component to show the comments of a poll [#3398](https://github.com/GetStream/stream-chat-swift/pull/3398)
2227
- Add `ChatUserAvatarView.shouldShowOnlineIndicator` to disable the online indicator easily [#3374](https://github.com/GetStream/stream-chat-swift/pull/3374)
2328
### 🎭 New Localizations
24-
- `message.polls.subtitle.selectOne`
25-
- `message.polls.subtitle.selectOneOrMore`
26-
- `message.polls.subtitle.selectUpTo`
27-
- `message.polls.subtitle.voteEnded`
28-
- `message.polls.button.endVote`
29-
- `message.polls.button.viewResults`
30-
- `message.polls.button.showAll`
31-
- `message.polls.votes`
32-
- `message.polls.results-title`
33-
- `message.preview.poll-someone-voted`
34-
- `message.preview.poll-you-voted`
35-
- `message.preview.poll-someone-created`
36-
- `message.preview.poll-you-created`
37-
- `alert.poll.end-title`
38-
- `alert.poll.end`
29+
Multiple localizations were added to Polls, for more details please check the strings file.
30+
- `polls.subtitle.*`
31+
- `polls.button.*`
32+
- `polls.*`
33+
- `alert.poll.*`
34+
- `message.preview.poll-*`
3935

4036
# [4.62.0](https://github.com/GetStream/stream-chat-swift/releases/tag/4.62.0)
4137
_August 15, 2024_

Sources/StreamChat/Database/DTOs/PollDTO.swift

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -167,17 +167,20 @@ extension NSManagedObjectContext {
167167
return optionDto
168168
} ?? []
169169
)
170-
pollDto.latestAnswers = try Set(
171-
payload.latestAnswers?.compactMap { payload in
172-
if let payload {
173-
let answerDto = try savePollVote(payload: payload, query: nil, cache: cache)
174-
answerDto.poll = pollDto
175-
return answerDto
176-
} else {
177-
return nil
170+
171+
if let latestAnswers = payload.latestAnswers {
172+
pollDto.latestAnswers = try Set(
173+
latestAnswers.compactMap { payload in
174+
if let payload {
175+
let answerDto = try savePollVote(payload: payload, query: nil, cache: cache)
176+
answerDto.poll = pollDto
177+
return answerDto
178+
} else {
179+
return nil
180+
}
178181
}
179-
} ?? []
180-
)
182+
)
183+
}
181184

182185
if let payloadOwnVotes = payload.ownVotes, !payload.fromEvent {
183186
pollDto.ownVotes = try Set(

Sources/StreamChat/Database/DTOs/PollVoteDTO.swift

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ extension PollVoteDTO {
7575
extension NSManagedObjectContext {
7676
@discardableResult
7777
func savePollVotes(payload: PollVoteListResponse, query: PollVoteListQuery?, cache: PreWarmedCache?) -> [PollVoteDTO] {
78-
let isFirstPage = query?.pagination.offset == 0
78+
let isFirstPage = query?.pagination.cursor == nil
7979
if let filterHash = query?.queryHash, isFirstPage {
8080
let queryDTO = PollVoteListQueryDTO.load(filterHash: filterHash, context: self)
8181
queryDTO?.votes = []
@@ -247,16 +247,8 @@ extension NSManagedObjectContext {
247247
extension PollVoteDTO {
248248
static func pollVoteListFetchRequest(query: PollVoteListQuery) -> NSFetchRequest<PollVoteDTO> {
249249
let request = NSFetchRequest<PollVoteDTO>(entityName: PollVoteDTO.entityName)
250-
251-
// Fetch results controller requires at least one sorting descriptor.
252-
// At the moment, we do not allow changing the query sorting.
253250
request.sortDescriptors = [.init(key: #keyPath(PollVoteDTO.createdAt), ascending: false)]
254-
255-
// If a filter exists, use is for the predicate. Otherwise, `nil` filter matches all reactions.
256-
if let filterHash = query.filter?.filterHash {
257-
request.predicate = NSPredicate(format: "ANY queries.filterHash == %@", filterHash)
258-
}
259-
251+
request.predicate = NSPredicate(format: "ANY queries.filterHash == %@", query.queryHash)
260252
return request
261253
}
262254
}

Sources/StreamChat/Database/DTOs/PollVoteListQueryDTO.swift

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,21 +53,16 @@ extension NSManagedObjectContext {
5353
}
5454

5555
func saveQuery(query: PollVoteListQuery) throws -> PollVoteListQueryDTO? {
56-
guard let filterHash = query.filter?.filterHash else {
57-
// A query without a filter doesn't have to be saved to the DB because it matches all users by default.
58-
return nil
59-
}
60-
61-
if let existingDTO = PollVoteListQueryDTO.load(filterHash: filterHash, context: self) {
56+
if let existingDTO = PollVoteListQueryDTO.load(filterHash: query.queryHash, context: self) {
6257
return existingDTO
6358
}
6459

6560
let request = PollVoteListQueryDTO.fetchRequest(
6661
keyPath: #keyPath(PollVoteListQueryDTO.filterHash),
67-
equalTo: filterHash
62+
equalTo: query.queryHash
6863
)
6964
let newDTO = NSEntityDescription.insertNewObject(into: self, for: request)
70-
newDTO.filterHash = filterHash
65+
newDTO.filterHash = query.queryHash
7166

7267
do {
7368
newDTO.filterJSONData = try JSONEncoder.default.encode(query.filter)

Sources/StreamChat/Models/Poll.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ import Foundation
66

77
/// The model for a Poll.
88
public struct Poll: Equatable {
9-
/// A boolean indicating whether the poll allows answers.
9+
/// A boolean indicating whether the poll allows answers/comments.
1010
public let allowAnswers: Bool
1111

1212
/// A boolean indicating whether the poll allows user-suggested options.
1313
public let allowUserSuggestedOptions: Bool
1414

15-
/// The count of answers received for the poll.
15+
/// The count of answers/comments received for the poll.
1616
public let answersCount: Int
1717

1818
/// The date and time when the poll was created.
@@ -60,7 +60,7 @@ public struct Poll: Equatable {
6060
/// This property is optional and may be `nil`.
6161
public let createdBy: ChatUser?
6262

63-
/// A list of the latest answers received for the poll.
63+
/// A list of the latest answers/comments received for the poll.
6464
public let latestAnswers: [PollVote]
6565

6666
/// An array of options available in the poll.

Sources/StreamChat/Query/PollVoteListQuery.swift

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ public struct PollVoteListQuery: Encodable {
1212
public var optionId: String?
1313
/// The pagination information to query the votes.
1414
public var pagination: Pagination
15+
// The sorting parameter. By default votes are sorted by newest first.
16+
public var sorting: [Sorting<PollVoteListSortingKey>]
1517
/// The filter details to query the votes.
1618
public var filter: Filter<VoteListFilterScope>?
1719

@@ -27,49 +29,73 @@ public struct PollVoteListQuery: Encodable {
2729
pollId: String,
2830
optionId: String?,
2931
pagination: Pagination = .init(pageSize: 10, offset: 0),
32+
sorting: [Sorting<PollVoteListSortingKey>] = [.init(key: .createdAt, isAscending: false)],
3033
filter: Filter<VoteListFilterScope>? = nil
3134
) {
3235
self.pollId = pollId
3336
self.optionId = optionId
3437
self.pagination = pagination
38+
self.sorting = sorting
3539
self.filter = filter
3640
}
3741

3842
/// Creates a vote list query for the given pollId and the provided filter.
3943
public init(
4044
pollId: String,
4145
filter: Filter<VoteListFilterScope>? = nil,
42-
pagination: Pagination = .init(pageSize: 10, offset: 0)
46+
pagination: Pagination = .init(pageSize: 10, offset: 0),
47+
sorting: [Sorting<PollVoteListSortingKey>] = [.init(key: .createdAt, isAscending: false)]
4348
) {
4449
self.pollId = pollId
4550
self.pagination = pagination
51+
self.sorting = sorting
4652
self.filter = filter
4753
}
4854

4955
/// Creates a vote list query for the given pollId and optionId.
5056
public init(
5157
pollId: String,
5258
optionId: String,
53-
pagination: Pagination = .init(pageSize: 10, offset: 0)
59+
pagination: Pagination = .init(pageSize: 10, offset: 0),
60+
sorting: [Sorting<PollVoteListSortingKey>] = [.init(key: .createdAt, isAscending: false)]
5461
) {
5562
self.pollId = pollId
5663
self.optionId = optionId
5764
self.pagination = pagination
65+
self.sorting = sorting
5866
filter = .equal(.optionId, to: optionId)
5967
}
6068

6169
enum CodingKeys: CodingKey {
6270
case pagination
6371
case filter
72+
case sort
6473
}
6574

6675
public func encode(to encoder: Encoder) throws {
6776
var container = encoder.container(keyedBy: CodingKeys.self)
6877
try container.encodeIfPresent(filter, forKey: .filter)
78+
if !sorting.isEmpty {
79+
try container.encode(sorting, forKey: .sort)
80+
}
6981
try pagination.encode(to: encoder)
7082
}
7183
}
7284

85+
/// The type describing a value that can be used as a sorting when paginating a list of votes in a poll.
86+
public struct PollVoteListSortingKey: RawRepresentable, Hashable, SortingKey {
87+
public let rawValue: String
88+
89+
public init(rawValue: String) {
90+
self.rawValue = rawValue
91+
}
92+
}
93+
94+
public extension PollVoteListSortingKey {
95+
/// Sorts votes by `created_at` field.
96+
static let createdAt = Self(rawValue: PollVotePayload.CodingKeys.createdAt.rawValue)
97+
}
98+
7399
/// A namespace for the `FilterKey`s suitable to be used for `PollVoteListQuery`.
74100
public protocol AnyVoteListFilterScope {}
75101

Sources/StreamChat/Repositories/PollsRepository.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ class PollsRepository {
285285
try session.linkVote(
286286
with: pollVote.id,
287287
in: pollVote.pollId,
288-
to: query.filter?.filterHash
288+
to: query.queryHash
289289
)
290290
}
291291
}

Sources/StreamChatUI/ChatMessageList/Attachments/Poll/PollAttachmentOptionListView/PollAttachmentOptionListItemView.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,9 @@ open class PollAttachmentOptionListItemView: _View, ThemeProvider {
130130

131131
optionNameLabel.text = content.option.text
132132
votesCountLabel.text = "\(content.voteCount)"
133+
133134
latestVotesAuthorsView.content = .init(users: latestVotesAuthors)
135+
latestVotesAuthorsView.isHidden = content.poll.votingVisibility == .anonymous
134136

135137
if content.isVotedByCurrentUser {
136138
voteCheckboxButton.setCheckedState()

0 commit comments

Comments
 (0)