Merge pull request #3995 from element-hq/feature/fga/requests_to_join_list

feat(knock_requests_list) : implement design
This commit is contained in:
ganfra 2024-12-06 13:28:52 +01:00 committed by GitHub
commit eae73ac2b9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
49 changed files with 1031 additions and 20 deletions

View file

@ -22,6 +22,7 @@ import im.vector.app.features.analytics.plan.Interaction
import io.element.android.anvilannotations.ContributesNode
import io.element.android.features.call.api.CallType
import io.element.android.features.call.api.ElementCallEntryPoint
import io.element.android.features.knockrequests.api.list.KnockRequestsListEntryPoint
import io.element.android.features.messages.api.MessagesEntryPoint
import io.element.android.features.poll.api.history.PollHistoryEntryPoint
import io.element.android.features.roomdetails.api.RoomDetailsEntryPoint
@ -56,6 +57,7 @@ class RoomDetailsFlowNode @AssistedInject constructor(
private val room: MatrixRoom,
private val analyticsService: AnalyticsService,
private val messagesEntryPoint: MessagesEntryPoint,
private val knockRequestsListEntryPoint: KnockRequestsListEntryPoint,
private val mediaViewerEntryPoint: MediaViewerEntryPoint,
) : BaseFlowNode<RoomDetailsFlowNode.NavTarget>(
backstack = BackStack(
@ -101,6 +103,9 @@ class RoomDetailsFlowNode @AssistedInject constructor(
@Parcelize
data object PinnedMessagesList : NavTarget
@Parcelize
data object KnockRequestsList : NavTarget
}
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
@ -139,6 +144,10 @@ class RoomDetailsFlowNode @AssistedInject constructor(
backstack.push(NavTarget.PinnedMessagesList)
}
override fun openKnockRequestsList() {
backstack.push(NavTarget.KnockRequestsList)
}
override fun onJoinCall() {
val inputs = CallType.RoomCall(
sessionId = room.sessionId,
@ -243,6 +252,9 @@ class RoomDetailsFlowNode @AssistedInject constructor(
.callback(callback)
.build()
}
NavTarget.KnockRequestsList -> {
knockRequestsListEntryPoint.createNode(this, buildContext)
}
}
}

View file

@ -47,6 +47,7 @@ class RoomDetailsNode @AssistedInject constructor(
fun openPollHistory()
fun openAdminSettings()
fun openPinnedMessagesList()
fun openKnockRequestsList()
fun onJoinCall()
}
@ -111,6 +112,10 @@ class RoomDetailsNode @AssistedInject constructor(
callbacks.forEach { it.openPinnedMessagesList() }
}
private fun openKnockRequestsLists() {
callbacks.forEach { it.openKnockRequestsList() }
}
@Composable
override fun View(modifier: Modifier) {
val context = LocalContext.current
@ -140,7 +145,8 @@ class RoomDetailsNode @AssistedInject constructor(
openPollHistory = ::openPollHistory,
openAdminSettings = this::openAdminSettings,
onJoinCallClick = ::onJoinCall,
onPinnedMessagesClick = ::openPinnedMessages
onPinnedMessagesClick = ::openPinnedMessages,
onKnockRequestsClick = ::openKnockRequestsLists,
)
}
}

View file

@ -38,6 +38,7 @@ import io.element.android.libraries.matrix.api.room.isDm
import io.element.android.libraries.matrix.api.room.powerlevels.canInvite
import io.element.android.libraries.matrix.api.room.powerlevels.canSendState
import io.element.android.libraries.matrix.api.room.roomNotificationSettings
import io.element.android.libraries.matrix.ui.room.canHandleKnockRequestsAsState
import io.element.android.libraries.matrix.ui.room.getCurrentRoomMember
import io.element.android.libraries.matrix.ui.room.getDirectRoomMember
import io.element.android.libraries.matrix.ui.room.isOwnUserAdmin
@ -69,7 +70,7 @@ class RoomDetailsPresenter @Inject constructor(
val canShowNotificationSettings = remember { mutableStateOf(false) }
val roomInfo by room.roomInfoFlow.collectAsState(initial = null)
val isUserAdmin = room.isOwnUserAdmin()
val syncUpdateFlow = room.syncUpdateFlow.collectAsState()
val roomAvatar by remember { derivedStateOf { roomInfo?.avatarUrl ?: room.avatarUrl } }
val roomName by remember { derivedStateOf { (roomInfo?.name ?: room.displayName).trim() } }
@ -90,6 +91,7 @@ class RoomDetailsPresenter @Inject constructor(
val membersState by room.membersStateFlow.collectAsState()
val canInvite by getCanInvite(membersState)
val canEditName by getCanSendState(membersState, StateEventType.ROOM_NAME)
val canEditAvatar by getCanSendState(membersState, StateEventType.ROOM_AVATAR)
val canEditTopic by getCanSendState(membersState, StateEventType.ROOM_TOPIC)
@ -99,6 +101,8 @@ class RoomDetailsPresenter @Inject constructor(
val roomType by getRoomType(dmMember, currentMember)
val roomCallState = roomCallStatePresenter.present()
val canHandleKnockRequests by room.canHandleKnockRequestsAsState(syncUpdateFlow.value)
val topicState = remember(canEditTopic, roomTopic, roomType) {
val topic = roomTopic
@ -109,6 +113,12 @@ class RoomDetailsPresenter @Inject constructor(
}
}
val isKnockRequestsEnabled by featureFlagService.isFeatureEnabledFlow(FeatureFlags.Knock).collectAsState(false)
val knockRequestsCount by remember { mutableStateOf(null) }
val canShowKnockRequests by remember {
derivedStateOf { isKnockRequestsEnabled && canHandleKnockRequests }
}
val roomNotificationSettingsState by room.roomNotificationSettingsStateFlow.collectAsState()
fun handleEvents(event: RoomDetailsEvent) {
@ -153,6 +163,8 @@ class RoomDetailsPresenter @Inject constructor(
heroes = roomInfo?.heroes.orEmpty().toPersistentList(),
canShowPinnedMessages = canShowPinnedMessages,
pinnedMessagesCount = pinnedMessagesCount,
canShowKnockRequests = canShowKnockRequests,
knockRequestsCount = knockRequestsCount,
eventSink = ::handleEvents,
)
}

View file

@ -41,6 +41,8 @@ data class RoomDetailsState(
val heroes: ImmutableList<MatrixUser>,
val canShowPinnedMessages: Boolean,
val pinnedMessagesCount: Int?,
val canShowKnockRequests: Boolean,
val knockRequestsCount: Int?,
val eventSink: (RoomDetailsEvent) -> Unit
) {
val roomBadges = buildList {

View file

@ -102,6 +102,8 @@ fun aRoomDetailsState(
heroes: List<MatrixUser> = emptyList(),
canShowPinnedMessages: Boolean = true,
pinnedMessagesCount: Int? = null,
canShowKnockRequests: Boolean = false,
knockRequestsCount: Int? = null,
eventSink: (RoomDetailsEvent) -> Unit = {},
) = RoomDetailsState(
roomId = roomId,
@ -125,6 +127,8 @@ fun aRoomDetailsState(
heroes = heroes.toPersistentList(),
canShowPinnedMessages = canShowPinnedMessages,
pinnedMessagesCount = pinnedMessagesCount,
canShowKnockRequests = canShowKnockRequests,
knockRequestsCount = knockRequestsCount,
eventSink = eventSink
)

View file

@ -104,6 +104,7 @@ fun RoomDetailsView(
openAdminSettings: () -> Unit,
onJoinCallClick: () -> Unit,
onPinnedMessagesClick: () -> Unit,
onKnockRequestsClick: () -> Unit,
modifier: Modifier = Modifier,
) {
Scaffold(
@ -206,6 +207,12 @@ fun RoomDetailsView(
memberCount = state.memberCount,
openRoomMemberList = openRoomMemberList,
)
if (state.canShowKnockRequests) {
KnockRequestsItem(
knockRequestsCount = state.knockRequestsCount,
onKnockRequestsClick = onKnockRequestsClick
)
}
}
}
@ -231,6 +238,20 @@ fun RoomDetailsView(
}
}
@Composable
private fun KnockRequestsItem(knockRequestsCount: Int?, onKnockRequestsClick: () -> Unit) {
ListItem(
headlineContent = { Text(stringResource(R.string.screen_room_details_requests_to_join_title)) },
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Notifications())),
trailingContent = if (knockRequestsCount == null || knockRequestsCount == 0) {
null
} else {
ListItemContent.Text(knockRequestsCount.toString())
},
onClick = onKnockRequestsClick,
)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun RoomDetailsTopBar(
@ -525,7 +546,7 @@ private fun PinnedMessagesItem(
) {
val analyticsService = LocalAnalyticsService.current
ListItem(
headlineContent = { Text(stringResource(CommonStrings.screen_room_details_pinned_events_row_title)) },
headlineContent = { Text(stringResource(R.string.screen_room_details_pinned_events_row_title)) },
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Pin())),
trailingContent =
if (pinnedMessagesCount == null) {
@ -613,5 +634,6 @@ private fun ContentToPreview(state: RoomDetailsState) {
openAdminSettings = {},
onJoinCallClick = {},
onPinnedMessagesClick = {},
onKnockRequestsClick = {},
)
}

View file

@ -49,9 +49,12 @@
<string name="screen_room_details_invite_people_title">"Invite people"</string>
<string name="screen_room_details_leave_conversation_title">"Leave conversation"</string>
<string name="screen_room_details_leave_room_title">"Leave room"</string>
<string name="screen_room_details_media_gallery_title">"Media and files"</string>
<string name="screen_room_details_notification_mode_custom">"Custom"</string>
<string name="screen_room_details_notification_mode_default">"Default"</string>
<string name="screen_room_details_notification_title">"Notifications"</string>
<string name="screen_room_details_pinned_events_row_title">"Pinned messages"</string>
<string name="screen_room_details_requests_to_join_title">"Requests to join"</string>
<string name="screen_room_details_roles_and_permissions">"Roles and permissions"</string>
<string name="screen_room_details_room_name_label">"Room name"</string>
<string name="screen_room_details_security_title">"Security"</string>

View file

@ -129,7 +129,7 @@ class RoomDetailsViewTest {
),
onPinnedMessagesClick = callback,
)
rule.clickOn(CommonStrings.screen_room_details_pinned_events_row_title)
rule.clickOn(R.string.screen_room_details_pinned_events_row_title)
}
}
@ -253,6 +253,21 @@ class RoomDetailsViewTest {
rule.clickOn(R.string.screen_room_details_leave_room_title)
eventsRecorder.assertSingle(RoomDetailsEvent.LeaveRoom)
}
@Config(qualifiers = "h1024dp")
@Test
fun `click on knock requests invokes expected callback`() {
ensureCalledOnce { callback ->
rule.setRoomDetailView(
state = aRoomDetailsState(
eventSink = EventsRecorder(expectEvents = false),
canShowKnockRequests = true,
),
onKnockRequestsClick = callback,
)
rule.clickOn(R.string.screen_room_details_requests_to_join_title)
}
}
}
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setRoomDetailView(
@ -270,6 +285,7 @@ private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setRoomD
openAdminSettings: () -> Unit = EnsureNeverCalled(),
onJoinCallClick: () -> Unit = EnsureNeverCalled(),
onPinnedMessagesClick: () -> Unit = EnsureNeverCalled(),
onKnockRequestsClick: () -> Unit = EnsureNeverCalled(),
) {
setContent {
RoomDetailsView(
@ -285,6 +301,7 @@ private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setRoomD
openAdminSettings = openAdminSettings,
onJoinCallClick = onJoinCallClick,
onPinnedMessagesClick = onPinnedMessagesClick,
onKnockRequestsClick = onKnockRequestsClick,
)
}
}