Skip to content

Commit a82510d

Browse files
authored
Update handling of feed member & follow events (#122)
* Merge feed member added & updated handling and add it where missing * Merge follow added & updated handling
1 parent 6d2b364 commit a82510d

File tree

12 files changed

+124
-82
lines changed

12 files changed

+124
-82
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ internal class FollowListImpl(
4545

4646
private val _state: FollowListStateImpl = FollowListStateImpl(query)
4747

48-
private val eventHandler = FollowListEventHandler(_state)
48+
private val eventHandler = FollowListEventHandler(query.filter, _state)
4949

5050
init {
5151
subscriptionManager.subscribe(eventHandler)

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

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import io.getstream.feeds.android.client.api.state.query.FollowsSort
2323
import io.getstream.feeds.android.client.internal.model.PaginationResult
2424
import io.getstream.feeds.android.client.internal.state.query.FollowsQueryConfig
2525
import io.getstream.feeds.android.client.internal.utils.mergeSorted
26+
import io.getstream.feeds.android.client.internal.utils.upsertSorted
2627
import kotlinx.coroutines.flow.MutableStateFlow
2728
import kotlinx.coroutines.flow.StateFlow
2829
import kotlinx.coroutines.flow.asStateFlow
@@ -65,16 +66,8 @@ internal class FollowListStateImpl(override val query: FollowsQuery) : FollowLis
6566
}
6667
}
6768

68-
override fun onFollowUpdated(follow: FollowData) {
69-
_follows.update { current ->
70-
current.map {
71-
if (it.id == follow.id) {
72-
follow
73-
} else {
74-
it
75-
}
76-
}
77-
}
69+
override fun onFollowUpserted(follow: FollowData) {
70+
_follows.update { current -> current.upsertSorted(follow, FollowData::id, followsSorting) }
7871
}
7972

8073
override fun onFollowRemoved(follow: FollowData) {
@@ -96,8 +89,8 @@ internal interface FollowListStateUpdates {
9689
/** Handles the result of a query for more follows. */
9790
fun onQueryMoreFollows(result: PaginationResult<FollowData>, queryConfig: FollowsQueryConfig)
9891

99-
/** Handles the update of a follow data. */
100-
fun onFollowUpdated(follow: FollowData)
92+
/** Handles the addition or update of a follow. */
93+
fun onFollowUpserted(follow: FollowData)
10194

10295
/** Handles the removal of a follow. */
10396
fun onFollowRemoved(follow: FollowData)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ internal class MemberListImpl(
4545

4646
private val _state: MemberListStateImpl = MemberListStateImpl(query)
4747

48-
private val eventHandler = MemberListEventHandler(query.fid, _state)
48+
private val eventHandler = MemberListEventHandler(query.fid, query.filter, _state)
4949

5050
init {
5151
subscriptionManager.subscribe(eventHandler)

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

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -69,17 +69,11 @@ internal class MemberListStateImpl(override val query: MembersQuery) : MemberLis
6969
}
7070
}
7171

72-
override fun onMemberAdded(member: FeedMemberData) {
73-
_members.update { current ->
74-
current.upsertSorted(member, FeedMemberData::id, membersSorting)
75-
}
76-
}
77-
7872
override fun onMemberRemoved(memberId: String) {
7973
_members.update { current -> current.filter { it.id != memberId } }
8074
}
8175

82-
override fun onMemberUpdated(member: FeedMemberData) {
76+
override fun onMemberUpserted(member: FeedMemberData) {
8377
_members.update { current ->
8478
current.upsertSorted(member, FeedMemberData::id, membersSorting)
8579
}
@@ -133,14 +127,11 @@ internal interface MemberListStateUpdates {
133127
queryConfig: MembersQueryConfig,
134128
)
135129

136-
/** Handles the addition of a new member. */
137-
fun onMemberAdded(member: FeedMemberData)
138-
139130
/** Handles the removal of a member by their ID. */
140131
fun onMemberRemoved(memberId: String)
141132

142-
/** Handles the update of a member's data. */
143-
fun onMemberUpdated(member: FeedMemberData)
133+
/** Handles the addition or update of a member. */
134+
fun onMemberUpserted(member: FeedMemberData)
144135

145136
/** Handles updates to multiple members. */
146137
fun onMembersUpdated(updates: ModelUpdates<FeedMemberData>)

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,10 +155,10 @@ internal sealed interface StateUpdateEvent {
155155

156156
data class FollowAdded(val follow: FollowData) : StateUpdateEvent
157157

158-
data class FollowUpdated(val follow: FollowData) : StateUpdateEvent
159-
160158
data class FollowDeleted(val follow: FollowData) : StateUpdateEvent
161159

160+
data class FollowUpdated(val follow: FollowData) : StateUpdateEvent
161+
162162
data class NotificationFeedUpdated(
163163
val fid: String,
164164
val aggregatedActivities: List<AggregatedActivityData>,

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

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,35 @@
1515
*/
1616
package io.getstream.feeds.android.client.internal.state.event.handler
1717

18+
import io.getstream.feeds.android.client.api.state.query.FollowsFilter
1819
import io.getstream.feeds.android.client.internal.state.FollowListStateUpdates
1920
import io.getstream.feeds.android.client.internal.state.event.StateUpdateEvent
21+
import io.getstream.feeds.android.client.internal.state.query.matches
2022
import io.getstream.feeds.android.client.internal.subscribe.StateUpdateEventListener
2123

22-
internal class FollowListEventHandler(private val state: FollowListStateUpdates) :
23-
StateUpdateEventListener {
24+
internal class FollowListEventHandler(
25+
private val filter: FollowsFilter?,
26+
private val state: FollowListStateUpdates,
27+
) : StateUpdateEventListener {
2428

2529
override fun onEvent(event: StateUpdateEvent) {
2630
when (event) {
27-
is StateUpdateEvent.FollowUpdated -> {
28-
state.onFollowUpdated(event.follow)
31+
is StateUpdateEvent.FollowAdded -> {
32+
if (event.follow matches filter) {
33+
state.onFollowUpserted(event.follow)
34+
}
2935
}
3036

3137
is StateUpdateEvent.FollowDeleted -> {
3238
state.onFollowRemoved(event.follow)
3339
}
3440

41+
is StateUpdateEvent.FollowUpdated -> {
42+
if (event.follow matches filter) {
43+
state.onFollowUpserted(event.follow)
44+
}
45+
}
46+
3547
else -> {}
3648
}
3749
}

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

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,22 @@
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.state.query.MembersFilter
1920
import io.getstream.feeds.android.client.internal.state.MemberListStateUpdates
2021
import io.getstream.feeds.android.client.internal.state.event.StateUpdateEvent
22+
import io.getstream.feeds.android.client.internal.state.query.matches
2123
import io.getstream.feeds.android.client.internal.subscribe.StateUpdateEventListener
2224

2325
internal class MemberListEventHandler(
2426
private val fid: FeedId,
27+
private val filter: MembersFilter?,
2528
private val state: MemberListStateUpdates,
2629
) : StateUpdateEventListener {
2730
override fun onEvent(event: StateUpdateEvent) {
2831
when (event) {
2932
is StateUpdateEvent.FeedMemberAdded -> {
30-
if (event.fid == fid.rawValue) {
31-
state.onMemberAdded(event.member)
33+
if (event.fid == fid.rawValue && event.member matches filter) {
34+
state.onMemberUpserted(event.member)
3235
}
3336
}
3437

@@ -39,8 +42,8 @@ internal class MemberListEventHandler(
3942
}
4043

4144
is StateUpdateEvent.FeedMemberUpdated -> {
42-
if (event.fid == fid.rawValue) {
43-
state.onMemberUpdated(event.member)
45+
if (event.fid == fid.rawValue && event.member matches filter) {
46+
state.onMemberUpserted(event.member)
4447
}
4548
}
4649

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

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -49,36 +49,35 @@ internal class FollowListStateImplTest {
4949
}
5050

5151
@Test
52-
fun `on followUpdated, then update specific follow`() = runTest {
52+
fun `on followUpserted, then update specific follow`() = runTest {
5353
val initialFollows = listOf(followData(), followData("user-2", "user-3"))
5454
val paginationResult = defaultPaginationResult(initialFollows)
5555
followListState.onQueryMoreFollows(paginationResult, queryConfig)
5656

5757
val updatedFollow =
58-
followData(
59-
sourceFid = "user:user-1",
60-
targetFid = "user:user-2",
61-
createdAt = java.util.Date(1000),
62-
)
63-
followListState.onFollowUpdated(updatedFollow)
58+
followData(sourceFid = "user:user-1", targetFid = "user:user-2", createdAt = 1000)
59+
followListState.onFollowUpserted(updatedFollow)
6460

6561
val updatedFollows = followListState.follows.value
6662
assertEquals(updatedFollow, updatedFollows.find { it.id == updatedFollow.id })
6763
assertEquals(initialFollows[1], updatedFollows.find { it.id == initialFollows[1].id })
6864
}
6965

7066
@Test
71-
fun `on followUpdated with non-existent follow, then keep existing follows unchanged`() =
72-
runTest {
73-
val initialFollows = listOf(followData(), followData("user-2", "user-3"))
74-
val paginationResult = defaultPaginationResult(initialFollows)
75-
followListState.onQueryMoreFollows(paginationResult, queryConfig)
67+
fun `on followUpserted with non-existent follow, then insert in sorted position`() = runTest {
68+
val follow1 = followData("user-1", "user-2", createdAt = 2000)
69+
val follow2 = followData("user-2", "user-3", createdAt = 1000)
70+
val initialFollows = listOf(follow1, follow2)
71+
val paginationResult = defaultPaginationResult(initialFollows)
72+
followListState.onQueryMoreFollows(paginationResult, queryConfig)
7673

77-
val nonExistentFollow = followData("user-4", "user-5")
78-
followListState.onFollowUpdated(nonExistentFollow)
74+
val newFollow = followData("user-4", "user-5", createdAt = 1500)
75+
followListState.onFollowUpserted(newFollow)
7976

80-
assertEquals(initialFollows, followListState.follows.value)
81-
}
77+
// Expect follows to be sorted by createdAt in descending order (newest first)
78+
val expectedFollows = listOf(follow1, newFollow, follow2)
79+
assertEquals(expectedFollows, followListState.follows.value)
80+
}
8281

8382
@Test
8483
fun `on followRemoved, then remove specific follow`() = runTest {

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ internal class MemberListStateImplTest {
6262
setupInitialState(initialMembers)
6363

6464
val updatedMember = feedMemberData("user-1", role = "admin", createdAt = Date(3000))
65-
memberListState.onMemberUpdated(updatedMember)
65+
memberListState.onMemberUpserted(updatedMember)
6666

6767
// Member should be repositioned according to new sort criteria
6868
val expectedMembers = listOf(updatedMember, initialMembers[0])
@@ -106,7 +106,7 @@ internal class MemberListStateImplTest {
106106
}
107107

108108
@Test
109-
fun `on memberAdded, then add member in sorted position`() = runTest {
109+
fun `on onMemberUpserted, then add member in sorted position`() = runTest {
110110
// Initial members already sorted by createdAt desc
111111
val initialMembers =
112112
listOf(
@@ -116,15 +116,15 @@ internal class MemberListStateImplTest {
116116
setupInitialState(initialMembers)
117117

118118
val newMember = feedMemberData("user-2", createdAt = Date(2000))
119-
memberListState.onMemberAdded(newMember)
119+
memberListState.onMemberUpserted(newMember)
120120

121121
// Member should be inserted in correct sorted position
122122
val expectedMembers = listOf(initialMembers[0], newMember, initialMembers[1])
123123
assertEquals(expectedMembers, memberListState.members.value)
124124
}
125125

126126
@Test
127-
fun `on memberAdded with existing id, then update and reposition member`() = runTest {
127+
fun `on onMemberUpserted with existing id, then update and reposition member`() = runTest {
128128
// Initial members already sorted by createdAt desc
129129
val initialMembers =
130130
listOf(
@@ -135,7 +135,7 @@ internal class MemberListStateImplTest {
135135

136136
// Add existing user-1 with newer createdAt that should move it to the front
137137
val updatedMember = feedMemberData("user-1", role = "admin", createdAt = Date(3000))
138-
memberListState.onMemberAdded(updatedMember)
138+
memberListState.onMemberUpserted(updatedMember)
139139

140140
// Member should be updated and repositioned according to new sort criteria (3000, 2000)
141141
val expectedMembers = listOf(updatedMember, initialMembers[0])

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

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,16 @@
1515
*/
1616
package io.getstream.feeds.android.client.internal.state.event.handler
1717

18+
import io.getstream.android.core.api.filter.equal
19+
import io.getstream.feeds.android.client.api.state.query.FollowsFilterField
1820
import io.getstream.feeds.android.client.internal.state.FollowListStateUpdates
1921
import io.getstream.feeds.android.client.internal.state.event.StateUpdateEvent
22+
import io.getstream.feeds.android.client.internal.state.event.StateUpdateEvent.FollowAdded
2023
import io.getstream.feeds.android.client.internal.state.event.StateUpdateEvent.FollowDeleted
2124
import io.getstream.feeds.android.client.internal.state.event.StateUpdateEvent.FollowUpdated
2225
import io.getstream.feeds.android.client.internal.test.TestData.followData
2326
import io.mockk.MockKVerificationScope
27+
import io.mockk.called
2428
import io.mockk.mockk
2529
import org.junit.runners.Parameterized
2630

@@ -31,22 +35,45 @@ internal class FollowListEventHandlerTest(
3135
) : BaseEventHandlerTest<FollowListStateUpdates>(testName, event, verifyBlock) {
3236

3337
override val state: FollowListStateUpdates = mockk(relaxed = true)
34-
override val handler = FollowListEventHandler(state)
38+
override val handler = FollowListEventHandler(filter, state)
3539

3640
companion object {
41+
private val filter = FollowsFilterField.sourceFeed.equal("user:timeline")
42+
3743
@JvmStatic
3844
@Parameterized.Parameters(name = "{0}")
3945
fun data(): List<Array<Any>> =
4046
listOf(
4147
testParams<FollowListStateUpdates>(
42-
name = "FollowUpdated",
43-
event = FollowUpdated(followData()),
44-
verifyBlock = { state -> state.onFollowUpdated(followData()) },
48+
name = "FollowAdded matching filter",
49+
event = FollowAdded(followData(sourceFid = "user:timeline")),
50+
verifyBlock = { state ->
51+
state.onFollowUpserted(followData(sourceFid = "user:timeline"))
52+
},
53+
),
54+
testParams<FollowListStateUpdates>(
55+
name = "FollowAdded non-matching filter",
56+
event = FollowAdded(followData(sourceFid = "user:notifications")),
57+
verifyBlock = { state -> state wasNot called },
58+
),
59+
testParams<FollowListStateUpdates>(
60+
name = "FollowDeleted calls onFollowRemoved",
61+
event = FollowDeleted(followData(sourceFid = "user:timeline")),
62+
verifyBlock = { state ->
63+
state.onFollowRemoved(followData(sourceFid = "user:timeline"))
64+
},
65+
),
66+
testParams<FollowListStateUpdates>(
67+
name = "FollowUpdated matching filter",
68+
event = FollowUpdated(followData(sourceFid = "user:timeline")),
69+
verifyBlock = { state ->
70+
state.onFollowUpserted(followData(sourceFid = "user:timeline"))
71+
},
4572
),
4673
testParams<FollowListStateUpdates>(
47-
name = "FollowDeleted",
48-
event = FollowDeleted(followData()),
49-
verifyBlock = { state -> state.onFollowRemoved(followData()) },
74+
name = "FollowUpdated non-matching filter",
75+
event = FollowUpdated(followData(sourceFid = "user:notifications")),
76+
verifyBlock = { state -> state wasNot called },
5077
),
5178
)
5279
}

0 commit comments

Comments
 (0)