Merge remote-tracking branch 'origin/develop' into bma/brandColorFix
This commit is contained in:
@@ -56,6 +56,7 @@ import io.element.android.features.ftue.api.state.FtueState
|
||||
import io.element.android.features.home.api.HomeEntryPoint
|
||||
import io.element.android.features.networkmonitor.api.NetworkMonitor
|
||||
import io.element.android.features.networkmonitor.api.NetworkStatus
|
||||
import io.element.android.features.networkmonitor.api.ui.ConnectivityIndicatorContainer
|
||||
import io.element.android.features.preferences.api.PreferencesEntryPoint
|
||||
import io.element.android.features.roomdirectory.api.RoomDescription
|
||||
import io.element.android.features.roomdirectory.api.RoomDirectoryEntryPoint
|
||||
@@ -82,6 +83,7 @@ import io.element.android.libraries.matrix.api.core.RoomIdOrAlias
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias
|
||||
import io.element.android.libraries.matrix.api.permalink.PermalinkData
|
||||
import io.element.android.libraries.matrix.api.sync.SyncService
|
||||
import io.element.android.libraries.matrix.api.verification.SessionVerificationServiceListener
|
||||
import io.element.android.libraries.matrix.api.verification.VerificationRequest
|
||||
import io.element.android.libraries.preferences.api.store.AppPreferencesStore
|
||||
@@ -128,6 +130,7 @@ class LoggedInFlowNode(
|
||||
private val sessionEnterpriseService: SessionEnterpriseService,
|
||||
private val networkMonitor: NetworkMonitor,
|
||||
private val notificationConversationService: NotificationConversationService,
|
||||
private val syncService: SyncService,
|
||||
private val enterpriseService: EnterpriseService,
|
||||
private val appPreferencesStore: AppPreferencesStore,
|
||||
private val buildMeta: BuildMeta,
|
||||
@@ -556,11 +559,17 @@ class LoggedInFlowNode(
|
||||
compoundDark = colors.dark,
|
||||
buildMeta = buildMeta,
|
||||
) {
|
||||
Box(modifier = modifier) {
|
||||
val ftueState by ftueService.state.collectAsState()
|
||||
BackstackView()
|
||||
if (ftueState is FtueState.Complete) {
|
||||
PermanentChild(permanentNavModel = permanentNavModel, navTarget = NavTarget.LoggedInPermanent)
|
||||
val isOnline by syncService.isOnline.collectAsState()
|
||||
ConnectivityIndicatorContainer(
|
||||
isOnline = isOnline,
|
||||
modifier = modifier,
|
||||
) { contentModifier ->
|
||||
Box(modifier = contentModifier) {
|
||||
val ftueState by ftueService.state.collectAsState()
|
||||
BackstackView()
|
||||
if (ftueState is FtueState.Complete) {
|
||||
PermanentChild(permanentNavModel = permanentNavModel, navTarget = NavTarget.LoggedInPermanent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ package io.element.android.appnav.room
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
@@ -48,7 +47,6 @@ import io.element.android.libraries.matrix.api.core.RoomIdOrAlias
|
||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
|
||||
import io.element.android.libraries.matrix.api.room.alias.ResolvedRoomAlias
|
||||
import io.element.android.libraries.matrix.api.sync.SyncService
|
||||
import io.element.android.libraries.matrix.ui.room.LoadingRoomState
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.combine
|
||||
@@ -71,7 +69,6 @@ class RoomFlowNode(
|
||||
private val client: MatrixClient,
|
||||
private val joinRoomEntryPoint: JoinRoomEntryPoint,
|
||||
private val roomAliasResolverEntryPoint: RoomAliasResolverEntryPoint,
|
||||
private val syncService: SyncService,
|
||||
private val membershipObserver: RoomMembershipObserver,
|
||||
private val spaceEntryPoint: SpaceEntryPoint,
|
||||
) : BaseFlowNode<RoomFlowNode.NavTarget>(
|
||||
@@ -222,10 +219,8 @@ class RoomFlowNode(
|
||||
}
|
||||
|
||||
private fun loadingNode(buildContext: BuildContext) = node(buildContext) { modifier ->
|
||||
val isOnline by syncService.isOnline.collectAsState()
|
||||
LoadingRoomNodeView(
|
||||
state = LoadingRoomState.Loading,
|
||||
hasNetworkConnection = isOnline,
|
||||
onBackClick = { navigateUp() },
|
||||
modifier = modifier,
|
||||
)
|
||||
|
||||
@@ -35,7 +35,6 @@ import io.element.android.libraries.architecture.createNode
|
||||
import io.element.android.libraries.architecture.inputs
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.sync.SyncService
|
||||
import io.element.android.libraries.matrix.ui.room.LoadingRoomState
|
||||
import io.element.android.libraries.matrix.ui.room.LoadingRoomStateFlowFactory
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
@@ -50,7 +49,6 @@ class JoinedRoomFlowNode(
|
||||
@Assisted val buildContext: BuildContext,
|
||||
@Assisted plugins: List<Plugin>,
|
||||
loadingRoomStateFlowFactory: LoadingRoomStateFlowFactory,
|
||||
private val syncService: SyncService,
|
||||
) :
|
||||
BaseFlowNode<JoinedRoomFlowNode.NavTarget>(
|
||||
backstack = BackStack(
|
||||
@@ -116,12 +114,10 @@ class JoinedRoomFlowNode(
|
||||
|
||||
private fun loadingNode(buildContext: BuildContext, onBackClick: () -> Unit) = node(buildContext) { modifier ->
|
||||
val loadingRoomState by loadingRoomStateStateFlow.collectAsState()
|
||||
val isOnline by syncService.isOnline.collectAsState()
|
||||
LoadingRoomNodeView(
|
||||
state = loadingRoomState,
|
||||
hasNetworkConnection = isOnline,
|
||||
modifier = modifier,
|
||||
onBackClick = onBackClick
|
||||
onBackClick = onBackClick,
|
||||
modifier = modifier
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
package io.element.android.appnav.room.joined
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.consumeWindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
@@ -21,7 +19,6 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.features.networkmonitor.api.ui.ConnectivityIndicatorView
|
||||
import io.element.android.libraries.designsystem.atomic.molecules.IconTitlePlaceholdersRowMolecule
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||
import io.element.android.libraries.designsystem.components.button.BackButton
|
||||
@@ -38,17 +35,13 @@ import io.element.android.libraries.ui.strings.CommonStrings
|
||||
@Composable
|
||||
fun LoadingRoomNodeView(
|
||||
state: LoadingRoomState,
|
||||
hasNetworkConnection: Boolean,
|
||||
onBackClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Scaffold(
|
||||
modifier = modifier,
|
||||
topBar = {
|
||||
Column {
|
||||
ConnectivityIndicatorView(isOnline = hasNetworkConnection)
|
||||
LoadingRoomTopBar(onBackClick)
|
||||
}
|
||||
LoadingRoomTopBar(onBackClick)
|
||||
},
|
||||
content = { padding ->
|
||||
Box(
|
||||
@@ -85,7 +78,6 @@ private fun LoadingRoomTopBar(
|
||||
title = {
|
||||
IconTitlePlaceholdersRowMolecule(iconSize = AvatarSize.TimelineRoom.dp)
|
||||
},
|
||||
windowInsets = WindowInsets(0.dp),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -94,7 +86,6 @@ private fun LoadingRoomTopBar(
|
||||
internal fun LoadingRoomNodeViewPreview(@PreviewParameter(LoadingRoomStateProvider::class) state: LoadingRoomState) = ElementPreview {
|
||||
LoadingRoomNodeView(
|
||||
state = state,
|
||||
onBackClick = {},
|
||||
hasNetworkConnection = false
|
||||
onBackClick = {}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ import androidx.compose.foundation.layout.calculateStartPadding
|
||||
import androidx.compose.foundation.layout.consumeWindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.statusBarsPadding
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
@@ -50,7 +49,6 @@ import io.element.android.features.home.impl.roomlist.RoomListEvents
|
||||
import io.element.android.features.home.impl.roomlist.RoomListState
|
||||
import io.element.android.features.home.impl.search.RoomListSearchView
|
||||
import io.element.android.features.home.impl.spaces.HomeSpacesView
|
||||
import io.element.android.features.networkmonitor.api.ui.ConnectivityIndicatorContainer
|
||||
import io.element.android.libraries.androidutils.throttler.FirstThrottler
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
@@ -84,56 +82,47 @@ fun HomeView(
|
||||
val state: RoomListState = homeState.roomListState
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val firstThrottler = remember { FirstThrottler(300, coroutineScope) }
|
||||
|
||||
ConnectivityIndicatorContainer(
|
||||
modifier = modifier,
|
||||
isOnline = homeState.hasNetworkConnection,
|
||||
) { topPadding ->
|
||||
Box {
|
||||
if (state.contextMenu is RoomListState.ContextMenu.Shown) {
|
||||
RoomListContextMenu(
|
||||
contextMenu = state.contextMenu,
|
||||
canReportRoom = state.canReportRoom,
|
||||
eventSink = state.eventSink,
|
||||
onRoomSettingsClick = onRoomSettingsClick,
|
||||
onReportRoomClick = onReportRoomClick,
|
||||
)
|
||||
}
|
||||
if (state.declineInviteMenu is RoomListState.DeclineInviteMenu.Shown) {
|
||||
RoomListDeclineInviteMenu(
|
||||
menu = state.declineInviteMenu,
|
||||
canReportRoom = state.canReportRoom,
|
||||
eventSink = state.eventSink,
|
||||
onDeclineAndBlockClick = onDeclineInviteAndBlockUser,
|
||||
)
|
||||
}
|
||||
|
||||
leaveRoomView()
|
||||
|
||||
HomeScaffold(
|
||||
state = homeState,
|
||||
onSetUpRecoveryClick = onSetUpRecoveryClick,
|
||||
onConfirmRecoveryKeyClick = onConfirmRecoveryKeyClick,
|
||||
onRoomClick = { if (firstThrottler.canHandle()) onRoomClick(it) },
|
||||
onOpenSettings = { if (firstThrottler.canHandle()) onSettingsClick() },
|
||||
onStartChatClick = { if (firstThrottler.canHandle()) onStartChatClick() },
|
||||
onMenuActionClick = onMenuActionClick,
|
||||
modifier = Modifier.padding(top = topPadding),
|
||||
)
|
||||
// This overlaid view will only be visible when state.displaySearchResults is true
|
||||
RoomListSearchView(
|
||||
state = state.searchState,
|
||||
Box(modifier) {
|
||||
if (state.contextMenu is RoomListState.ContextMenu.Shown) {
|
||||
RoomListContextMenu(
|
||||
contextMenu = state.contextMenu,
|
||||
canReportRoom = state.canReportRoom,
|
||||
eventSink = state.eventSink,
|
||||
hideInvitesAvatars = state.hideInvitesAvatars,
|
||||
onRoomClick = { if (firstThrottler.canHandle()) onRoomClick(it) },
|
||||
modifier = Modifier
|
||||
.statusBarsPadding()
|
||||
.padding(top = topPadding)
|
||||
.fillMaxSize()
|
||||
.background(ElementTheme.colors.bgCanvasDefault)
|
||||
onRoomSettingsClick = onRoomSettingsClick,
|
||||
onReportRoomClick = onReportRoomClick,
|
||||
)
|
||||
acceptDeclineInviteView()
|
||||
}
|
||||
if (state.declineInviteMenu is RoomListState.DeclineInviteMenu.Shown) {
|
||||
RoomListDeclineInviteMenu(
|
||||
menu = state.declineInviteMenu,
|
||||
canReportRoom = state.canReportRoom,
|
||||
eventSink = state.eventSink,
|
||||
onDeclineAndBlockClick = onDeclineInviteAndBlockUser,
|
||||
)
|
||||
}
|
||||
|
||||
leaveRoomView()
|
||||
|
||||
HomeScaffold(
|
||||
state = homeState,
|
||||
onSetUpRecoveryClick = onSetUpRecoveryClick,
|
||||
onConfirmRecoveryKeyClick = onConfirmRecoveryKeyClick,
|
||||
onRoomClick = { if (firstThrottler.canHandle()) onRoomClick(it) },
|
||||
onOpenSettings = { if (firstThrottler.canHandle()) onSettingsClick() },
|
||||
onStartChatClick = { if (firstThrottler.canHandle()) onStartChatClick() },
|
||||
onMenuActionClick = onMenuActionClick,
|
||||
)
|
||||
// This overlaid view will only be visible when state.displaySearchResults is true
|
||||
RoomListSearchView(
|
||||
state = state.searchState,
|
||||
eventSink = state.eventSink,
|
||||
hideInvitesAvatars = state.hideInvitesAvatars,
|
||||
onRoomClick = { if (firstThrottler.canHandle()) onRoomClick(it) },
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(ElementTheme.colors.bgCanvasDefault)
|
||||
)
|
||||
acceptDeclineInviteView()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@ import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.TextFieldDefaults
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
@@ -51,7 +50,6 @@ import io.element.android.libraries.designsystem.theme.components.Icon
|
||||
import io.element.android.libraries.designsystem.theme.components.IconButton
|
||||
import io.element.android.libraries.designsystem.theme.components.Scaffold
|
||||
import io.element.android.libraries.designsystem.theme.components.TopAppBar
|
||||
import io.element.android.libraries.designsystem.utils.copy
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
@@ -157,7 +155,6 @@ private fun RoomListSearchContent(
|
||||
focusRequester.saveFocusedChild()
|
||||
}
|
||||
},
|
||||
windowInsets = TopAppBarDefaults.windowInsets.copy(top = 0)
|
||||
)
|
||||
}
|
||||
) { padding ->
|
||||
|
||||
@@ -81,7 +81,6 @@ import io.element.android.libraries.matrix.api.room.powerlevels.canPinUnpin
|
||||
import io.element.android.libraries.matrix.api.room.powerlevels.canRedactOther
|
||||
import io.element.android.libraries.matrix.api.room.powerlevels.canRedactOwn
|
||||
import io.element.android.libraries.matrix.api.room.powerlevels.canSendMessage
|
||||
import io.element.android.libraries.matrix.api.sync.SyncService
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId
|
||||
import io.element.android.libraries.matrix.ui.messages.reply.map
|
||||
import io.element.android.libraries.matrix.ui.model.getAvatarData
|
||||
@@ -112,7 +111,6 @@ class MessagesPresenter(
|
||||
private val pinnedMessagesBannerPresenter: Presenter<PinnedMessagesBannerState>,
|
||||
private val roomCallStatePresenter: Presenter<RoomCallState>,
|
||||
private val roomMemberModerationPresenter: Presenter<RoomMemberModerationState>,
|
||||
private val syncService: SyncService,
|
||||
private val snackbarDispatcher: SnackbarDispatcher,
|
||||
private val dispatchers: CoroutineDispatchers,
|
||||
private val clipboardHelper: ClipboardHelper,
|
||||
@@ -193,7 +191,6 @@ class MessagesPresenter(
|
||||
showReinvitePrompt = !hasDismissedInviteDialog && composerHasFocus && roomInfo.isDm && roomInfo.activeMembersCount == 1L
|
||||
}
|
||||
}
|
||||
val isOnline by syncService.isOnline.collectAsState()
|
||||
|
||||
val snackbarMessage by snackbarDispatcher.collectSnackbarMessageAsState()
|
||||
|
||||
@@ -250,8 +247,8 @@ class MessagesPresenter(
|
||||
roomName = roomInfo.name,
|
||||
roomAvatar = roomAvatar,
|
||||
heroes = heroes,
|
||||
composerState = composerState,
|
||||
userEventPermissions = userEventPermissions,
|
||||
composerState = composerState,
|
||||
voiceMessageComposerState = voiceMessageComposerState,
|
||||
timelineState = timelineState,
|
||||
timelineProtectionState = timelineProtectionState,
|
||||
@@ -261,19 +258,17 @@ class MessagesPresenter(
|
||||
customReactionState = customReactionState,
|
||||
reactionSummaryState = reactionSummaryState,
|
||||
readReceiptBottomSheetState = readReceiptBottomSheetState,
|
||||
hasNetworkConnection = isOnline,
|
||||
snackbarMessage = snackbarMessage,
|
||||
showReinvitePrompt = showReinvitePrompt,
|
||||
inviteProgress = inviteProgress.value,
|
||||
showReinvitePrompt = showReinvitePrompt,
|
||||
enableTextFormatting = MessageComposerConfig.ENABLE_RICH_TEXT_EDITING,
|
||||
appName = buildMeta.applicationName,
|
||||
roomCallState = roomCallState,
|
||||
appName = buildMeta.applicationName,
|
||||
pinnedMessagesBannerState = pinnedMessagesBannerState,
|
||||
dmUserVerificationState = dmUserVerificationState,
|
||||
roomMemberModerationState = roomMemberModerationState,
|
||||
successorRoom = roomInfo.successorRoom,
|
||||
eventSink = { handleEvents(it) }
|
||||
)
|
||||
successorRoom = roomInfo.successorRoom
|
||||
) { handleEvents(it) }
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
||||
@@ -44,7 +44,6 @@ data class MessagesState(
|
||||
val customReactionState: CustomReactionState,
|
||||
val reactionSummaryState: ReactionSummaryState,
|
||||
val readReceiptBottomSheetState: ReadReceiptBottomSheetState,
|
||||
val hasNetworkConnection: Boolean,
|
||||
val snackbarMessage: SnackbarMessage?,
|
||||
val inviteProgress: AsyncData<Unit>,
|
||||
val showReinvitePrompt: Boolean,
|
||||
|
||||
@@ -56,7 +56,6 @@ open class MessagesStateProvider : PreviewParameterProvider<MessagesState> {
|
||||
override val values: Sequence<MessagesState>
|
||||
get() = sequenceOf(
|
||||
aMessagesState(),
|
||||
aMessagesState(hasNetworkConnection = false),
|
||||
aMessagesState(composerState = aMessageComposerState(showAttachmentSourcePicker = true)),
|
||||
aMessagesState(userEventPermissions = aUserEventPermissions(canSendMessage = false)),
|
||||
aMessagesState(showReinvitePrompt = true),
|
||||
@@ -108,7 +107,6 @@ fun aMessagesState(
|
||||
actionListState: ActionListState = anActionListState(),
|
||||
customReactionState: CustomReactionState = aCustomReactionState(),
|
||||
reactionSummaryState: ReactionSummaryState = aReactionSummaryState(),
|
||||
hasNetworkConnection: Boolean = true,
|
||||
showReinvitePrompt: Boolean = false,
|
||||
roomCallState: RoomCallState = aStandByCallState(),
|
||||
pinnedMessagesBannerState: PinnedMessagesBannerState = aLoadedPinnedMessagesBannerState(),
|
||||
@@ -132,7 +130,6 @@ fun aMessagesState(
|
||||
actionListState = actionListState,
|
||||
customReactionState = customReactionState,
|
||||
reactionSummaryState = reactionSummaryState,
|
||||
hasNetworkConnection = hasNetworkConnection,
|
||||
snackbarMessage = null,
|
||||
inviteProgress = AsyncData.Uninitialized,
|
||||
showReinvitePrompt = showReinvitePrompt,
|
||||
|
||||
@@ -71,7 +71,6 @@ import io.element.android.features.messages.impl.topbars.MessagesViewTopBar
|
||||
import io.element.android.features.messages.impl.topbars.ThreadTopBar
|
||||
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessagePermissionRationaleDialog
|
||||
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageSendingFailedDialog
|
||||
import io.element.android.features.networkmonitor.api.ui.ConnectivityIndicatorView
|
||||
import io.element.android.libraries.androidutils.ui.hideKeyboard
|
||||
import io.element.android.libraries.designsystem.atomic.molecules.ComposerAlertMolecule
|
||||
import io.element.android.libraries.designsystem.components.ExpandableBottomSheetLayout
|
||||
@@ -84,6 +83,7 @@ import io.element.android.libraries.designsystem.text.toAnnotatedString
|
||||
import io.element.android.libraries.designsystem.theme.components.BottomSheetDragHandle
|
||||
import io.element.android.libraries.designsystem.theme.components.Scaffold
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.designsystem.utils.HideKeyboardWhenDisposed
|
||||
import io.element.android.libraries.designsystem.utils.KeepScreenOn
|
||||
import io.element.android.libraries.designsystem.utils.OnLifecycleEvent
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarHost
|
||||
@@ -123,6 +123,8 @@ fun MessagesView(
|
||||
|
||||
KeepScreenOn(state.voiceMessageComposerState.keepScreenOn)
|
||||
|
||||
HideKeyboardWhenDisposed()
|
||||
|
||||
val snackbarHostState = rememberSnackbarHostState(snackbarMessage = state.snackbarMessage)
|
||||
|
||||
// This is needed because the composer is inside an AndroidView that can't be affected by the FocusManager in Compose
|
||||
@@ -180,8 +182,6 @@ fun MessagesView(
|
||||
Scaffold(
|
||||
contentWindowInsets = WindowInsets.statusBars,
|
||||
topBar = {
|
||||
Column {
|
||||
ConnectivityIndicatorView(isOnline = state.hasNetworkConnection)
|
||||
if (state.timelineState.timelineMode is Timeline.Mode.Thread) {
|
||||
ThreadTopBar(
|
||||
roomName = state.roomName,
|
||||
@@ -203,7 +203,6 @@ fun MessagesView(
|
||||
onJoinCallClick = onJoinCallClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
content = { padding ->
|
||||
Box(
|
||||
|
||||
@@ -84,7 +84,6 @@ import io.element.android.libraries.matrix.test.room.FakeBaseRoom
|
||||
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
|
||||
import io.element.android.libraries.matrix.test.room.aRoomInfo
|
||||
import io.element.android.libraries.matrix.test.room.aRoomMember
|
||||
import io.element.android.libraries.matrix.test.sync.FakeSyncService
|
||||
import io.element.android.libraries.matrix.test.timeline.FakeTimeline
|
||||
import io.element.android.libraries.matrix.test.timeline.aTimelineItemDebugInfo
|
||||
import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetails
|
||||
@@ -130,7 +129,6 @@ class MessagesPresenterTest {
|
||||
.isEqualTo(AvatarData(id = A_ROOM_ID.value, name = "", url = AN_AVATAR_URL, size = AvatarSize.TimelineRoom))
|
||||
assertThat(initialState.userEventPermissions.canSendMessage).isTrue()
|
||||
assertThat(initialState.userEventPermissions.canRedactOwn).isTrue()
|
||||
assertThat(initialState.hasNetworkConnection).isTrue()
|
||||
assertThat(initialState.snackbarMessage).isNull()
|
||||
assertThat(initialState.inviteProgress).isEqualTo(AsyncData.Uninitialized)
|
||||
assertThat(initialState.showReinvitePrompt).isFalse()
|
||||
@@ -1274,31 +1272,30 @@ class MessagesPresenterTest {
|
||||
addRecentEmoji: AddRecentEmoji = AddRecentEmoji(FakeMatrixClient(), testCoroutineDispatchers()),
|
||||
): MessagesPresenter {
|
||||
return MessagesPresenter(
|
||||
navigator = navigator,
|
||||
room = joinedRoom,
|
||||
composerPresenter = messageComposerPresenter,
|
||||
voiceMessageComposerPresenterFactory = FakeDefaultVoiceMessageComposerPresenterFactory(backgroundScope),
|
||||
timelinePresenter = { aTimelineState(eventSink = timelineEventSink) },
|
||||
timelineProtectionPresenter = { aTimelineProtectionState() },
|
||||
identityChangeStatePresenter = { anIdentityChangeState() },
|
||||
linkPresenter = { aLinkState() },
|
||||
actionListPresenter = { anActionListState(eventSink = actionListEventSink) },
|
||||
customReactionPresenter = { aCustomReactionState() },
|
||||
reactionSummaryPresenter = { aReactionSummaryState() },
|
||||
readReceiptBottomSheetPresenter = { aReadReceiptBottomSheetState() },
|
||||
identityChangeStatePresenter = { anIdentityChangeState() },
|
||||
linkPresenter = { aLinkState() },
|
||||
pinnedMessagesBannerPresenter = { aLoadedPinnedMessagesBannerState() },
|
||||
roomCallStatePresenter = { aStandByCallState() },
|
||||
roomMemberModerationPresenter = roomMemberModerationPresenter,
|
||||
syncService = FakeSyncService(),
|
||||
snackbarDispatcher = SnackbarDispatcher(),
|
||||
navigator = navigator,
|
||||
clipboardHelper = clipboardHelper,
|
||||
buildMeta = aBuildMeta(),
|
||||
dispatchers = coroutineDispatchers,
|
||||
clipboardHelper = clipboardHelper,
|
||||
htmlConverterProvider = FakeHtmlConverterProvider(),
|
||||
buildMeta = aBuildMeta(),
|
||||
timelineController = TimelineController(joinedRoom, timeline),
|
||||
permalinkParser = permalinkParser,
|
||||
encryptionService = encryptionService,
|
||||
analyticsService = analyticsService,
|
||||
encryptionService = encryptionService,
|
||||
featureFlagService = featureFlagService,
|
||||
addRecentEmoji = addRecentEmoji,
|
||||
)
|
||||
|
||||
@@ -20,6 +20,7 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
@@ -32,7 +33,8 @@ import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
@Composable
|
||||
internal fun Indicator(
|
||||
internal fun ConnectivityIndicator(
|
||||
verticalPadding: Dp,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Row(
|
||||
@@ -40,7 +42,7 @@ internal fun Indicator(
|
||||
.fillMaxWidth()
|
||||
.background(ElementTheme.colors.bgSubtlePrimary)
|
||||
.statusBarsPadding()
|
||||
.padding(vertical = 6.dp),
|
||||
.padding(vertical = verticalPadding),
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
@@ -61,6 +63,6 @@ internal fun Indicator(
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun IndicatorPreview() = ElementPreview {
|
||||
Indicator()
|
||||
internal fun ConnectivityIndicatorPreview() = ElementPreview {
|
||||
ConnectivityIndicator(verticalPadding = 6.dp)
|
||||
}
|
||||
@@ -16,55 +16,58 @@ import androidx.compose.animation.expandVertically
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.shrinkVertically
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.asPaddingValues
|
||||
import androidx.compose.foundation.layout.consumeWindowInsets
|
||||
import androidx.compose.foundation.layout.statusBars
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalInspectionMode
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
private val INDICATOR_VERTICAL_PADDING = 6.dp
|
||||
|
||||
/**
|
||||
* A view that displays a connectivity indicator when the device is offline, passing the padding
|
||||
* needed to make sure the status bar is not overlapped to its content views.
|
||||
* A view that displays a connectivity indicator when the device is offline.
|
||||
*/
|
||||
@Composable
|
||||
fun ConnectivityIndicatorContainer(
|
||||
isOnline: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
content: @Composable (topPadding: Dp) -> Unit = {},
|
||||
content: @Composable (Modifier) -> Unit = {},
|
||||
) {
|
||||
val isIndicatorVisible = remember { MutableTransitionState(!isOnline) }.apply { targetState = !isOnline }
|
||||
|
||||
val statusBarTopPadding = if (LocalInspectionMode.current) {
|
||||
// Needed to get valid UI previews
|
||||
24.dp
|
||||
} else {
|
||||
WindowInsets.statusBars.asPaddingValues().calculateTopPadding() + 6.dp
|
||||
}
|
||||
val target = remember(isIndicatorVisible.targetState, statusBarTopPadding) {
|
||||
if (!isIndicatorVisible.targetState) 0.dp else statusBarTopPadding
|
||||
}
|
||||
val animationStateOffset by animateDpAsState(
|
||||
targetValue = target,
|
||||
animationSpec = spring(
|
||||
stiffness = Spring.StiffnessMediumLow,
|
||||
visibilityThreshold = 1.dp,
|
||||
),
|
||||
label = "insets-animation",
|
||||
)
|
||||
|
||||
content(animationStateOffset)
|
||||
|
||||
// Display the network indicator with an animation
|
||||
AnimatedVisibility(
|
||||
visibleState = isIndicatorVisible,
|
||||
enter = fadeIn() + expandVertically(),
|
||||
exit = fadeOut() + shrinkVertically(),
|
||||
) {
|
||||
Indicator(modifier)
|
||||
Column(modifier = modifier) {
|
||||
val statusBarTopPadding = if (LocalInspectionMode.current) {
|
||||
// Needed to get valid UI previews
|
||||
24.dp
|
||||
} else {
|
||||
WindowInsets.statusBars.asPaddingValues().calculateTopPadding() + INDICATOR_VERTICAL_PADDING
|
||||
}
|
||||
val target = if (isIndicatorVisible.targetState) statusBarTopPadding else 0.dp
|
||||
val topWindowInset by animateDpAsState(
|
||||
targetValue = target,
|
||||
animationSpec = spring(
|
||||
stiffness = Spring.StiffnessMediumLow,
|
||||
visibilityThreshold = 1.dp,
|
||||
),
|
||||
label = "insets-animation",
|
||||
)
|
||||
// Display the network indicator with an animation
|
||||
AnimatedVisibility(
|
||||
visibleState = isIndicatorVisible,
|
||||
enter = fadeIn() + expandVertically(),
|
||||
exit = fadeOut() + shrinkVertically(),
|
||||
) {
|
||||
ConnectivityIndicator(verticalPadding = INDICATOR_VERTICAL_PADDING)
|
||||
}
|
||||
// Consume the window insets to avoid double padding.
|
||||
content(
|
||||
Modifier.consumeWindowInsets(PaddingValues(top = topWindowInset))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023, 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.networkmonitor.api.ui
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.MutableTransitionState
|
||||
import androidx.compose.animation.expandVertically
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.shrinkVertically
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.statusBarsPadding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
|
||||
/**
|
||||
* A view that displays a connectivity indicator when the device is offline, adding a default
|
||||
* padding to make sure the status bar is not overlapped.
|
||||
*/
|
||||
@Composable
|
||||
fun ConnectivityIndicatorView(
|
||||
isOnline: Boolean,
|
||||
) {
|
||||
val isIndicatorVisible = remember { MutableTransitionState(!isOnline) }.apply { targetState = !isOnline }
|
||||
val isStatusBarPaddingVisible = remember { MutableTransitionState(isOnline) }.apply { targetState = isOnline }
|
||||
|
||||
// Display the network indicator with an animation
|
||||
AnimatedVisibility(
|
||||
visibleState = isIndicatorVisible,
|
||||
enter = fadeIn() + expandVertically(),
|
||||
exit = fadeOut() + shrinkVertically(),
|
||||
) {
|
||||
Indicator()
|
||||
}
|
||||
|
||||
// Show missing status bar padding when the indicator is not visible
|
||||
AnimatedVisibility(
|
||||
visibleState = isStatusBarPaddingVisible,
|
||||
enter = fadeIn() + expandVertically(),
|
||||
exit = fadeOut() + shrinkVertically(),
|
||||
) {
|
||||
StatusBarPaddingSpacer()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun StatusBarPaddingSpacer(modifier: Modifier = Modifier) {
|
||||
Spacer(modifier = modifier.statusBarsPadding())
|
||||
}
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun ConnectivityIndicatorViewPreview() {
|
||||
ElementPreview {
|
||||
ConnectivityIndicatorView(isOnline = false)
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user