knock requests : refine and clean banner
This commit is contained in:
parent
85669e6d10
commit
ef3c056952
10 changed files with 205 additions and 157 deletions
|
|
@ -17,16 +17,13 @@ import javax.inject.Inject
|
|||
@ContributesBinding(RoomScope::class)
|
||||
class DefaultKnockRequestsBannerRenderer @Inject constructor(
|
||||
private val presenter: KnockRequestsBannerPresenter,
|
||||
): KnockRequestsBannerRenderer {
|
||||
|
||||
) : KnockRequestsBannerRenderer {
|
||||
@Composable
|
||||
override fun View(modifier: Modifier, onViewRequestsClick: () -> Unit) {
|
||||
val state = presenter.present()
|
||||
KnockRequestsBannerView(
|
||||
state = state,
|
||||
onDismissClick = {},
|
||||
onViewRequestsClick = onViewRequestsClick,
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.knockrequests.impl.banner
|
||||
|
||||
import io.element.android.features.knockrequests.impl.KnockRequest
|
||||
|
||||
sealed interface KnockRequestsBannerEvents {
|
||||
data class Accept(val knockRequest: KnockRequest) : KnockRequestsBannerEvents
|
||||
data object Dismiss : KnockRequestsBannerEvents
|
||||
}
|
||||
|
|
@ -8,12 +8,35 @@
|
|||
package io.element.android.features.knockrequests.impl.banner
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import javax.inject.Inject
|
||||
|
||||
class KnockRequestsBannerPresenter @Inject constructor(): Presenter<KnockRequestsBannerState> {
|
||||
class KnockRequestsBannerPresenter @Inject constructor() : Presenter<KnockRequestsBannerState> {
|
||||
@Composable
|
||||
override fun present(): KnockRequestsBannerState {
|
||||
return KnockRequestsBannerState.Hidden
|
||||
var shouldShowBanner by remember { mutableStateOf(false) }
|
||||
|
||||
fun handleEvents(event: KnockRequestsBannerEvents) {
|
||||
when (event) {
|
||||
is KnockRequestsBannerEvents.Accept -> Unit
|
||||
is KnockRequestsBannerEvents.Dismiss -> {
|
||||
shouldShowBanner = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return KnockRequestsBannerState(
|
||||
knockRequests = persistentListOf(),
|
||||
acceptAction = AsyncAction.Uninitialized,
|
||||
canAccept = false,
|
||||
isVisible = shouldShowBanner,
|
||||
eventSink = ::handleEvents,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@
|
|||
package io.element.android.features.knockrequests.impl.banner
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.ui.res.pluralStringResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import io.element.android.features.knockrequests.impl.KnockRequest
|
||||
|
|
@ -18,15 +17,13 @@ import io.element.android.libraries.ui.strings.CommonPlurals
|
|||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
@Immutable
|
||||
sealed interface KnockRequestsBannerState {
|
||||
data object Hidden : KnockRequestsBannerState
|
||||
data class Visible(
|
||||
data class KnockRequestsBannerState(
|
||||
val isVisible: Boolean,
|
||||
val knockRequests: ImmutableList<KnockRequest>,
|
||||
val acceptAction: AsyncAction<Unit>,
|
||||
val canAccept: Boolean,
|
||||
) : KnockRequestsBannerState {
|
||||
|
||||
val eventSink: (KnockRequestsBannerEvents) -> Unit,
|
||||
) {
|
||||
val subtitle = if (knockRequests.size == 1) {
|
||||
knockRequests.first().userId.value
|
||||
} else {
|
||||
|
|
@ -56,5 +53,4 @@ sealed interface KnockRequestsBannerState {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,15 +16,23 @@ import kotlinx.collections.immutable.toImmutableList
|
|||
class KnockRequestsBannerStateProvider : PreviewParameterProvider<KnockRequestsBannerState> {
|
||||
override val values: Sequence<KnockRequestsBannerState>
|
||||
get() = sequenceOf(
|
||||
KnockRequestsBannerState.Hidden,
|
||||
aVisibleKnockRequestsBannerState(),
|
||||
aVisibleKnockRequestsBannerState(
|
||||
aKnockRequestsBannerState(),
|
||||
aKnockRequestsBannerState(
|
||||
knockRequests = listOf(
|
||||
aKnockRequest(
|
||||
reason = "A very long reason that should probably be truncated, " +
|
||||
"but could be also expanded so you can see it over the lines, wow," +
|
||||
"very amazing reason, I know, right, I'm so good at writing reasons."
|
||||
)
|
||||
)
|
||||
),
|
||||
aKnockRequestsBannerState(
|
||||
knockRequests = listOf(
|
||||
aKnockRequest(),
|
||||
aKnockRequest(displayName = "Alice")
|
||||
)
|
||||
),
|
||||
aVisibleKnockRequestsBannerState(
|
||||
aKnockRequestsBannerState(
|
||||
knockRequests = listOf(
|
||||
aKnockRequest(),
|
||||
aKnockRequest(displayName = "Alice"),
|
||||
|
|
@ -32,24 +40,28 @@ class KnockRequestsBannerStateProvider : PreviewParameterProvider<KnockRequestsB
|
|||
aKnockRequest(displayName = "Charlie")
|
||||
)
|
||||
),
|
||||
aVisibleKnockRequestsBannerState(
|
||||
aKnockRequestsBannerState(
|
||||
canAccept = false
|
||||
),
|
||||
aVisibleKnockRequestsBannerState(
|
||||
aKnockRequestsBannerState(
|
||||
acceptAction = AsyncAction.Loading
|
||||
),
|
||||
aVisibleKnockRequestsBannerState(
|
||||
acceptAction = AsyncAction.Failure(Throwable())
|
||||
aKnockRequestsBannerState(
|
||||
acceptAction = AsyncAction.Failure(Throwable("Failed to accept knock"))
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fun aVisibleKnockRequestsBannerState(
|
||||
fun aKnockRequestsBannerState(
|
||||
knockRequests: List<KnockRequest> = listOf(aKnockRequest()),
|
||||
acceptAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
|
||||
canAccept: Boolean = true,
|
||||
) = KnockRequestsBannerState.Visible(
|
||||
isVisible: Boolean = true,
|
||||
eventSink: (KnockRequestsBannerEvents) -> Unit = {}
|
||||
) = KnockRequestsBannerState(
|
||||
knockRequests = knockRequests.toImmutableList(),
|
||||
acceptAction = acceptAction,
|
||||
canAccept = canAccept
|
||||
canAccept = canAccept,
|
||||
isVisible = isVisible,
|
||||
eventSink = eventSink,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@
|
|||
|
||||
package io.element.android.features.knockrequests.impl.banner
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.expandVertically
|
||||
import androidx.compose.animation.shrinkVertically
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
|
|
@ -27,6 +30,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.zIndex
|
||||
|
|
@ -52,36 +56,41 @@ private const val MAX_AVATAR_COUNT = 3
|
|||
@Composable
|
||||
fun KnockRequestsBannerView(
|
||||
state: KnockRequestsBannerState,
|
||||
onDismissClick: () -> Unit,
|
||||
onViewRequestsClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
when (state) {
|
||||
is KnockRequestsBannerState.Hidden -> Unit
|
||||
is KnockRequestsBannerState.Visible -> VisibleKnockRequestsBannerView(
|
||||
AnimatedVisibility(
|
||||
visible = state.isVisible,
|
||||
enter = expandVertically(),
|
||||
exit = shrinkVertically(),
|
||||
modifier = modifier,
|
||||
) {
|
||||
Surface(
|
||||
shape = MaterialTheme.shapes.small,
|
||||
color = ElementTheme.colors.bgCanvasDefaultLevel1,
|
||||
shadowElevation = 24.dp,
|
||||
modifier = Modifier.padding(16.dp),
|
||||
) {
|
||||
KnockRequestsBannerContent(
|
||||
state = state,
|
||||
onDismissClick = onDismissClick,
|
||||
onViewRequestsClick = onViewRequestsClick,
|
||||
modifier = modifier
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun VisibleKnockRequestsBannerView(
|
||||
state: KnockRequestsBannerState.Visible,
|
||||
onDismissClick: () -> Unit,
|
||||
private fun KnockRequestsBannerContent(
|
||||
state: KnockRequestsBannerState,
|
||||
onViewRequestsClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Surface(
|
||||
modifier.fillMaxWidth(),
|
||||
shape = MaterialTheme.shapes.small,
|
||||
color = ElementTheme.colors.bgCanvasDefaultLevel1,
|
||||
shadowElevation = 24.dp
|
||||
) {
|
||||
fun onDismissClick() {
|
||||
state.eventSink(KnockRequestsBannerEvents.Dismiss)
|
||||
}
|
||||
|
||||
Column(
|
||||
Modifier
|
||||
modifier
|
||||
.fillMaxWidth()
|
||||
.padding(all = 16.dp)
|
||||
) {
|
||||
|
|
@ -105,7 +114,7 @@ private fun VisibleKnockRequestsBannerView(
|
|||
}
|
||||
}
|
||||
Icon(
|
||||
modifier = Modifier.clickable(onClick = onDismissClick),
|
||||
modifier = Modifier.clickable(onClick = ::onDismissClick),
|
||||
imageVector = CompoundIcons.Close(),
|
||||
contentDescription = stringResource(CommonStrings.action_close)
|
||||
)
|
||||
|
|
@ -116,6 +125,8 @@ private fun VisibleKnockRequestsBannerView(
|
|||
text = state.reason,
|
||||
color = ElementTheme.colors.textPrimary,
|
||||
style = ElementTheme.typography.fontBodyMdRegular,
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
|
@ -145,7 +156,6 @@ private fun VisibleKnockRequestsBannerView(
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -197,8 +207,6 @@ private fun KnockRequestAvatarListView(
|
|||
internal fun KnockRequestsBannerViewPreview(@PreviewParameter(KnockRequestsBannerStateProvider::class) state: KnockRequestsBannerState) = ElementPreview {
|
||||
KnockRequestsBannerView(
|
||||
state = state,
|
||||
onDismissClick = {},
|
||||
onViewRequestsClick = {},
|
||||
modifier = Modifier.padding(16.dp)
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -238,9 +238,9 @@ class MessagesNode @AssistedInject constructor(
|
|||
onCreatePollClick = this::onCreatePollClick,
|
||||
onJoinCallClick = this::onJoinCallClick,
|
||||
onViewAllPinnedMessagesClick = this::onViewAllPinnedMessagesClick,
|
||||
knockRequestsBanner = { modifier ->
|
||||
knockRequestsBannerView = {
|
||||
knockRequestsBannerRenderer.View(
|
||||
modifier = modifier,
|
||||
modifier = Modifier,
|
||||
onViewRequestsClick = this::onViewKnockRequestsClick
|
||||
)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ fun MessagesView(
|
|||
onViewAllPinnedMessagesClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
forceJumpToBottomVisibility: Boolean = false,
|
||||
knockRequestsBanner: @Composable (Modifier) -> Unit,
|
||||
knockRequestsBannerView: @Composable () -> Unit,
|
||||
) {
|
||||
OnLifecycleEvent { _, event ->
|
||||
state.voiceMessageComposerState.eventSink(VoiceMessageComposerEvents.LifecycleEvent(event))
|
||||
|
|
@ -216,7 +216,7 @@ fun MessagesView(
|
|||
forceJumpToBottomVisibility = forceJumpToBottomVisibility,
|
||||
onJoinCallClick = onJoinCallClick,
|
||||
onViewAllPinnedMessagesClick = onViewAllPinnedMessagesClick,
|
||||
knockRequestsBanner = knockRequestsBanner,
|
||||
knockRequestsBannerView = knockRequestsBannerView,
|
||||
)
|
||||
},
|
||||
snackbarHost = {
|
||||
|
|
@ -286,7 +286,7 @@ private fun MessagesViewContent(
|
|||
forceJumpToBottomVisibility: Boolean,
|
||||
onSwipeToReply: (TimelineItem.Event) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
knockRequestsBanner: @Composable (Modifier) -> Unit,
|
||||
knockRequestsBannerView: @Composable () -> Unit,
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
|
|
@ -375,9 +375,7 @@ private fun MessagesViewContent(
|
|||
onViewAllClick = onViewAllPinnedMessagesClick,
|
||||
)
|
||||
}
|
||||
Box(modifier = Modifier.padding(all = 16.dp)) {
|
||||
knockRequestsBanner(Modifier)
|
||||
}
|
||||
knockRequestsBannerView()
|
||||
}
|
||||
},
|
||||
sheetContent = { subcomposing: Boolean ->
|
||||
|
|
@ -545,6 +543,6 @@ internal fun MessagesViewPreview(@PreviewParameter(MessagesStateProvider::class)
|
|||
onJoinCallClick = {},
|
||||
onViewAllPinnedMessagesClick = { },
|
||||
forceJumpToBottomVisibility = true,
|
||||
knockRequestsBanner = {},
|
||||
knockRequestsBannerView = {},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@ internal fun MessagesViewWithIdentityChangePreview(
|
|||
onCreatePollClick = {},
|
||||
onJoinCallClick = {},
|
||||
onViewAllPinnedMessagesClick = {},
|
||||
knockRequestsBanner = {}
|
||||
|
||||
knockRequestsBannerView = {}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -533,7 +533,7 @@ private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setMessa
|
|||
onCreatePollClick = onCreatePollClick,
|
||||
onJoinCallClick = onJoinCallClick,
|
||||
onViewAllPinnedMessagesClick = onViewAllPinnedMessagesClick,
|
||||
knockRequestsBanner = {}
|
||||
knockRequestsBannerView = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue