@@ -21,6 +21,8 @@ import kotlinx.coroutines.flow.mapNotNull
2121import kotlinx.coroutines.flow.onEach
2222import xyz.flipchat.app.R
2323import xyz.flipchat.app.data.RoomInfo
24+ import xyz.flipchat.app.features.chat.conversation.ConversationViewModel
25+ import xyz.flipchat.app.features.chat.conversation.ConversationViewModel.Event
2426import xyz.flipchat.app.features.login.register.onResult
2527import xyz.flipchat.app.util.IntentUtils
2628import xyz.flipchat.chat.RoomController
@@ -47,6 +49,7 @@ class ChatInfoViewModel @Inject constructor(
4749) {
4850
4951 data class State (
52+ val isPreview : Boolean = false ,
5053 val isHost : Boolean = false ,
5154 val isMember : Boolean = false ,
5255 val paymentDestination : PublicKey ? = null ,
@@ -64,7 +67,7 @@ class ChatInfoViewModel @Inject constructor(
6467 data class OnHostStatusChanged (val isHost : Boolean ) : Event
6568 data class OnRoomOpenStateChanged (val isOpen : Boolean ) : Event
6669 data class OnDestinationChanged (val destination : PublicKey ) : Event
67- data class OnInfoChanged (val args : RoomInfoArgs ) : Event
70+ data class OnInfoChanged (val args : RoomInfoArgs , val isPreview : Boolean ) : Event
6871 data class OnMembersUpdated (val members : List <MinimalMember >) : Event
6972 // endregion state updates
7073
@@ -86,6 +89,13 @@ class ChatInfoViewModel @Inject constructor(
8689 data class OnOpenRoom (val conversationId : ID ) : Event
8790 data class OnCloseRoom (val conversationId : ID ) : Event
8891
92+ data class PromoteRequested (val member : MinimalMember ) : Event
93+ data class PromoteUser (val conversationId : ID , val userId : ID ) : Event
94+ data class OnUserPromoted (val id : ID ) : Event
95+ data class DemoteRequested (val member : MinimalMember ) : Event
96+ data class DemoteUser (val conversationId : ID , val userId : ID ) : Event
97+ data class OnUserDemoted (val id : ID ) : Event
98+
8999 data object LeaveRoom : Event
90100 data class OnLeavingStateChanged (val leaving : Boolean , val left : Boolean = false ) : Event
91101 data object OnLeaveRoomConfirmed : Event
@@ -274,6 +284,60 @@ class ChatInfoViewModel @Inject constructor(
274284 .map { IntentUtils .shareRoom(stateFlow.value.roomInfo.roomNumber) }
275285 .onEach { dispatchEvent(Event .ShareRoom (it)) }
276286 .launchIn(viewModelScope)
287+
288+ eventFlow
289+ .filterIsInstance<Event .PromoteRequested >()
290+ .map { it.member }
291+ .onEach {
292+ confirmUserPromote(
293+ conversationId = stateFlow.value.roomInfo.id.orEmpty(),
294+ userId = it.id.orEmpty(),
295+ user = it.displayName
296+ )
297+ }.launchIn(viewModelScope)
298+
299+ eventFlow
300+ .filterIsInstance<Event .DemoteRequested >()
301+ .map { it.member }
302+ .onEach {
303+ confirmUserDemote(
304+ conversationId = stateFlow.value.roomInfo.id.orEmpty(),
305+ userId = it.id.orEmpty(),
306+ user = it.displayName
307+ )
308+ }.launchIn(viewModelScope)
309+
310+ eventFlow
311+ .filterIsInstance<Event .PromoteUser >()
312+ .onEach { member ->
313+ roomController.promoteUser(member.conversationId, member.userId)
314+ .onFailure {
315+ TopBarManager .showMessage(
316+ TopBarManager .TopBarMessage (
317+ resources.getString(R .string.error_title_failedToPromoteUser),
318+ resources.getString(R .string.error_description_failedToPromoteUser)
319+ )
320+ )
321+ }.onSuccess {
322+ dispatchEvent(Event .OnUserPromoted (member.userId))
323+ }
324+ }.launchIn(viewModelScope)
325+
326+ eventFlow
327+ .filterIsInstance<Event .DemoteUser >()
328+ .onEach { member ->
329+ roomController.demoteUser(member.conversationId, member.userId)
330+ .onFailure {
331+ TopBarManager .showMessage(
332+ TopBarManager .TopBarMessage (
333+ resources.getString(R .string.error_title_failedToDemoteUser),
334+ resources.getString(R .string.error_description_failedToDemoteUser)
335+ )
336+ )
337+ }.onSuccess {
338+ dispatchEvent(Event .OnUserDemoted (member.userId))
339+ }
340+ }.launchIn(viewModelScope)
277341 }
278342
279343 private fun confirmOpenStateChange (conversationId : ID , isRoomOpen : Boolean ) {
@@ -302,13 +366,50 @@ class ChatInfoViewModel @Inject constructor(
302366 )
303367 }
304368
369+ private fun confirmUserPromote (conversationId : ID , user : String? , userId : ID ) {
370+ BottomBarManager .showMessage(
371+ BottomBarManager .BottomBarMessage (
372+ title = resources.getString(
373+ R .string.title_promoteUserInRoom,
374+ user.orEmpty().ifEmpty { " User" }),
375+ subtitle = resources.getString(R .string.subtitle_promoteUserInRoom),
376+ positiveText = resources.getString(R .string.action_promote),
377+ negativeText = " " ,
378+ tertiaryText = resources.getString(R .string.action_cancel),
379+ onPositive = { dispatchEvent(Event .PromoteUser (conversationId, userId)) },
380+ onNegative = { },
381+ type = BottomBarManager .BottomBarMessageType .THEMED ,
382+ showScrim = true ,
383+ )
384+ )
385+ }
386+
387+ private fun confirmUserDemote (conversationId : ID , user : String? , userId : ID ) {
388+ BottomBarManager .showMessage(
389+ BottomBarManager .BottomBarMessage (
390+ title = resources.getString(
391+ R .string.title_demoteUserInRoom,
392+ user.orEmpty().ifEmpty { " User" }),
393+ subtitle = resources.getString(R .string.subtitle_demoteUserInRoom),
394+ positiveText = resources.getString(R .string.action_demote),
395+ negativeText = " " ,
396+ tertiaryText = resources.getString(R .string.action_cancel),
397+ onPositive = { dispatchEvent(Event .DemoteUser (conversationId, userId)) },
398+ onNegative = { },
399+ type = BottomBarManager .BottomBarMessageType .DESTRUCTIVE ,
400+ showScrim = true ,
401+ )
402+ )
403+ }
404+
305405 companion object {
306406 val updateStateForEvent: (Event ) -> ((State ) -> State ) = { event ->
307- when (event) {
407+ ( when (event) {
308408 Event .LeaveRoom -> { state -> state }
309409 is Event .OnInfoChanged -> { state ->
310410 val args = event.args
311411 state.copy(
412+ isPreview = event.isPreview,
312413 roomInfo = RoomInfo (
313414 id = args.roomId,
314415 number = args.roomNumber,
@@ -322,6 +423,10 @@ class ChatInfoViewModel @Inject constructor(
322423 )
323424 }
324425
426+ is Event .PromoteRequested ,
427+ is Event .PromoteUser ,
428+ is Event .DemoteRequested ,
429+ is Event .DemoteUser ,
325430 is Event .OnChangeMessageFee ,
326431 Event .OnLeaveRoomConfirmed ,
327432 is Event .OnChangeName ,
@@ -334,6 +439,52 @@ class ChatInfoViewModel @Inject constructor(
334439 is Event .OnOpenRoom ,
335440 Event .OnLeftRoom -> { state -> state }
336441
442+ is Event .OnUserPromoted -> { state ->
443+ val members = state.members.flatMap { it.value }
444+ val updatedMembers = members.map {
445+ if (it.id == event.id) {
446+ it.copy(canSpeak = true )
447+ } else {
448+ it
449+ }
450+ }
451+
452+ val groupedMembers = updatedMembers
453+ .groupBy { it.canSpeak }
454+ .mapKeys {
455+ if (it.key) {
456+ MemberType .Speaker
457+ } else {
458+ MemberType .Listener
459+ }
460+ }.mapValues { it.value.sortedByDescending { it.isHost } }
461+
462+ state.copy(members = groupedMembers)
463+ }
464+
465+ is Event .OnUserDemoted -> { state ->
466+ val members = state.members.flatMap { it.value }
467+ val updatedMembers = members.map {
468+ if (it.id == event.id) {
469+ it.copy(canSpeak = false )
470+ } else {
471+ it
472+ }
473+ }
474+
475+ val groupedMembers = updatedMembers
476+ .groupBy { it.canSpeak }
477+ .mapKeys {
478+ if (it.key) {
479+ MemberType .Speaker
480+ } else {
481+ MemberType .Listener
482+ }
483+ }.mapValues { it.value.sortedByDescending { it.isHost } }
484+
485+ state.copy(members = groupedMembers)
486+ }
487+
337488 is Event .OnHostStatusChanged -> { state -> state.copy(isHost = event.isHost) }
338489 is Event .OnFeeChanged -> { state ->
339490 state.copy(
@@ -371,7 +522,12 @@ class ChatInfoViewModel @Inject constructor(
371522 )
372523 }
373524
374- is Event .OnRoomNameChangesEnabled -> { state -> state.copy(roomNameChangesEnabled = event.enabled) }
525+ is Event .OnRoomNameChangesEnabled -> { state ->
526+ state.copy(
527+ roomNameChangesEnabled = event.enabled
528+ )
529+ }
530+
375531 is Event .OnDestinationChanged -> { state -> state.copy(paymentDestination = event.destination) }
376532 is Event .OnJoiningStateChanged -> { state ->
377533 state.copy(
@@ -384,12 +540,15 @@ class ChatInfoViewModel @Inject constructor(
384540
385541 is Event .OnLeavingStateChanged -> { state ->
386542 state.copy(
387- leaving = state.joining.copy(loading = event.leaving, success = event.left)
543+ leaving = state.joining.copy(
544+ loading = event.leaving,
545+ success = event.left
546+ )
388547 )
389548 }
390549
391550 is Event .OnRoomOpenStateChanged -> { state -> state.copy(isOpen = event.isOpen) }
392- }
551+ })
393552 }
394553 }
395554}
0 commit comments