@@ -23,6 +23,7 @@ import androidx.compose.runtime.setValue
23
23
import dev.zacsweers.metro.Assisted
24
24
import dev.zacsweers.metro.Inject
25
25
import im.vector.app.features.analytics.plan.JoinedRoom
26
+ import io.element.android.features.invite.api.InviteData
26
27
import io.element.android.features.invite.api.SeenInvitesStore
27
28
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteEvents
28
29
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteState
@@ -42,17 +43,21 @@ import io.element.android.libraries.matrix.api.exception.ClientException
42
43
import io.element.android.libraries.matrix.api.exception.ErrorKind
43
44
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
44
45
import io.element.android.libraries.matrix.api.room.RoomInfo
45
- import io.element.android.libraries.matrix.api.room.RoomMember
46
+ import io.element.android.libraries.matrix.api.room.RoomMembershipDetails
46
47
import io.element.android.libraries.matrix.api.room.RoomType
47
48
import io.element.android.libraries.matrix.api.room.isDm
48
49
import io.element.android.libraries.matrix.api.room.join.JoinRoom
49
50
import io.element.android.libraries.matrix.api.room.join.JoinRule
50
51
import io.element.android.libraries.matrix.api.room.preview.RoomPreviewInfo
52
+ import io.element.android.libraries.matrix.api.spaces.SpaceRoom
51
53
import io.element.android.libraries.matrix.ui.model.toInviteSender
52
54
import io.element.android.libraries.matrix.ui.safety.rememberHideInvitesAvatar
55
+ import kotlinx.collections.immutable.persistentListOf
56
+ import kotlinx.collections.immutable.toPersistentList
53
57
import kotlinx.coroutines.CoroutineScope
54
58
import kotlinx.coroutines.launch
55
59
import java.util.Optional
60
+ import kotlin.jvm.optionals.getOrNull
56
61
57
62
@Inject
58
63
class JoinRoomPresenter (
@@ -80,13 +85,18 @@ class JoinRoomPresenter(
80
85
): JoinRoomPresenter
81
86
}
82
87
88
+ private val spaceList = matrixClient.spaceService.spaceRoomList(roomId)
89
+
83
90
@Composable
84
91
override fun present (): JoinRoomState {
85
92
val coroutineScope = rememberCoroutineScope()
86
93
var retryCount by remember { mutableIntStateOf(0 ) }
87
94
val roomInfo by remember {
88
95
matrixClient.getRoomInfoFlow(roomId)
89
96
}.collectAsState(initial = Optional .empty())
97
+ val spaceRoom by remember {
98
+ spaceList.currentSpaceFlow()
99
+ }.collectAsState()
90
100
val joinAction: MutableState <AsyncAction <Unit >> = remember { mutableStateOf(AsyncAction .Uninitialized ) }
91
101
val knockAction: MutableState <AsyncAction <Unit >> = remember { mutableStateOf(AsyncAction .Uninitialized ) }
92
102
val cancelKnockAction: MutableState <AsyncAction <Unit >> = remember { mutableStateOf(AsyncAction .Uninitialized ) }
@@ -96,55 +106,41 @@ class JoinRoomPresenter(
96
106
val hideInviteAvatars by matrixClient.rememberHideInvitesAvatar()
97
107
val canReportRoom by produceState(false ) { value = matrixClient.canReportRoom() }
98
108
99
- val contentState by produceState<ContentState >(
100
- initialValue = ContentState .Loading ,
101
- key1 = roomInfo,
102
- key2 = retryCount,
103
- key3 = isDismissingContent,
104
- ) {
109
+ var contentState by remember {
110
+ mutableStateOf<ContentState >(ContentState .Loading )
111
+ }
112
+ LaunchedEffect (roomInfo, retryCount, isDismissingContent, spaceRoom) {
105
113
when {
106
- isDismissingContent -> value = ContentState .Dismissing
114
+ isDismissingContent -> contentState = ContentState .Dismissing
107
115
roomInfo.isPresent -> {
108
116
val notJoinedRoom = matrixClient.getRoomPreview(roomIdOrAlias, serverNames).getOrNull()
109
- val (sender, reason) = when (roomInfo.get().currentUserMembership) {
110
- CurrentUserMembership .BANNED -> {
111
- // Workaround to get info about the sender for banned rooms
112
- // TODO re-do this once we have a better API in the SDK
113
- val membershipDetails = notJoinedRoom?.membershipDetails()?.getOrNull()
114
- membershipDetails?.senderMember to membershipDetails?.currentUserMember?.membershipChangeReason
115
- }
116
- CurrentUserMembership .INVITED -> {
117
- roomInfo.get().inviter to null
118
- }
119
- else -> null to null
120
- }
117
+ val membershipDetails = notJoinedRoom?.membershipDetails()?.getOrNull()
121
118
val joinedMembersCountOverride = notJoinedRoom?.previewInfo?.numberOfJoinedMembers
122
- value = roomInfo.get().toContentState(
123
- membershipSender = sender,
119
+ contentState = roomInfo.get().toContentState(
124
120
joinedMembersCountOverride = joinedMembersCountOverride,
125
- reason = reason,
121
+ membershipDetails = membershipDetails,
122
+ childrenCount = spaceRoom.getOrNull()?.childrenCount,
126
123
)
127
124
}
125
+ spaceRoom.isPresent -> {
126
+ val spaceRoom = spaceRoom.get()
127
+ // Only use this state when space is not locally known
128
+ contentState = if (spaceRoom.state != null ) {
129
+ ContentState .Loading
130
+ } else {
131
+ spaceRoom.toContentState()
132
+ }
133
+ }
128
134
roomDescription.isPresent -> {
129
- value = roomDescription.get().toContentState()
135
+ contentState = roomDescription.get().toContentState()
130
136
}
131
137
else -> {
132
- value = ContentState .Loading
138
+ contentState = ContentState .Loading
133
139
val result = matrixClient.getRoomPreview(roomIdOrAlias, serverNames)
134
- value = result.fold(
140
+ contentState = result.fold(
135
141
onSuccess = { preview ->
136
- val membershipInfo = when (preview.previewInfo.membership) {
137
- CurrentUserMembership .INVITED ,
138
- CurrentUserMembership .BANNED ,
139
- CurrentUserMembership .KNOCKED -> {
140
- preview.membershipDetails().getOrNull()
141
- }
142
- else -> null
143
- }
144
- preview.previewInfo.toContentState(
145
- senderMember = membershipInfo?.senderMember,
146
- reason = membershipInfo?.currentUserMember?.membershipChangeReason,
147
- )
142
+ val membershipDetails = preview.membershipDetails().getOrNull()
143
+ preview.previewInfo.toContentState(membershipDetails)
148
144
},
149
145
onFailure = { throwable ->
150
146
if (throwable is ClientException .MatrixApi && (throwable.kind == ErrorKind .NotFound || throwable.kind == ErrorKind .Forbidden )) {
@@ -252,30 +248,56 @@ class JoinRoomPresenter(
252
248
}
253
249
}
254
250
255
- private fun RoomPreviewInfo.toContentState (senderMember : RoomMember ? , reason : String ? ): ContentState {
251
+ private fun RoomPreviewInfo.toContentState (membershipDetails : RoomMembershipDetails ? ): ContentState {
256
252
return ContentState .Loaded (
257
253
roomId = roomId,
258
254
name = name,
259
255
topic = topic,
260
256
alias = canonicalAlias,
261
257
numberOfMembers = numberOfJoinedMembers,
262
- isDm = false ,
263
- roomType = roomType,
264
258
roomAvatarUrl = avatarUrl,
265
- joinAuthorisationStatus = when (membership) {
266
- CurrentUserMembership .INVITED -> {
267
- JoinAuthorisationStatus .IsInvited (
268
- inviteData = toInviteData(),
269
- inviteSender = senderMember?.toInviteSender()
270
- )
271
- }
272
- CurrentUserMembership .BANNED -> JoinAuthorisationStatus .IsBanned (senderMember?.toInviteSender(), reason)
273
- CurrentUserMembership .KNOCKED -> JoinAuthorisationStatus .IsKnocked
274
- else -> joinRule.toJoinAuthorisationStatus()
259
+ joinAuthorisationStatus = computeJoinAuthorisationStatus(
260
+ membership,
261
+ membershipDetails,
262
+ joinRule,
263
+ { toInviteData() }
264
+ ),
265
+ joinRule = joinRule,
266
+ details = when (roomType) {
267
+ is RoomType .Other ,
268
+ RoomType .Room -> LoadedDetails .Room (
269
+ isDm = false ,
270
+ )
271
+ RoomType .Space -> LoadedDetails .Space (
272
+ childrenCount = 0 ,
273
+ heroes = persistentListOf(),
274
+ )
275
275
}
276
276
)
277
277
}
278
278
279
+ private fun SpaceRoom.toContentState (): ContentState {
280
+ return ContentState .Loaded (
281
+ roomId = roomId,
282
+ name = name,
283
+ topic = topic,
284
+ alias = canonicalAlias,
285
+ numberOfMembers = numJoinedMembers.toLong(),
286
+ roomAvatarUrl = avatarUrl,
287
+ joinAuthorisationStatus = computeJoinAuthorisationStatus(
288
+ membership = state,
289
+ membershipDetails = null ,
290
+ joinRule = joinRule,
291
+ inviteData = { toInviteData() }
292
+ ),
293
+ joinRule = joinRule,
294
+ details = LoadedDetails .Space (
295
+ childrenCount = childrenCount,
296
+ heroes = heroes.toPersistentList(),
297
+ )
298
+ )
299
+ }
300
+
279
301
@VisibleForTesting
280
302
internal fun RoomDescription.toContentState (): ContentState {
281
303
return ContentState .Loaded (
@@ -284,47 +306,79 @@ internal fun RoomDescription.toContentState(): ContentState {
284
306
topic = topic,
285
307
alias = alias,
286
308
numberOfMembers = numberOfMembers,
287
- isDm = false ,
288
- roomType = RoomType .Room ,
289
309
roomAvatarUrl = avatarUrl,
290
310
joinAuthorisationStatus = when (joinRule) {
291
311
RoomDescription .JoinRule .KNOCK -> JoinAuthorisationStatus .CanKnock
292
312
RoomDescription .JoinRule .PUBLIC -> JoinAuthorisationStatus .CanJoin
293
313
else -> JoinAuthorisationStatus .Unknown
294
- }
314
+ },
315
+ joinRule = when (joinRule) {
316
+ RoomDescription .JoinRule .KNOCK -> JoinRule .Knock
317
+ RoomDescription .JoinRule .PUBLIC -> JoinRule .Public
318
+ RoomDescription .JoinRule .RESTRICTED -> JoinRule .Restricted (persistentListOf())
319
+ RoomDescription .JoinRule .KNOCK_RESTRICTED -> JoinRule .KnockRestricted (persistentListOf())
320
+ RoomDescription .JoinRule .INVITE -> JoinRule .Invite
321
+ RoomDescription .JoinRule .UNKNOWN -> null
322
+ },
323
+ details = LoadedDetails .Room (isDm = false )
295
324
)
296
325
}
297
326
298
327
@VisibleForTesting
299
328
internal fun RoomInfo.toContentState (
300
- membershipSender : RoomMember ? ,
301
329
joinedMembersCountOverride : Long? ,
302
- reason : String? ,
330
+ membershipDetails : RoomMembershipDetails ? ,
331
+ childrenCount : Int? ,
303
332
): ContentState {
304
333
return ContentState .Loaded (
305
334
roomId = id,
306
335
name = name,
307
336
topic = topic,
308
337
alias = canonicalAlias,
309
338
numberOfMembers = joinedMembersCountOverride ? : joinedMembersCount,
310
- isDm = isDm,
311
- roomType = if (isSpace) RoomType .Space else RoomType .Room ,
312
339
roomAvatarUrl = avatarUrl,
313
- joinAuthorisationStatus = when (currentUserMembership) {
314
- CurrentUserMembership .INVITED -> JoinAuthorisationStatus .IsInvited (
315
- inviteData = toInviteData(),
316
- inviteSender = membershipSender?.toInviteSender(),
340
+ joinAuthorisationStatus = computeJoinAuthorisationStatus(
341
+ membership = currentUserMembership,
342
+ membershipDetails = membershipDetails,
343
+ joinRule = joinRule,
344
+ inviteData = { toInviteData() }
345
+ ),
346
+ joinRule = joinRule,
347
+ details = if (isSpace) {
348
+ LoadedDetails .Space (
349
+ childrenCount = childrenCount ? : 0 ,
350
+ heroes = heroes,
317
351
)
318
- CurrentUserMembership . BANNED -> JoinAuthorisationStatus . IsBanned (
319
- banSender = membershipSender?.toInviteSender(),
320
- reason = reason ,
352
+ } else {
353
+ LoadedDetails . Room (
354
+ isDm = isDm ,
321
355
)
322
- CurrentUserMembership .KNOCKED -> JoinAuthorisationStatus .IsKnocked
323
- else -> joinRule.toJoinAuthorisationStatus()
324
- }
356
+ },
325
357
)
326
358
}
327
359
360
+ private fun computeJoinAuthorisationStatus (
361
+ membership : CurrentUserMembership ? ,
362
+ membershipDetails : RoomMembershipDetails ? ,
363
+ joinRule : JoinRule ? ,
364
+ inviteData : () -> InviteData ,
365
+ ): JoinAuthorisationStatus {
366
+ return when (membership) {
367
+ CurrentUserMembership .INVITED -> {
368
+ JoinAuthorisationStatus .IsInvited (
369
+ inviteData = inviteData(),
370
+ inviteSender = membershipDetails?.senderMember?.toInviteSender()
371
+ )
372
+ }
373
+ CurrentUserMembership .BANNED -> JoinAuthorisationStatus .IsBanned (
374
+ membershipDetails?.senderMember?.toInviteSender(),
375
+ membershipDetails?.membershipChangeReason
376
+ )
377
+ CurrentUserMembership .KNOCKED -> JoinAuthorisationStatus .IsKnocked
378
+ else -> joinRule.toJoinAuthorisationStatus()
379
+ }
380
+ }
381
+
328
382
private fun JoinRule?.toJoinAuthorisationStatus (): JoinAuthorisationStatus {
329
383
return when (this ) {
330
384
JoinRule .Knock ,
0 commit comments