Skip to content

Commit 337074e

Browse files
xsahil03xd3xvn
andauthored
feat(ui, core, localization): Add Poll attachment interactor (#2052)
Co-authored-by: Deven Joshi <[email protected]> Co-authored-by: xsahil03x <[email protected]>
1 parent 714a194 commit 337074e

File tree

131 files changed

+6999
-113
lines changed

Some content is hidden

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

131 files changed

+6999
-113
lines changed

.github/workflows/update_goldens.yml

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,8 @@ jobs:
66
update_goldens:
77
runs-on: ubuntu-latest
88
steps:
9-
- name: 🚫 Ensure branch is not master
10-
if: ${{ github.event.inputs.branch == 'master' || github.event.inputs.branch == 'origin/master'}}
11-
run: |
12-
echo "Updating goldens on 'master' branch is prohibited."
13-
exit 1
14-
159
- name: 📚 Checkout branch
1610
uses: actions/checkout@v4
17-
with:
18-
ref: ${{ github.event.inputs.branch }}
1911

2012
- name: 🐦 Install Flutter
2113
uses: subosito/flutter-action@v2
@@ -32,10 +24,8 @@ jobs:
3224
run: melos bootstrap --verbose
3325

3426
- name: 🖼️ Update Goldens
35-
working-directory: packages/stream_chat_flutter
3627
continue-on-error: true
37-
run: |
38-
flutter test --tags golden --update-goldens
28+
run: melos run update:goldens
3929

4030
- name: 📤 Commit Changes
4131
id: commit_changes

melos.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,10 @@ scripts:
159159
flutter: true
160160
dirExists: test
161161

162+
update:goldens:
163+
run: melos exec -c 1 --depends-on="alchemist" -- "flutter test --tags golden --update-goldens"
164+
description: Update golden files for all packages in this project.
165+
162166
clean:flutter:
163167
run: melos exec -c 4 --fail-fast -- "flutter clean"
164168
description: Run Flutter clean for a specific package in this project.

packages/stream_chat/lib/src/client/channel.dart

Lines changed: 119 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1035,6 +1035,8 @@ class Channel {
10351035
return _client.sendEvent(id!, type, event);
10361036
}
10371037

1038+
final _pollLock = Lock();
1039+
10381040
/// Send a message with a poll to this channel.
10391041
///
10401042
/// Optionally provide a [messageText] to send a message along with the poll.
@@ -1043,7 +1045,7 @@ class Channel {
10431045
String messageText = '',
10441046
}) async {
10451047
_checkInitialized();
1046-
final res = await _client.createPoll(poll);
1048+
final res = await _pollLock.synchronized(() => _client.createPoll(poll));
10471049
return sendMessage(
10481050
Message(
10491051
text: messageText,
@@ -1056,13 +1058,109 @@ class Channel {
10561058
/// Updates the [poll] in this channel.
10571059
Future<UpdatePollResponse> updatePoll(Poll poll) {
10581060
_checkInitialized();
1059-
return _client.updatePoll(poll);
1061+
return _pollLock.synchronized(() => _client.updatePoll(poll));
1062+
}
1063+
1064+
/// Deletes the given [poll] from this channel.
1065+
Future<EmptyResponse> deletePoll(Poll poll) {
1066+
_checkInitialized();
1067+
return _pollLock.synchronized(() => _client.deletePoll(poll.id));
1068+
}
1069+
1070+
/// Close the given [poll].
1071+
Future<UpdatePollResponse> closePoll(Poll poll) {
1072+
_checkInitialized();
1073+
return _pollLock.synchronized(() => _client.closePoll(poll.id));
10601074
}
10611075

1062-
/// Deletes the poll with the given [pollId] from this channel.
1063-
Future<EmptyResponse> deletePoll(String pollId) {
1076+
/// Create a new poll option for the given [poll].
1077+
Future<CreatePollOptionResponse> createPollOption(
1078+
Poll poll,
1079+
PollOption option,
1080+
) {
1081+
_checkInitialized();
1082+
return _pollLock.synchronized(
1083+
() => _client.createPollOption(poll.id, option),
1084+
);
1085+
}
1086+
1087+
final _pollVoteLock = Lock();
1088+
1089+
/// Cast a vote on the given [poll] with the given [option].
1090+
Future<CastPollVoteResponse> castPollVote(
1091+
Message message,
1092+
Poll poll,
1093+
PollOption option,
1094+
) async {
10641095
_checkInitialized();
1065-
return _client.deletePoll(pollId);
1096+
1097+
final optionId = option.id;
1098+
if (optionId == null) {
1099+
throw ArgumentError('Option id cannot be null');
1100+
}
1101+
1102+
return _pollVoteLock.synchronized(
1103+
() => _client.castPollVote(
1104+
message.id,
1105+
poll.id,
1106+
optionId: optionId,
1107+
),
1108+
);
1109+
}
1110+
1111+
/// Add a new answer to the given [poll].
1112+
Future<CastPollVoteResponse> addPollAnswer(
1113+
Message message,
1114+
Poll poll, {
1115+
required String answerText,
1116+
}) {
1117+
_checkInitialized();
1118+
return _pollVoteLock.synchronized(
1119+
() => _client.addPollAnswer(
1120+
message.id,
1121+
poll.id,
1122+
answerText: answerText,
1123+
),
1124+
);
1125+
}
1126+
1127+
/// Remove a vote on the given [poll] with the given [vote].
1128+
Future<RemovePollVoteResponse> removePollVote(
1129+
Message message,
1130+
Poll poll,
1131+
PollVote vote,
1132+
) {
1133+
_checkInitialized();
1134+
1135+
final voteId = vote.id;
1136+
if (voteId == null) {
1137+
throw ArgumentError('Vote id cannot be null');
1138+
}
1139+
1140+
return _pollVoteLock.synchronized(
1141+
() => _client.removePollVote(
1142+
message.id,
1143+
poll.id,
1144+
voteId,
1145+
),
1146+
);
1147+
}
1148+
1149+
/// Query the poll votes for the given [pollId] with the given [filter] and
1150+
/// [sort] options.
1151+
Future<QueryPollVotesResponse> queryPollVotes(
1152+
String pollId, {
1153+
Filter? filter,
1154+
List<SortOption>? sort,
1155+
PaginationParams pagination = const PaginationParams(),
1156+
}) {
1157+
_checkInitialized();
1158+
return _client.queryPollVotes(
1159+
pollId,
1160+
filter: filter,
1161+
sort: sort,
1162+
pagination: pagination,
1163+
);
10661164
}
10671165

10681166
/// Send a reaction to this channel.
@@ -2120,12 +2218,12 @@ class ChannelClientState {
21202218

21212219
final oldPoll = pollMessage.poll;
21222220

2123-
final answers = oldPoll?.answers ?? eventPoll.answers;
2221+
final latestAnswers = oldPoll?.latestAnswers ?? eventPoll.latestAnswers;
21242222
final ownVotesAndAnswers =
21252223
oldPoll?.ownVotesAndAnswers ?? eventPoll.ownVotesAndAnswers;
21262224

21272225
final poll = eventPoll.copyWith(
2128-
answers: answers,
2226+
latestAnswers: latestAnswers,
21292227
ownVotesAndAnswers: ownVotesAndAnswers,
21302228
);
21312229

@@ -2160,8 +2258,8 @@ class ChannelClientState {
21602258

21612259
final oldPoll = pollMessage.poll;
21622260

2163-
final answers = <String, PollVote>{
2164-
for (final ans in oldPoll?.answers ?? []) ans.id: ans,
2261+
final latestAnswers = <String, PollVote>{
2262+
for (final ans in oldPoll?.latestAnswers ?? []) ans.id: ans,
21652263
eventPollVote.id!: eventPollVote,
21662264
};
21672265

@@ -2173,7 +2271,7 @@ class ChannelClientState {
21732271
};
21742272

21752273
final poll = eventPoll.copyWith(
2176-
answers: [...answers.values],
2274+
latestAnswers: [...latestAnswers.values],
21772275
ownVotesAndAnswers: [...ownVotesAndAnswers.values],
21782276
);
21792277

@@ -2192,7 +2290,7 @@ class ChannelClientState {
21922290

21932291
final oldPoll = pollMessage.poll;
21942292

2195-
final answers = oldPoll?.answers ?? eventPoll.answers;
2293+
final latestAnswers = oldPoll?.latestAnswers ?? eventPoll.latestAnswers;
21962294
final currentUserId = _channel.client.state.currentUser?.id;
21972295
final ownVotesAndAnswers = <String, PollVote>{
21982296
for (final vote in oldPoll?.ownVotesAndAnswers ?? []) vote.id: vote,
@@ -2201,7 +2299,7 @@ class ChannelClientState {
22012299
};
22022300

22032301
final poll = eventPoll.copyWith(
2204-
answers: answers,
2302+
latestAnswers: latestAnswers,
22052303
ownVotesAndAnswers: [...ownVotesAndAnswers.values],
22062304
);
22072305

@@ -2220,16 +2318,16 @@ class ChannelClientState {
22202318

22212319
final oldPoll = pollMessage.poll;
22222320

2223-
final answers = <String, PollVote>{
2224-
for (final ans in oldPoll?.answers ?? []) ans.id: ans,
2321+
final latestAnswers = <String, PollVote>{
2322+
for (final ans in oldPoll?.latestAnswers ?? []) ans.id: ans,
22252323
}..remove(eventPollVote.id);
22262324

22272325
final ownVotesAndAnswers = <String, PollVote>{
22282326
for (final vote in oldPoll?.ownVotesAndAnswers ?? []) vote.id: vote,
22292327
}..remove(eventPollVote.id);
22302328

22312329
final poll = eventPoll.copyWith(
2232-
answers: [...answers.values],
2330+
latestAnswers: [...latestAnswers.values],
22332331
ownVotesAndAnswers: [...ownVotesAndAnswers.values],
22342332
);
22352333

@@ -2248,13 +2346,13 @@ class ChannelClientState {
22482346

22492347
final oldPoll = pollMessage.poll;
22502348

2251-
final answers = oldPoll?.answers ?? eventPoll.answers;
2349+
final latestAnswers = oldPoll?.latestAnswers ?? eventPoll.latestAnswers;
22522350
final ownVotesAndAnswers = <String, PollVote>{
22532351
for (final vote in oldPoll?.ownVotesAndAnswers ?? []) vote.id: vote,
22542352
}..remove(eventPollVote.id);
22552353

22562354
final poll = eventPoll.copyWith(
2257-
answers: answers,
2355+
latestAnswers: latestAnswers,
22582356
ownVotesAndAnswers: [...ownVotesAndAnswers.values],
22592357
);
22602358

@@ -2273,7 +2371,7 @@ class ChannelClientState {
22732371

22742372
final oldPoll = pollMessage.poll;
22752373

2276-
final answers = oldPoll?.answers ?? eventPoll.answers;
2374+
final latestAnswers = oldPoll?.latestAnswers ?? eventPoll.latestAnswers;
22772375
final currentUserId = _channel.client.state.currentUser?.id;
22782376
final ownVotesAndAnswers = <String, PollVote>{
22792377
for (final vote in oldPoll?.ownVotesAndAnswers ?? []) vote.id: vote,
@@ -2282,7 +2380,7 @@ class ChannelClientState {
22822380
};
22832381

22842382
final poll = eventPoll.copyWith(
2285-
answers: answers,
2383+
latestAnswers: latestAnswers,
22862384
ownVotesAndAnswers: [...ownVotesAndAnswers.values],
22872385
);
22882386

@@ -2338,6 +2436,8 @@ class ChannelClientState {
23382436
threads[event.message?.parentId]
23392437
?.firstWhereOrNull((e) => e.id == event.message?.id);
23402438
final message = event.message!.copyWith(
2439+
poll: oldMessage?.poll,
2440+
pollId: oldMessage?.pollId,
23412441
ownReactions: oldMessage?.ownReactions,
23422442
);
23432443
updateMessage(message);

packages/stream_chat/lib/src/core/models/poll.dart

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,15 @@ class Poll extends Equatable {
4141
this.enforceUniqueVote = true,
4242
this.maxVotesAllowed,
4343
this.allowAnswers = false,
44-
this.answers = const [],
44+
this.latestAnswers = const [],
4545
this.answersCount = 0,
4646
this.allowUserSuggestedOptions = false,
4747
this.isClosed = false,
4848
DateTime? createdAt,
4949
DateTime? updatedAt,
5050
this.voteCountsByOption = const {},
5151
this.voteCount = 0,
52-
this.votesByOption = const {},
52+
this.latestVotesByOption = const {},
5353
this.createdById,
5454
this.createdBy,
5555
this.ownVotesAndAnswers = const [],
@@ -109,20 +109,18 @@ class Poll extends Equatable {
109109
final Map<String, int> voteCountsByOption;
110110

111111
/// Map of latest votes by option.
112-
@JsonKey(name: 'latest_votes_by_option', includeToJson: false)
113-
final Map<String, List<PollVote>> votesByOption;
112+
@JsonKey(includeToJson: false)
113+
final Map<String, List<PollVote>> latestVotesByOption;
114114

115115
/// List of votes received by the poll.
116116
///
117117
/// Note: This does not include the answers provided by the users,
118-
/// see [answers] for that.
119-
List<PollVote> get votes => [
120-
...votesByOption.values.flattened.where((it) => !it.isAnswer),
121-
];
118+
/// see [latestAnswers] for that.
119+
late final latestVotes = [...latestVotesByOption.values.flattened];
122120

123121
/// List of latest answers received by the poll.
124-
@JsonKey(name: 'latest_answers', includeToJson: false)
125-
final List<PollVote> answers;
122+
@JsonKey(includeToJson: false)
123+
final List<PollVote> latestAnswers;
126124

127125
/// List of votes casted by the current user.
128126
///
@@ -134,6 +132,18 @@ class Poll extends Equatable {
134132
@JsonKey(includeToJson: false)
135133
final int voteCount;
136134

135+
/// List of votes casted by the current user.
136+
///
137+
/// Note: This does not include the answers provided by the user,
138+
/// see [ownAnswers] for that.
139+
late final ownVotes = [...ownVotesAndAnswers.where((it) => !it.isAnswer)];
140+
141+
/// List of answers provided by the current user.
142+
///
143+
/// Note: This does not include the votes casted by the user,
144+
/// see [ownVotes] for that.
145+
late final ownAnswers = [...ownVotesAndAnswers.where((it) => it.isAnswer)];
146+
137147
/// The id of the user who created the poll.
138148
@JsonKey(includeToJson: false)
139149
final String? createdById;
@@ -173,8 +183,8 @@ class Poll extends Equatable {
173183
List<PollVote>? ownVotesAndAnswers,
174184
int? voteCount,
175185
int? answersCount,
176-
Map<String, List<PollVote>>? votesByOption,
177-
List<PollVote>? answers,
186+
Map<String, List<PollVote>>? latestVotesByOption,
187+
List<PollVote>? latestAnswers,
178188
String? createdById,
179189
User? createdBy,
180190
DateTime? createdAt,
@@ -199,8 +209,8 @@ class Poll extends Equatable {
199209
ownVotesAndAnswers: ownVotesAndAnswers ?? this.ownVotesAndAnswers,
200210
voteCount: voteCount ?? this.voteCount,
201211
answersCount: answersCount ?? this.answersCount,
202-
votesByOption: votesByOption ?? this.votesByOption,
203-
answers: answers ?? this.answers,
212+
latestVotesByOption: latestVotesByOption ?? this.latestVotesByOption,
213+
latestAnswers: latestAnswers ?? this.latestAnswers,
204214
createdById: createdById ?? this.createdById,
205215
createdBy: createdBy ?? this.createdBy,
206216
createdAt: createdAt ?? this.createdAt,
@@ -251,8 +261,8 @@ class Poll extends Equatable {
251261
ownVotesAndAnswers,
252262
voteCount,
253263
answersCount,
254-
votesByOption,
255-
answers,
264+
latestVotesByOption,
265+
latestAnswers,
256266
createdById,
257267
createdBy,
258268
createdAt,

0 commit comments

Comments
 (0)