Skip to content

Commit 784ff3c

Browse files
authored
Migrate feed members batch update to a state event (#136)
1 parent bd0d86b commit 784ff3c

File tree

6 files changed

+86
-7
lines changed

6 files changed

+86
-7
lines changed

stream-feeds-android-client/src/main/kotlin/io/getstream/feeds/android/client/internal/state/FeedImpl.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,11 @@ internal class FeedImpl(
357357
): Result<ModelUpdates<FeedMemberData>> {
358358
return feedsRepository
359359
.updateFeedMembers(feedGroupId = group, feedId = id, request = request)
360-
.onSuccess { updates -> memberList.mutableState.onMembersUpdated(updates) }
360+
.onSuccess { updates ->
361+
subscriptionManager.onEvent(
362+
StateUpdateEvent.FeedMemberBatchUpdate(fid.rawValue, updates)
363+
)
364+
}
361365
}
362366

363367
override suspend fun acceptFeedMember(): Result<FeedMemberData> {

stream-feeds-android-client/src/main/kotlin/io/getstream/feeds/android/client/internal/state/MemberListStateImpl.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,12 +82,13 @@ internal class MemberListStateImpl(override val query: MembersQuery) : MemberLis
8282
override fun onMembersUpdated(updates: ModelUpdates<FeedMemberData>) {
8383
// Create a map for efficient lookups of updated members
8484
val updatesMap = updates.updated.associateBy(FeedMemberData::id)
85+
val removedIdSet = updates.removedIds.toSet()
8586

8687
_members.update { current ->
8788
current
8889
.mapNotNullTo(mutableListOf()) { member ->
8990
// Apply updates and filter out removed members in a single pass
90-
if (member.id in updates.removedIds) {
91+
if (member.id in removedIdSet) {
9192
null
9293
} else {
9394
updatesMap[member.id] ?: member

stream-feeds-android-client/src/main/kotlin/io/getstream/feeds/android/client/internal/state/event/StateUpdateEvent.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import io.getstream.feeds.android.client.api.model.FeedData
2525
import io.getstream.feeds.android.client.api.model.FeedMemberData
2626
import io.getstream.feeds.android.client.api.model.FeedsReactionData
2727
import io.getstream.feeds.android.client.api.model.FollowData
28+
import io.getstream.feeds.android.client.api.model.ModelUpdates
2829
import io.getstream.feeds.android.client.api.model.PollData
2930
import io.getstream.feeds.android.client.api.model.PollVoteData
3031
import io.getstream.feeds.android.client.api.model.toModel
@@ -144,6 +145,9 @@ internal sealed interface StateUpdateEvent {
144145

145146
data class FeedMemberUpdated(val fid: String, val member: FeedMemberData) : StateUpdateEvent
146147

148+
data class FeedMemberBatchUpdate(val fid: String, val updates: ModelUpdates<FeedMemberData>) :
149+
StateUpdateEvent
150+
147151
data class FollowAdded(val follow: FollowData) : StateUpdateEvent
148152

149153
data class FollowDeleted(val follow: FollowData) : StateUpdateEvent

stream-feeds-android-client/src/main/kotlin/io/getstream/feeds/android/client/internal/state/event/handler/MemberListEventHandler.kt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
package io.getstream.feeds.android.client.internal.state.event.handler
1717

1818
import io.getstream.feeds.android.client.api.model.FeedId
19+
import io.getstream.feeds.android.client.api.model.FeedMemberData
20+
import io.getstream.feeds.android.client.api.model.ModelUpdates
1921
import io.getstream.feeds.android.client.api.state.query.MembersFilter
2022
import io.getstream.feeds.android.client.internal.state.MemberListStateUpdates
2123
import io.getstream.feeds.android.client.internal.state.event.StateUpdateEvent
@@ -52,6 +54,20 @@ internal class MemberListEventHandler(
5254
}
5355
}
5456

57+
is StateUpdateEvent.FeedMemberBatchUpdate -> {
58+
if (event.fid != fid.rawValue) return
59+
60+
val added = event.updates.added.filter { it matches filter }
61+
// We remove elements that used to match the filter but no longer do
62+
val (updated, removed) = event.updates.updated.partition { it matches filter }
63+
val removedIds = event.updates.removedIds.toMutableList()
64+
removed.mapTo(removedIds, FeedMemberData::id)
65+
66+
state.onMembersUpdated(
67+
ModelUpdates(added = added, updated = updated, removedIds = removedIds)
68+
)
69+
}
70+
5571
else -> {}
5672
}
5773
}

stream-feeds-android-client/src/test/kotlin/io/getstream/feeds/android/client/internal/state/FeedImplTest.kt

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -567,12 +567,17 @@ internal class FeedImplTest {
567567
val request =
568568
UpdateFeedMembersRequest(
569569
operation = UpdateFeedMembersRequest.Operation.Upsert,
570-
members = listOf(FeedMemberRequest(userId = "user1", role = "member")),
570+
members =
571+
listOf(
572+
FeedMemberRequest(userId = "user1", role = "member"),
573+
FeedMemberRequest(userId = "user2", role = "admin"),
574+
FeedMemberRequest(userId = "user3"),
575+
),
571576
)
572577
val memberUpdates =
573578
ModelUpdates(
574-
added = emptyList(),
575-
removedIds = emptyList(),
579+
added = listOf(feedMemberData("user2", role = "admin")),
580+
removedIds = listOf("user3"),
576581
updated = listOf(feedMemberData("user1", role = "member")),
577582
)
578583

@@ -584,8 +589,13 @@ internal class FeedImplTest {
584589

585590
val result = feed.updateFeedMembers(request)
586591

592+
val expected =
593+
listOf(
594+
feedMemberData(userId = "user1", role = "member"),
595+
feedMemberData(userId = "user2", role = "admin"),
596+
)
587597
assertEquals(memberUpdates, result.getOrNull())
588-
assertEquals(memberUpdates.updated, feed.state.members.value)
598+
assertEquals(expected, feed.state.members.value)
589599
}
590600

591601
@Test

stream-feeds-android-client/src/test/kotlin/io/getstream/feeds/android/client/internal/state/event/handler/MemberListEventHandlerTest.kt

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@ package io.getstream.feeds.android.client.internal.state.event.handler
1717

1818
import io.getstream.android.core.api.filter.equal
1919
import io.getstream.feeds.android.client.api.model.FeedId
20+
import io.getstream.feeds.android.client.api.model.ModelUpdates
2021
import io.getstream.feeds.android.client.api.state.query.MembersFilterField
2122
import io.getstream.feeds.android.client.internal.state.MemberListStateUpdates
2223
import io.getstream.feeds.android.client.internal.state.event.StateUpdateEvent
2324
import io.getstream.feeds.android.client.internal.state.event.StateUpdateEvent.FeedMemberAdded
25+
import io.getstream.feeds.android.client.internal.state.event.StateUpdateEvent.FeedMemberBatchUpdate
2426
import io.getstream.feeds.android.client.internal.state.event.StateUpdateEvent.FeedMemberRemoved
2527
import io.getstream.feeds.android.client.internal.state.event.StateUpdateEvent.FeedMemberUpdated
2628
import io.getstream.feeds.android.client.internal.test.TestData.feedMemberData
@@ -39,7 +41,7 @@ internal class MemberListEventHandlerTest(
3941
override val handler = MemberListEventHandler(FeedId(fid), testFilter, state)
4042

4143
companion object {
42-
private val fid = "user:feed-1"
44+
private const val fid = "user:feed-1"
4345
private const val differentFeedId = "user:different-feed"
4446
private val testFilter = MembersFilterField.role.equal("admin")
4547
private val matchingMember = feedMemberData(role = "admin")
@@ -89,6 +91,48 @@ internal class MemberListEventHandlerTest(
8991
event = FeedMemberUpdated(fid, nonMatchingMember),
9092
verifyBlock = { state -> state.onMemberRemoved(nonMatchingMember.id) },
9193
),
94+
run {
95+
val anotherMatching = feedMemberData(userId = "user-2", role = "admin")
96+
val anotherNonMatching = feedMemberData(userId = "user-3", role = "member")
97+
98+
testParams<MemberListStateUpdates>(
99+
name = "FeedMemberBatchUpdate matching feed with mixed updates",
100+
event =
101+
FeedMemberBatchUpdate(
102+
fid = fid,
103+
updates =
104+
ModelUpdates(
105+
added = listOf(matchingMember, nonMatchingMember),
106+
updated = listOf(anotherMatching, anotherNonMatching),
107+
removedIds = listOf("removed-1", "removed-2"),
108+
),
109+
),
110+
verifyBlock = { state ->
111+
state.onMembersUpdated(
112+
ModelUpdates(
113+
added = listOf(matchingMember),
114+
updated = listOf(anotherMatching),
115+
removedIds =
116+
listOf("removed-1", "removed-2", anotherNonMatching.id),
117+
)
118+
)
119+
},
120+
)
121+
},
122+
testParams<MemberListStateUpdates>(
123+
name = "FeedMemberBatchUpdate non-matching feed",
124+
event =
125+
FeedMemberBatchUpdate(
126+
fid = differentFeedId,
127+
updates =
128+
ModelUpdates(
129+
added = listOf(matchingMember),
130+
updated = emptyList(),
131+
removedIds = listOf("removed-1"),
132+
),
133+
),
134+
verifyBlock = { state -> state wasNot called },
135+
),
92136
)
93137
}
94138
}

0 commit comments

Comments
 (0)