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

feat(llc): add support for polls api #2040

Draft
wants to merge 10 commits into
base: master
Choose a base branch
from
213 changes: 211 additions & 2 deletions packages/stream_chat/lib/src/client/channel.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import 'dart:async';
import 'dart:math';

import 'package:collection/collection.dart'
show IterableExtension, ListEquality;
import 'package:collection/collection.dart';
import 'package:rxdart/rxdart.dart';
import 'package:stream_chat/src/client/retry_queue.dart';
import 'package:stream_chat/src/core/util/utils.dart';
Expand Down Expand Up @@ -1790,6 +1789,24 @@

_listenReactionDeleted();

/* Start of poll events */

_listenPollUpdated();

_listenPollClosed();

_listenPollAnswerCasted();

_listenPollVoteCasted();

_listenPollVoteChanged();

_listenPollAnswerRemoved();

_listenPollVoteRemoved();

/* End of poll events */

_listenReadEvents();

_listenUnreadEvents();
Expand Down Expand Up @@ -2042,6 +2059,198 @@
_retryQueue.add(failedMessages);
}

Message? _findPollMessage(String pollId) {
final message = messages.firstWhereOrNull((it) => it.pollId == pollId);

Check warning on line 2063 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

packages/stream_chat/lib/src/client/channel.dart#L2062-L2063

Added lines #L2062 - L2063 were not covered by tests
if (message != null) return message;

final threadMessage = threads.values.flattened.firstWhereOrNull((it) {
return it.pollId == pollId;

Check warning on line 2067 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

packages/stream_chat/lib/src/client/channel.dart#L2066-L2067

Added lines #L2066 - L2067 were not covered by tests
});

return threadMessage;
}

void _listenPollUpdated() {
_subscriptions.add(_channel.on(EventType.pollUpdated).listen((event) {
final eventPoll = event.poll;

Check warning on line 2075 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

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

Added line #L2075 was not covered by tests
if (eventPoll == null) return;

final pollMessage = _findPollMessage(eventPoll.id);

Check warning on line 2078 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

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

Added line #L2078 was not covered by tests
if (pollMessage == null) return;

final oldPoll = pollMessage.poll;

Check warning on line 2081 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

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

Added line #L2081 was not covered by tests

final answers = oldPoll?.answers ?? eventPoll.answers;

Check warning on line 2083 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

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

Added line #L2083 was not covered by tests
final ownVotesAndAnswers =
oldPoll?.ownVotesAndAnswers ?? eventPoll.ownVotesAndAnswers;

Check warning on line 2085 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

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

Added line #L2085 was not covered by tests

final poll = eventPoll.copyWith(

Check warning on line 2087 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

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

Added line #L2087 was not covered by tests
answers: answers,
ownVotesAndAnswers: ownVotesAndAnswers,
);

final message = pollMessage.copyWith(poll: poll);
updateMessage(message);

Check warning on line 2093 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

packages/stream_chat/lib/src/client/channel.dart#L2092-L2093

Added lines #L2092 - L2093 were not covered by tests
}));
}

void _listenPollClosed() {
_subscriptions.add(_channel.on(EventType.pollClosed).listen((event) {
final eventPoll = event.poll;

Check warning on line 2099 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

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

Added line #L2099 was not covered by tests
if (eventPoll == null) return;

final pollMessage = _findPollMessage(eventPoll.id);

Check warning on line 2102 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

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

Added line #L2102 was not covered by tests
if (pollMessage == null) return;

final oldPoll = pollMessage.poll;
final poll = oldPoll?.copyWith(isClosed: true) ?? eventPoll;

Check warning on line 2106 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

packages/stream_chat/lib/src/client/channel.dart#L2105-L2106

Added lines #L2105 - L2106 were not covered by tests

final message = pollMessage.copyWith(poll: poll);
updateMessage(message);

Check warning on line 2109 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

packages/stream_chat/lib/src/client/channel.dart#L2108-L2109

Added lines #L2108 - L2109 were not covered by tests
}));
}

void _listenPollAnswerCasted() {
_subscriptions.add(_channel.on(EventType.pollAnswerCasted).listen((event) {
final (eventPoll, eventPollVote) = (event.poll, event.pollVote);

Check warning on line 2115 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

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

Added line #L2115 was not covered by tests
if (eventPoll == null || eventPollVote == null) return;

final pollMessage = _findPollMessage(eventPoll.id);

Check warning on line 2118 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

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

Added line #L2118 was not covered by tests
if (pollMessage == null) return;

final oldPoll = pollMessage.poll;

Check warning on line 2121 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

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

Added line #L2121 was not covered by tests

final answers = <String, PollVote>{
for (final ans in oldPoll?.answers ?? []) ans.id: ans,
eventPollVote.id!: eventPollVote,

Check warning on line 2125 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

packages/stream_chat/lib/src/client/channel.dart#L2123-L2125

Added lines #L2123 - L2125 were not covered by tests
};

final currentUserId = _channel.client.state.currentUser?.id;
final ownVotesAndAnswers = <String, PollVote>{
for (final vote in oldPoll?.ownVotesAndAnswers ?? []) vote.id: vote,
if (eventPollVote.userId == currentUserId)
eventPollVote.id!: eventPollVote,

Check warning on line 2132 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

packages/stream_chat/lib/src/client/channel.dart#L2128-L2132

Added lines #L2128 - L2132 were not covered by tests
};

final poll = eventPoll.copyWith(
answers: [...answers.values],
ownVotesAndAnswers: [...ownVotesAndAnswers.values],

Check warning on line 2137 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

packages/stream_chat/lib/src/client/channel.dart#L2135-L2137

Added lines #L2135 - L2137 were not covered by tests
);

final message = pollMessage.copyWith(poll: poll);
updateMessage(message);

Check warning on line 2141 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

packages/stream_chat/lib/src/client/channel.dart#L2140-L2141

Added lines #L2140 - L2141 were not covered by tests
}));
}

void _listenPollVoteCasted() {
_subscriptions.add(_channel.on(EventType.pollVoteCasted).listen((event) {
final (eventPoll, eventPollVote) = (event.poll, event.pollVote);

Check warning on line 2147 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

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

Added line #L2147 was not covered by tests
if (eventPoll == null || eventPollVote == null) return;

final pollMessage = _findPollMessage(eventPoll.id);

Check warning on line 2150 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

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

Added line #L2150 was not covered by tests
if (pollMessage == null) return;

final oldPoll = pollMessage.poll;

Check warning on line 2153 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

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

Added line #L2153 was not covered by tests

final answers = oldPoll?.answers ?? eventPoll.answers;
final currentUserId = _channel.client.state.currentUser?.id;
final ownVotesAndAnswers = <String, PollVote>{
for (final vote in oldPoll?.ownVotesAndAnswers ?? []) vote.id: vote,
if (eventPollVote.userId == currentUserId)
eventPollVote.id!: eventPollVote,

Check warning on line 2160 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

packages/stream_chat/lib/src/client/channel.dart#L2155-L2160

Added lines #L2155 - L2160 were not covered by tests
};

final poll = eventPoll.copyWith(

Check warning on line 2163 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

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

Added line #L2163 was not covered by tests
answers: answers,
ownVotesAndAnswers: [...ownVotesAndAnswers.values],

Check warning on line 2165 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

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

Added line #L2165 was not covered by tests
);

final message = pollMessage.copyWith(poll: poll);
updateMessage(message);

Check warning on line 2169 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

packages/stream_chat/lib/src/client/channel.dart#L2168-L2169

Added lines #L2168 - L2169 were not covered by tests
}));
}

void _listenPollAnswerRemoved() {
_subscriptions.add(_channel.on(EventType.pollAnswerRemoved).listen((event) {
final (eventPoll, eventPollVote) = (event.poll, event.pollVote);

Check warning on line 2175 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

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

Added line #L2175 was not covered by tests
if (eventPoll == null || eventPollVote == null) return;

final pollMessage = _findPollMessage(eventPoll.id);

Check warning on line 2178 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

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

Added line #L2178 was not covered by tests
if (pollMessage == null) return;

final oldPoll = pollMessage.poll;

Check warning on line 2181 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

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

Added line #L2181 was not covered by tests

final answers = <String, PollVote>{
for (final ans in oldPoll?.answers ?? []) ans.id: ans,
}..remove(eventPollVote.id);

Check warning on line 2185 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

packages/stream_chat/lib/src/client/channel.dart#L2183-L2185

Added lines #L2183 - L2185 were not covered by tests

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

Check warning on line 2189 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

packages/stream_chat/lib/src/client/channel.dart#L2187-L2189

Added lines #L2187 - L2189 were not covered by tests

final poll = eventPoll.copyWith(
answers: [...answers.values],
ownVotesAndAnswers: [...ownVotesAndAnswers.values],

Check warning on line 2193 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

packages/stream_chat/lib/src/client/channel.dart#L2191-L2193

Added lines #L2191 - L2193 were not covered by tests
);

final message = pollMessage.copyWith(poll: poll);
updateMessage(message);

Check warning on line 2197 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

packages/stream_chat/lib/src/client/channel.dart#L2196-L2197

Added lines #L2196 - L2197 were not covered by tests
}));
}

void _listenPollVoteRemoved() {
_subscriptions.add(_channel.on(EventType.pollVoteRemoved).listen((event) {
final (eventPoll, eventPollVote) = (event.poll, event.pollVote);

Check warning on line 2203 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

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

Added line #L2203 was not covered by tests
if (eventPoll == null || eventPollVote == null) return;

final pollMessage = _findPollMessage(eventPoll.id);

Check warning on line 2206 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

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

Added line #L2206 was not covered by tests
if (pollMessage == null) return;

final oldPoll = pollMessage.poll;

Check warning on line 2209 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

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

Added line #L2209 was not covered by tests

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

Check warning on line 2214 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

packages/stream_chat/lib/src/client/channel.dart#L2211-L2214

Added lines #L2211 - L2214 were not covered by tests

final poll = eventPoll.copyWith(

Check warning on line 2216 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

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

Added line #L2216 was not covered by tests
answers: answers,
ownVotesAndAnswers: [...ownVotesAndAnswers.values],

Check warning on line 2218 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

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

Added line #L2218 was not covered by tests
);

final message = pollMessage.copyWith(poll: poll);
updateMessage(message);

Check warning on line 2222 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

packages/stream_chat/lib/src/client/channel.dart#L2221-L2222

Added lines #L2221 - L2222 were not covered by tests
}));
}

void _listenPollVoteChanged() {
_subscriptions.add(_channel.on(EventType.pollVoteChanged).listen((event) {
final (eventPoll, eventPollVote) = (event.poll, event.pollVote);

Check warning on line 2228 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

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

Added line #L2228 was not covered by tests
if (eventPoll == null || eventPollVote == null) return;

final pollMessage = _findPollMessage(eventPoll.id);

Check warning on line 2231 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

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

Added line #L2231 was not covered by tests
if (pollMessage == null) return;

final oldPoll = pollMessage.poll;

Check warning on line 2234 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

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

Added line #L2234 was not covered by tests

final answers = oldPoll?.answers ?? eventPoll.answers;
final currentUserId = _channel.client.state.currentUser?.id;
final ownVotesAndAnswers = <String, PollVote>{
for (final vote in oldPoll?.ownVotesAndAnswers ?? []) vote.id: vote,
if (eventPollVote.userId == currentUserId)
eventPollVote.id!: eventPollVote,

Check warning on line 2241 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

packages/stream_chat/lib/src/client/channel.dart#L2236-L2241

Added lines #L2236 - L2241 were not covered by tests
};

final poll = eventPoll.copyWith(

Check warning on line 2244 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

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

Added line #L2244 was not covered by tests
answers: answers,
ownVotesAndAnswers: [...ownVotesAndAnswers.values],

Check warning on line 2246 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

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

Added line #L2246 was not covered by tests
);

final message = pollMessage.copyWith(poll: poll);
updateMessage(message);

Check warning on line 2250 in packages/stream_chat/lib/src/client/channel.dart

View check run for this annotation

Codecov / codecov/patch

packages/stream_chat/lib/src/client/channel.dart#L2249-L2250

Added lines #L2249 - L2250 were not covered by tests
}));
}

void _listenReactionDeleted() {
_subscriptions.add(_channel.on(EventType.reactionDeleted).listen((event) {
final oldMessage =
Expand Down
135 changes: 134 additions & 1 deletion packages/stream_chat/lib/src/client/client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
import 'package:stream_chat/src/core/models/member.dart';
import 'package:stream_chat/src/core/models/message.dart';
import 'package:stream_chat/src/core/models/own_user.dart';
import 'package:stream_chat/src/core/models/poll.dart';
import 'package:stream_chat/src/core/models/poll_option.dart';
import 'package:stream_chat/src/core/models/poll_vote.dart';
import 'package:stream_chat/src/core/models/user.dart';
import 'package:stream_chat/src/core/platform_detector/platform_detector.dart';
import 'package:stream_chat/src/core/util/utils.dart';
Expand Down Expand Up @@ -197,7 +200,17 @@

/// Stream of [Event] coming from [_ws] connection
/// Listen to this or use the [on] method to filter specific event types
Stream<Event> get eventStream => _eventController.stream;
Stream<Event> get eventStream => _eventController.stream.map(
// If the poll vote is an answer, we should emit a different event
// to make it easier to handle in the state.
(event) => switch ((event.type, event.pollVote?.isAnswer == true)) {
(EventType.pollVoteCasted || EventType.pollVoteChanged, true) =>
event.copyWith(type: EventType.pollAnswerCasted),

Check warning on line 208 in packages/stream_chat/lib/src/client/client.dart

View check run for this annotation

Codecov / codecov/patch

packages/stream_chat/lib/src/client/client.dart#L208

Added line #L208 was not covered by tests
(EventType.pollVoteRemoved, true) =>
event.copyWith(type: EventType.pollAnswerRemoved),

Check warning on line 210 in packages/stream_chat/lib/src/client/client.dart

View check run for this annotation

Codecov / codecov/patch

packages/stream_chat/lib/src/client/client.dart#L210

Added line #L210 was not covered by tests
_ => event,
},
);

final _wsConnectionStatusController =
BehaviorSubject.seeded(ConnectionStatus.disconnected);
Expand Down Expand Up @@ -1200,6 +1213,126 @@
messageId,
);

/// Creates a new Poll
Future<CreatePollResponse> createPoll(Poll poll) =>
_chatApi.polls.createPoll(poll);

/// Retrieves a Poll by [pollId]
Future<GetPollResponse> getPoll(String pollId) =>
_chatApi.polls.getPoll(pollId);

/// Updates a Poll
Future<UpdatePollResponse> updatePoll(Poll poll) =>
_chatApi.polls.updatePoll(poll);

/// Partially updates a Poll by [pollId].
///
/// Use [set] to define values to be set.
/// Use [unset] to define values to be unset.
Future<UpdatePollResponse> partialUpdatePoll(
String pollId, {
Map<String, Object?>? set,
List<String>? unset,
}) =>
_chatApi.polls.partialUpdatePoll(
pollId,
set: set,
unset: unset,
);

/// Deletes the Poll by [pollId].
Future<EmptyResponse> deletePoll(String pollId) =>
_chatApi.polls.deletePoll(pollId);

/// Marks the Poll [pollId] as closed.
Future<UpdatePollResponse> closePoll(String pollId) =>
partialUpdatePoll(pollId, set: {
'is_closed': true,
});

/// Creates a new Poll Option for the Poll [pollId].
Future<CreatePollOptionResponse> createPollOption(
String pollId,
PollOption option,
) =>
_chatApi.polls.createPollOption(pollId, option);

/// Retrieves a Poll Option by [optionId] for the Poll [pollId].
Future<GetPollOptionResponse> getPollOption(
String pollId,
String optionId,
) =>
_chatApi.polls.getPollOption(pollId, optionId);

/// Updates a Poll Option for the Poll [pollId].
Future<UpdatePollOptionResponse> updatePollOption(
String pollId,
PollOption option,
) =>
_chatApi.polls.updatePollOption(pollId, option);

/// Deletes a Poll Option by [optionId] for the Poll [pollId].
Future<EmptyResponse> deletePollOption(
String pollId,
String optionId,
) =>
_chatApi.polls.deletePollOption(pollId, optionId);

/// Cast a [vote] for the Poll [pollId].
Future<CastPollVoteResponse> castPollVote(
String messageId,
String pollId, {
required String optionId,
}) {
final vote = PollVote(optionId: optionId);
return _chatApi.polls.castPollVote(messageId, pollId, vote);
}

/// Adds a answer with [answerText] for the Poll [pollId].
Future<CastPollVoteResponse> addPollAnswer(
String messageId,
String pollId, {
required String answerText,
}) {
final vote = PollVote(answerText: answerText);
return _chatApi.polls.castPollVote(messageId, pollId, vote);
}

/// Removes a vote by [voteId] for the Poll [pollId].
Future<RemovePollVoteResponse> removePollVote(
String messageId,
String pollId,
String voteId,
) =>
_chatApi.polls.removePollVote(messageId, pollId, voteId);

/// Queries Polls with the given [filter] and [sort] options.
Future<QueryPollsResponse> queryPolls({
Filter? filter,
List<SortOption>? sort,
PaginationParams pagination = const PaginationParams(),
}) =>
_chatApi.polls.queryPolls(
filter: filter,
sort: sort,
pagination: pagination,
);

/// Queries Poll Votes for the Poll [pollId] with the given [filter]
/// and [sort] options.
Future<QueryPollVotesResponse> queryPollVotes(
String pollId, {
Filter? filter,
List<SortOption>? sort,
PaginationParams pagination = const PaginationParams(),
}) =>
_chatApi.polls.queryPollVotes(
pollId,
filter: filter,
sort: sort,
pagination: pagination,
);

/// Update or Create the given user object.
Future<UpdateUsersResponse> updateUser(User user) => updateUsers([user]);

Expand Down
Loading
Loading