Skip to content

Commit 619962b

Browse files
authored
fix(core): use the right time units for participant joinedAt and CallUser dates (#1891)
1 parent 708c562 commit 619962b

4 files changed

Lines changed: 117 additions & 6 deletions

File tree

‎stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/ParticipantState.kt‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -229,10 +229,12 @@ public data class ParticipantState(
229229
fun updateFromParticipantInfo(participant: Participant) {
230230
sessionId = participant.session_id
231231

232-
val joinedAtMilli =
233-
participant.joined_at?.toEpochMilli() ?: OffsetDateTime.now().toEpochSecond()
234-
val instant = Instant.ofEpochSecond(joinedAtMilli)
235-
_joinedAt.value = OffsetDateTime.ofInstant(instant, ZoneOffset.UTC)
232+
_joinedAt.value = participant.joined_at?.let {
233+
OffsetDateTime.ofInstant(
234+
Instant.ofEpochSecond(it.epochSecond, it.nano.toLong()),
235+
ZoneOffset.UTC,
236+
)
237+
} ?: OffsetDateTime.now(ZoneOffset.UTC)
236238

237239
trackLookupPrefix = participant.track_lookup_prefix
238240
_networkQuality.value = NetworkQuality.fromConnectionQuality(participant.connection_quality)

‎stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/utils/DomainUtils.kt‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ internal fun MemberResponse.toCallUser(): CallUser {
5252
teams = user.teams ?: emptyList(),
5353
role = user.role,
5454
state = null,
55-
createdAt = Date(createdAt.toEpochSecond()),
56-
updatedAt = Date(updatedAt.toEpochSecond()),
55+
createdAt = Date(createdAt.toInstant().toEpochMilli()),
56+
updatedAt = Date(updatedAt.toInstant().toEpochMilli()),
5757
)
5858
}
5959

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright (c) 2014-2026 Stream.io Inc. All rights reserved.
3+
*
4+
* Licensed under the Stream License;
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://github.com/GetStream/stream-video-android/blob/main/LICENSE
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.getstream.video.android.core
18+
19+
import com.google.common.truth.Truth.assertThat
20+
import io.getstream.video.android.core.sorting.participant
21+
import io.getstream.video.android.core.sorting.stubCallActions
22+
import kotlinx.coroutines.CoroutineScope
23+
import kotlinx.coroutines.test.StandardTestDispatcher
24+
import kotlinx.coroutines.test.TestCoroutineScheduler
25+
import org.junit.Test
26+
import org.threeten.bp.OffsetDateTime
27+
import org.threeten.bp.ZoneOffset
28+
import stream.video.sfu.models.Participant
29+
30+
class ParticipantStateJoinedAtTest {
31+
32+
private val scope = CoroutineScope(StandardTestDispatcher(TestCoroutineScheduler()))
33+
34+
@Test
35+
fun `joinedAt keeps the instant the SFU sent`() {
36+
val state = participant("session", scope, stubCallActions())
37+
38+
state.updateFromParticipantInfo(
39+
Participant(
40+
session_id = "session",
41+
joined_at = java.time.Instant.parse("2026-10-22T08:15:30.123456789Z"),
42+
),
43+
)
44+
45+
assertThat(state.joinedAt.value)
46+
.isEqualTo(OffsetDateTime.of(2026, 10, 22, 8, 15, 30, 123_456_789, ZoneOffset.UTC))
47+
}
48+
49+
@Test
50+
fun `joinedAt falls back to now when the SFU sends none`() {
51+
val state = participant("session", scope, stubCallActions())
52+
val before = OffsetDateTime.now(ZoneOffset.UTC)
53+
54+
state.updateFromParticipantInfo(Participant(session_id = "session"))
55+
56+
val joinedAt = state.joinedAt.value!!
57+
assertThat(joinedAt).isAtLeast(before)
58+
assertThat(joinedAt).isAtMost(OffsetDateTime.now(ZoneOffset.UTC))
59+
}
60+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright (c) 2014-2026 Stream.io Inc. All rights reserved.
3+
*
4+
* Licensed under the Stream License;
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://github.com/GetStream/stream-video-android/blob/main/LICENSE
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.getstream.video.android.core.utils
18+
19+
import com.google.common.truth.Truth.assertThat
20+
import io.getstream.android.video.generated.models.MemberResponse
21+
import io.getstream.android.video.generated.models.UserResponse
22+
import org.junit.Test
23+
import org.threeten.bp.OffsetDateTime
24+
25+
class MemberResponseToCallUserTest {
26+
27+
@Test
28+
fun `toCallUser keeps the member dates`() {
29+
val createdAt = OffsetDateTime.parse("2026-10-22T08:15:30.123Z")
30+
val updatedAt = OffsetDateTime.parse("2026-10-22T09:00:00.456+02:00")
31+
val member = MemberResponse(
32+
createdAt = createdAt,
33+
updatedAt = updatedAt,
34+
userId = "user",
35+
user = UserResponse(
36+
createdAt = createdAt,
37+
id = "user",
38+
language = "en",
39+
role = "user",
40+
updatedAt = updatedAt,
41+
),
42+
)
43+
44+
val callUser = member.toCallUser()
45+
46+
assertThat(callUser.createdAt!!.time).isEqualTo(createdAt.toInstant().toEpochMilli())
47+
assertThat(callUser.updatedAt!!.time).isEqualTo(updatedAt.toInstant().toEpochMilli())
48+
}
49+
}

0 commit comments

Comments
 (0)