Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
53e93b0
feat: handle missing events in ActivityCommentList
xsahil03x Dec 15, 2025
8c46fc1
feat: handle missing events in ActivityReactionList
xsahil03x Dec 15, 2025
4f4123d
feat: handle missing events in BookmarkFolderList
xsahil03x Dec 15, 2025
69401e8
feat: handle missing events in BookmarkList
xsahil03x Dec 15, 2025
4803db4
feat: handle missing events in CommentReactionList
xsahil03x Dec 15, 2025
43f0308
feat: handle missing events in CommentReplyList
xsahil03x Dec 15, 2025
322093d
feat: handle missing events in FeedList
xsahil03x Dec 15, 2025
0eadfdb
feat: handle missing events in FollowList
xsahil03x Dec 15, 2025
318a364
feat: handle missing events in MemberList
xsahil03x Dec 15, 2025
6c677b2
feat: handle missing events in PollVoteList
xsahil03x Dec 15, 2025
03b37a0
feat: handle missing events in PollList
xsahil03x Dec 15, 2025
610c2a4
feat: handle missing events in CommentList
xsahil03x Dec 15, 2025
ea3838d
chore: ignore reaction deleted events for other comments
xsahil03x Dec 15, 2025
3ddf9a2
feat: handle missing events in Activity
xsahil03x Dec 16, 2025
3753635
feat: add update handler for existing polls in PollList
xsahil03x Dec 16, 2025
8d7cad7
feat: add missing event handlers in ActivityList
xsahil03x Dec 16, 2025
dcde690
feat: update poll closed event handler to use poll ID
xsahil03x Dec 16, 2025
18146de
fix: fix generic type parameter in MarkActivityDataHandler
xsahil03x Dec 16, 2025
a130241
feat: handle missing events in Feed
xsahil03x Dec 16, 2025
e5cd7df
chore: review changes
xsahil03x Dec 16, 2025
483fd9c
chore: review changes
xsahil03x Dec 17, 2025
f393f4f
test: add tests for ActivityCommentList
xsahil03x Dec 17, 2025
33b8f7e
test: add tests for ActivityReactionList
xsahil03x Dec 17, 2025
8c369bc
test: add tests for BookmarkFolderList
xsahil03x Dec 17, 2025
91e9db5
chore: format
xsahil03x Dec 17, 2025
b026131
test: add tests for BookmarkList
xsahil03x Dec 17, 2025
af0ea13
test: add tests for CommentReactionList
xsahil03x Dec 17, 2025
08438f7
test: add tests for CommentReplyList
xsahil03x Dec 17, 2025
8c7b564
test: add tests for FeedList
xsahil03x Dec 17, 2025
328fee2
chore: run format
xsahil03x Dec 17, 2025
f0b7e41
test: add tests for FollowList
xsahil03x Dec 17, 2025
be8b9c3
test: add tests for MemberList
xsahil03x Dec 17, 2025
e6711c9
test: add tests for PollVoteList
xsahil03x Dec 17, 2025
a27229a
test: add tests for PollList
xsahil03x Dec 17, 2025
abf0252
test: add tests for CommentList
xsahil03x Dec 17, 2025
ea03430
test: add tests for Activity
xsahil03x Dec 18, 2025
3757b1c
test: add teardown for comment list in activity, comment, and member …
xsahil03x Dec 18, 2025
361a3d4
test: add remaining tests for Activity
xsahil03x Dec 18, 2025
0e1cd9e
test: add remaining tests for ActivityList
xsahil03x Dec 18, 2025
65cd533
test: add remaining tests for Feed
xsahil03x Dec 19, 2025
a972e78
test: improve reaction, poll handling in tests
xsahil03x Dec 19, 2025
6dd0a13
test: add tests for ModerationConfigList
xsahil03x Dec 19, 2025
413635b
test: update pagination assertions to use `canLoadMore` in state tests
xsahil03x Dec 19, 2025
f5ae50e
test: add tests for PollDeletedFeedEvent and PollUpdatedFeedEvent
xsahil03x Dec 19, 2025
d30ad57
chore: update stream_core dependency to version 0.3.3
xsahil03x Dec 19, 2025
1b4217b
Merge remote-tracking branch 'origin/main' into feat/add-missing-even…
xsahil03x Dec 19, 2025
2619d48
chore: update CHANGELOG.md
xsahil03x Dec 19, 2025
f53dd04
chore: fix analysis issue
xsahil03x Dec 19, 2025
6869e82
fix: update reaction timestamps to use createdAt
xsahil03x Dec 19, 2025
bdc3b3f
chore: update comment to reflect member list disposal in tests
xsahil03x Dec 19, 2025
65d11f2
chore: update comments to reflect correct disposal of reaction lists …
xsahil03x Dec 19, 2025
4a62569
fix: fix incorrect pagination verifications
xsahil03x Dec 19, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion melos.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ command:
shared_preferences: ^2.5.3
state_notifier: ^1.0.0
stream_feeds: ^0.5.0
stream_core: ^0.3.2
stream_core: ^0.3.3
video_player: ^2.10.0
uuid: ^4.5.1

Expand Down
3 changes: 3 additions & 0 deletions packages/stream_feeds/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## Upcoming
- Added missing state updates for the websocket events.

## 0.5.0
- [BREAKING] Unified `ThreadedCommentData` into `CommentData` to handle both flat and threaded comments.
- [BREAKING] Renamed `ActivitiesFilterField.type` to `ActivitiesFilterField.activityType`.
Expand Down
12 changes: 9 additions & 3 deletions packages/stream_feeds/dart_test.yaml
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
tags:
feed:
feed-list:
activity:
activity-comment-list:
activity-list:
bookmark-list:
activity-reaction-list:
bookmark-folder-list:
bookmark-list:
comment-list:
comment-reaction-list:
comment-reply-list:
feed:
feed-list:
follow-list:
member-list:
moderation-config-list:
poll-list:
poll-vote-list:
2 changes: 2 additions & 0 deletions packages/stream_feeds/lib/src/client/feeds_client_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,7 @@ class StreamFeedsClientImpl implements StreamFeedsClient {
query: query,
commentsRepository: _commentsRepository,
eventsEmitter: events,
currentUserId: user.id,
);
}

Expand Down Expand Up @@ -444,6 +445,7 @@ class StreamFeedsClientImpl implements StreamFeedsClient {
query: query,
pollsRepository: _pollsRepository,
eventsEmitter: events,
currentUserId: user.id,
);
}

Expand Down
85 changes: 85 additions & 0 deletions packages/stream_feeds/lib/src/models/activity_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,22 @@ extension ActivityDataMutations on ActivityData {
);
}

/// Conditionally updates this activity based on a filter.
///
/// If [filter] returns true for this activity, applies the [update] function
/// to produce a new updated activity. Otherwise, returns this activity unchanged.
///
/// This is useful for applying updates only when certain conditions are met.
///
/// Returns a new [ActivityData] instance, either updated or unchanged.
ActivityData updateIf({
required bool Function(ActivityData) filter,
required ActivityData Function(ActivityData) update,
}) {
if (!filter(this)) return this;
return update(this);
}

/// Adds or updates a comment in this activity.
///
/// Updates the comments list by adding or updating [comment]. If the comment already
Expand Down Expand Up @@ -381,6 +397,75 @@ extension ActivityDataMutations on ActivityData {
);
}

/// Adds or updates a reaction in a comment within this activity with unique enforcement.
///
/// Updates the own reactions list of the comment by adding or updating [reaction]. Only adds reactions
/// that belong to [currentUserId]. When unique enforcement is enabled, replaces any
/// existing reaction from the same user.
///
/// Returns a new [ActivityData] instance with the updated comment reactions.
ActivityData upsertUniqueCommentReaction(
CommentData updatedComment,
FeedsReactionData reaction,
String currentUserId,
) {
return upsertCommentReaction(
updatedComment,
reaction,
currentUserId,
enforceUnique: true,
);
}

/// Adds or updates a reaction in a comment within this activity.
///
/// Updates the own reactions list of the comment by adding or updating [reaction]. Only adds reactions
/// that belong to [currentUserId]. When [enforceUnique] is true, replaces any existing
/// reaction from the same user; otherwise, allows multiple reactions from the same user.
///
/// Returns a new [ActivityData] instance with the updated comment reactions.
ActivityData upsertCommentReaction(
CommentData updatedComment,
FeedsReactionData reaction,
String currentUserId, {
bool enforceUnique = false,
}) {
final updatedComments = comments.updateWhere(
(it) => it.id == updatedComment.id,
update: (it) => it.upsertReaction(
updatedComment,
reaction,
currentUserId,
enforceUnique: enforceUnique,
),
);

return copyWith(comments: updatedComments);
}

/// Removes a reaction from a comment within this activity.
///
/// Updates the own reactions list of the comment by removing [reaction]. Only removes reactions
/// that belong to [currentUserId].
///
/// Returns a new [ActivityData] instance with the updated comment reactions.
ActivityData removeCommentReaction(
CommentData updatedComment,
FeedsReactionData reaction,
String currentUserId,
) {
final updatedComments = comments.updateWhere(
(it) => it.id == updatedComment.id,
update: (it) => it.removeReaction(
updatedComment,
reaction,
currentUserId,
),
);

return copyWith(comments: updatedComments);
}

/// Adds or updates a bookmark in this activity.
///
/// Updates the own bookmarks list by adding or updating [bookmark]. Only adds bookmarks
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ class MarkActivityData with _$MarkActivityData {
final List<String>? markWatched;
}

extension MarkActivityDataHandler<R> on MarkActivityData {
R handle({
extension MarkActivityDataHandler on MarkActivityData {
R handle<R>({
R Function()? markAllRead,
R Function()? markAllSeen,
R Function(Set<String> read)? markRead,
Expand Down
99 changes: 76 additions & 23 deletions packages/stream_feeds/lib/src/models/poll_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -176,10 +176,10 @@ extension PollDataMutations on PollData {
/// exists (by ID), it will be updated.
///
/// Returns a new [PollData] instance with the updated options.
PollData addOption(PollOptionData option) {
PollData upsertOption(PollOptionData option) {
final updatedOptions = options.upsert(
option,
key: (it) => it.id == option.id,
key: (it) => it.id,
);

return copyWith(options: updatedOptions);
Expand All @@ -191,44 +191,97 @@ extension PollDataMutations on PollData {
///
/// Returns a new [PollData] instance with the updated options.
PollData removeOption(String optionId) {
final updatedOptions = options.where((it) => it.id != optionId).toList();
final updatedOptions = options.where((it) {
return it.id != optionId;
}).toList();

return copyWith(options: updatedOptions);
}

/// Updates an existing option in this poll.
/// Adds or updates a vote in this poll.
///
/// Updates the options list by replacing the option with the same ID as [option]
/// with the new [option] data.
/// Updates the own votes and answers list by adding or updating [vote]. Only adds votes
/// that belong to [currentUserId].
///
/// Returns a new [PollData] instance with the updated options.
PollData updateOption(PollOptionData option) {
final updatedOptions = options.map((it) {
if (it.id != option.id) return it;
return option;
}).toList();
/// Returns a new [PollData] instance with the updated own votes and answers.
PollData upsertVote(
PollData updatedPoll,
PollVoteData vote,
String currentUserId,
) {
final updatedOwnVotesAndAnswers = ownVotesAndAnswers.let((it) {
if (vote.userId != currentUserId) return it;
return it.upsert(vote, key: (it) => it.id);
});

return copyWith(options: updatedOptions);
return updateWith(
updatedPoll,
ownVotesAndAnswers: updatedOwnVotesAndAnswers,
);
}

/// Casts an answer to this poll.
/// Adds or updates an answer in this poll.
///
/// Updates the latest answers and own votes/answers lists by adding or updating [answer].
/// Only adds answers that belong to [currentUserId] to the own votes/answers list.
/// Updates the own votes and answers list by adding or updating [answer]. Only adds answers
/// that belong to [currentUserId].
///
/// Returns a new [PollData] instance with the updated answers.
PollData castAnswer(PollVoteData answer, String currentUserId) {
final updatedLatestAnswers = latestAnswers.let((it) {
return it.upsert(answer, key: (it) => it.id == answer.id);
/// Returns a new [PollData] instance with the updated own votes and answers.
PollData upsertAnswer(
PollData updatedPoll,
PollVoteData answer,
String currentUserId,
) {
final updatedOwnVotesAndAnswers = ownVotesAndAnswers.let((it) {
if (answer.userId != currentUserId) return it;
return it.upsert(answer, key: (it) => it.id);
});

return updateWith(
updatedPoll,
ownVotesAndAnswers: updatedOwnVotesAndAnswers,
);
}

/// Removes a vote from this poll.
///
/// Updates the own votes and answers list by removing [vote]. Only removes votes
/// that belong to [currentUserId].
///
/// Returns a new [PollData] instance with the updated own votes and answers.
PollData removeVote(
PollData updatedPoll,
PollVoteData vote,
String currentUserId,
) {
final updatedOwnVotesAndAnswers = ownVotesAndAnswers.let((it) {
if (vote.userId != currentUserId) return it;
return it.where((it) => it.id != vote.id).toList();
});

return updateWith(
updatedPoll,
ownVotesAndAnswers: updatedOwnVotesAndAnswers,
);
}

/// Removes an answer from this poll.
///
/// Updates the own votes and answers list by removing [answer]. Only removes answers
/// that belong to [currentUserId].
///
/// Returns a new [PollData] instance with the updated own votes and answers.
PollData removeAnswer(
PollData updatedPoll,
PollVoteData answer,
String currentUserId,
) {
final updatedOwnVotesAndAnswers = ownVotesAndAnswers.let((it) {
if (answer.userId != currentUserId) return it;
return it.upsert(answer, key: (it) => it.id == answer.id);
return it.where((it) => it.id != answer.id).toList();
});

return copyWith(
latestAnswers: updatedLatestAnswers,
return updateWith(
updatedPoll,
ownVotesAndAnswers: updatedOwnVotesAndAnswers,
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class PollsRepository {
const request = api.UpdatePollPartialRequest(
set: {'is_closed': true},
);

final result = await _api.updatePollPartial(
pollId: pollId,
updatePollPartialRequest: request,
Expand Down
3 changes: 1 addition & 2 deletions packages/stream_feeds/lib/src/state/activity.dart
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ class Activity with Disposable {

final initialActivityState = ActivityState(
activity: initialActivityData,
poll: initialActivityData?.poll,
);

_stateNotifier = ActivityStateNotifier(
Expand Down Expand Up @@ -110,7 +109,7 @@ class Activity with Disposable {
final result = await activitiesRepository.getActivity(activityId);

result.onSuccess((activity) {
_stateNotifier.onActivityUpdated(activity);
_stateNotifier.onActivityGet(activity);
if (activity.currentFeed case final feed?) {
capabilitiesRepository.cacheCapabilitiesForFeeds([feed]);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ class ActivityCommentListStateNotifier
);
}

/// Handles the deletion of the activity.
void onActivityDeleted() {
state = state.copyWith(
comments: [], // Clear all comments when the activity is deleted
pagination: null,
);
}

/// Handles the addition of a new comment.
void onCommentAdded(CommentData comment) {
final parentId = comment.parentId;
Expand Down
Loading