Skip to content

Commit

Permalink
feat: add poll event handlers
Browse files Browse the repository at this point in the history
  • Loading branch information
xsahil03x committed Dec 10, 2024
1 parent 687d2c5 commit 8d92ca5
Show file tree
Hide file tree
Showing 8 changed files with 399 additions and 51 deletions.
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 @@ -1800,6 +1799,24 @@ class ChannelClientState {

_listenReactionDeleted();

/* Start of poll events */

_listenPollUpdated();

_listenPollClosed();

_listenPollAnswerCasted();

_listenPollVoteCasted();

_listenPollVoteChanged();

_listenPollAnswerRemoved();

_listenPollVoteRemoved();

/* End of poll events */

_listenReadEvents();

_listenUnreadEvents();
Expand Down Expand Up @@ -2052,6 +2069,198 @@ class ChannelClientState {
_retryQueue.add(failedMessages);
}

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

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

return threadMessage;
}

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

final pollMessage = _findPollMessage(eventPoll.id);
if (pollMessage == null) return;

final oldPoll = pollMessage.poll;

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

final poll = eventPoll.copyWith(
answers: answers,
ownVotesAndAnswers: ownVotesAndAnswers,
);

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

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

final pollMessage = _findPollMessage(eventPoll.id);
if (pollMessage == null) return;

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

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

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

final pollMessage = _findPollMessage(eventPoll.id);
if (pollMessage == null) return;

final oldPoll = pollMessage.poll;

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

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,
};

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

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

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

final pollMessage = _findPollMessage(eventPoll.id);
if (pollMessage == null) return;

final oldPoll = pollMessage.poll;

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,
};

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

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

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

final pollMessage = _findPollMessage(eventPoll.id);
if (pollMessage == null) return;

final oldPoll = pollMessage.poll;

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

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

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

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

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

final pollMessage = _findPollMessage(eventPoll.id);
if (pollMessage == null) return;

final oldPoll = pollMessage.poll;

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

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

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

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

final pollMessage = _findPollMessage(eventPoll.id);
if (pollMessage == null) return;

final oldPoll = pollMessage.poll;

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,
};

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

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

void _listenReactionDeleted() {
_subscriptions.add(_channel.on(EventType.reactionDeleted).listen((event) {
final oldMessage =
Expand Down
12 changes: 11 additions & 1 deletion packages/stream_chat/lib/src/client/client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,17 @@ class StreamChatClient {

/// 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),
(EventType.pollVoteRemoved, true) =>
event.copyWith(type: EventType.pollAnswerRemoved),
_ => event,
},
);

final _wsConnectionStatusController =
BehaviorSubject.seeded(ConnectionStatus.disconnected);
Expand Down
4 changes: 4 additions & 0 deletions packages/stream_chat/lib/src/core/models/event.dart
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,8 @@ class Event {
OwnUser? me,
User? user,
Message? message,
Poll? poll,
PollVote? pollVote,
EventChannel? channel,
Member? member,
Reaction? reaction,
Expand All @@ -201,6 +203,8 @@ class Event {
me: me ?? this.me,
user: user ?? this.user,
message: message ?? this.message,
poll: poll ?? this.poll,
pollVote: pollVote ?? this.pollVote,
totalUnreadCount: totalUnreadCount ?? this.totalUnreadCount,
unreadChannels: unreadChannels ?? this.unreadChannels,
reaction: reaction ?? this.reaction,
Expand Down
Loading

0 comments on commit 8d92ca5

Please sign in to comment.