Merge pull request #3537 from element-hq/feature/fga/fix_image_viewer_glitch
Fix image viewer glitch
This commit is contained in:
commit
5bc89f3c7b
3 changed files with 19 additions and 28 deletions
|
|
@ -33,9 +33,10 @@ import io.element.android.features.roomdetails.impl.notificationsettings.RoomNot
|
||||||
import io.element.android.features.roomdetails.impl.rolesandpermissions.RolesAndPermissionsFlowNode
|
import io.element.android.features.roomdetails.impl.rolesandpermissions.RolesAndPermissionsFlowNode
|
||||||
import io.element.android.features.userprofile.shared.UserProfileNodeHelper
|
import io.element.android.features.userprofile.shared.UserProfileNodeHelper
|
||||||
import io.element.android.features.userprofile.shared.avatar.AvatarPreviewNode
|
import io.element.android.features.userprofile.shared.avatar.AvatarPreviewNode
|
||||||
import io.element.android.libraries.architecture.BackstackView
|
import io.element.android.libraries.architecture.BackstackWithOverlayBox
|
||||||
import io.element.android.libraries.architecture.BaseFlowNode
|
import io.element.android.libraries.architecture.BaseFlowNode
|
||||||
import io.element.android.libraries.architecture.createNode
|
import io.element.android.libraries.architecture.createNode
|
||||||
|
import io.element.android.libraries.architecture.overlay.operation.show
|
||||||
import io.element.android.libraries.core.mimetype.MimeTypes
|
import io.element.android.libraries.core.mimetype.MimeTypes
|
||||||
import io.element.android.libraries.di.RoomScope
|
import io.element.android.libraries.di.RoomScope
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
|
@ -125,7 +126,7 @@ class RoomDetailsFlowNode @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun openAvatarPreview(name: String, url: String) {
|
override fun openAvatarPreview(name: String, url: String) {
|
||||||
backstack.push(NavTarget.AvatarPreview(name, url))
|
overlay.show(NavTarget.AvatarPreview(name, url))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun openPollHistory() {
|
override fun openPollHistory() {
|
||||||
|
|
@ -186,7 +187,7 @@ class RoomDetailsFlowNode @AssistedInject constructor(
|
||||||
is NavTarget.RoomMemberDetails -> {
|
is NavTarget.RoomMemberDetails -> {
|
||||||
val callback = object : UserProfileNodeHelper.Callback {
|
val callback = object : UserProfileNodeHelper.Callback {
|
||||||
override fun openAvatarPreview(username: String, avatarUrl: String) {
|
override fun openAvatarPreview(username: String, avatarUrl: String) {
|
||||||
backstack.push(NavTarget.AvatarPreview(username, avatarUrl))
|
overlay.show(NavTarget.AvatarPreview(username, avatarUrl))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStartDM(roomId: RoomId) {
|
override fun onStartDM(roomId: RoomId) {
|
||||||
|
|
@ -252,6 +253,6 @@ class RoomDetailsFlowNode @AssistedInject constructor(
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun View(modifier: Modifier) {
|
override fun View(modifier: Modifier) {
|
||||||
BackstackView()
|
BackstackWithOverlayBox(modifier)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import coil.compose.AsyncImage
|
||||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||||
import io.element.android.libraries.architecture.AsyncData
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.core.mimetype.MimeTypes
|
import io.element.android.libraries.core.mimetype.MimeTypes
|
||||||
|
|
@ -66,9 +67,6 @@ import me.saket.telephoto.flick.FlickToDismiss
|
||||||
import me.saket.telephoto.flick.FlickToDismissState
|
import me.saket.telephoto.flick.FlickToDismissState
|
||||||
import me.saket.telephoto.flick.rememberFlickToDismissState
|
import me.saket.telephoto.flick.rememberFlickToDismissState
|
||||||
import me.saket.telephoto.zoomable.ZoomSpec
|
import me.saket.telephoto.zoomable.ZoomSpec
|
||||||
import me.saket.telephoto.zoomable.ZoomableState
|
|
||||||
import me.saket.telephoto.zoomable.coil.ZoomableAsyncImage
|
|
||||||
import me.saket.telephoto.zoomable.rememberZoomableImageState
|
|
||||||
import me.saket.telephoto.zoomable.rememberZoomableState
|
import me.saket.telephoto.zoomable.rememberZoomableState
|
||||||
import kotlin.time.Duration
|
import kotlin.time.Duration
|
||||||
|
|
||||||
|
|
@ -181,7 +179,6 @@ private fun MediaViewerPage(
|
||||||
mediaInfo = state.mediaInfo,
|
mediaInfo = state.mediaInfo,
|
||||||
thumbnailSource = state.thumbnailSource,
|
thumbnailSource = state.thumbnailSource,
|
||||||
isVisible = showThumbnail,
|
isVisible = showThumbnail,
|
||||||
zoomableState = zoomableState
|
|
||||||
)
|
)
|
||||||
if (showError) {
|
if (showError) {
|
||||||
ErrorView(
|
ErrorView(
|
||||||
|
|
@ -316,24 +313,18 @@ private fun ThumbnailView(
|
||||||
thumbnailSource: MediaSource?,
|
thumbnailSource: MediaSource?,
|
||||||
isVisible: Boolean,
|
isVisible: Boolean,
|
||||||
mediaInfo: MediaInfo,
|
mediaInfo: MediaInfo,
|
||||||
zoomableState: ZoomableState,
|
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
AnimatedVisibility(
|
Box(
|
||||||
visible = isVisible,
|
modifier = modifier.fillMaxSize(),
|
||||||
enter = fadeIn(),
|
contentAlignment = Alignment.Center
|
||||||
exit = fadeOut()
|
|
||||||
) {
|
) {
|
||||||
Box(
|
if (isVisible) {
|
||||||
modifier = modifier.fillMaxSize(),
|
|
||||||
contentAlignment = Alignment.Center
|
|
||||||
) {
|
|
||||||
val mediaRequestData = MediaRequestData(
|
val mediaRequestData = MediaRequestData(
|
||||||
source = thumbnailSource,
|
source = thumbnailSource,
|
||||||
kind = MediaRequestData.Kind.File(mediaInfo.name, mediaInfo.mimeType)
|
kind = MediaRequestData.Kind.File(mediaInfo.name, mediaInfo.mimeType)
|
||||||
)
|
)
|
||||||
ZoomableAsyncImage(
|
AsyncImage(
|
||||||
state = rememberZoomableImageState(zoomableState),
|
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
model = mediaRequestData,
|
model = mediaRequestData,
|
||||||
contentScale = ContentScale.Fit,
|
contentScale = ContentScale.Fit,
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,6 @@ import io.element.android.tests.testutils.EventsRecorder
|
||||||
import io.element.android.tests.testutils.clickOn
|
import io.element.android.tests.testutils.clickOn
|
||||||
import io.element.android.tests.testutils.ensureCalledOnce
|
import io.element.android.tests.testutils.ensureCalledOnce
|
||||||
import io.element.android.tests.testutils.pressBack
|
import io.element.android.tests.testutils.pressBack
|
||||||
import org.junit.Ignore
|
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.rules.TestRule
|
import org.junit.rules.TestRule
|
||||||
|
|
@ -81,7 +80,6 @@ class MediaViewerViewTest {
|
||||||
eventsRecorder.assertSingle(expectedEvent)
|
eventsRecorder.assertSingle(expectedEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore("This test is not passing yet, maybe due to interaction with ZoomableAsyncImage?")
|
|
||||||
@Test
|
@Test
|
||||||
fun `clicking on image hides the overlay`() {
|
fun `clicking on image hides the overlay`() {
|
||||||
val eventsRecorder = EventsRecorder<MediaViewerEvents>(expectEvents = false)
|
val eventsRecorder = EventsRecorder<MediaViewerEvents>(expectEvents = false)
|
||||||
|
|
@ -96,16 +94,17 @@ class MediaViewerViewTest {
|
||||||
)
|
)
|
||||||
// Ensure that the action are visible
|
// Ensure that the action are visible
|
||||||
val contentDescription = rule.activity.getString(CommonStrings.action_open_with)
|
val contentDescription = rule.activity.getString(CommonStrings.action_open_with)
|
||||||
rule.onNodeWithContentDescription(contentDescription).assertHasClickAction()
|
rule.onNodeWithContentDescription(contentDescription)
|
||||||
|
.assertExists()
|
||||||
|
.assertHasClickAction()
|
||||||
val imageContentDescription = rule.activity.getString(CommonStrings.common_image)
|
val imageContentDescription = rule.activity.getString(CommonStrings.common_image)
|
||||||
rule.onNodeWithContentDescription(imageContentDescription).performClick()
|
rule.onNodeWithContentDescription(imageContentDescription).performClick()
|
||||||
// assertHasNoClickAction does not work as expected (?)
|
// Give time for the animation (? since even by removing AnimatedVisibility it still fails)
|
||||||
// rule.onNodeWithContentDescription(contentDescription).assertHasNoClickAction()
|
rule.mainClock.advanceTimeBy(1_000)
|
||||||
rule.onNodeWithContentDescription(contentDescription).performClick()
|
rule.onNodeWithContentDescription(contentDescription)
|
||||||
// No emitted event
|
.assertDoesNotExist()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore("This test is not passing yet, maybe due to interaction with ZoomableAsyncImage?")
|
|
||||||
@Test
|
@Test
|
||||||
fun `clicking swipe on the image invokes the expected callback`() {
|
fun `clicking swipe on the image invokes the expected callback`() {
|
||||||
val eventsRecorder = EventsRecorder<MediaViewerEvents>(expectEvents = false)
|
val eventsRecorder = EventsRecorder<MediaViewerEvents>(expectEvents = false)
|
||||||
|
|
@ -121,7 +120,7 @@ class MediaViewerViewTest {
|
||||||
onBackClick = callback,
|
onBackClick = callback,
|
||||||
)
|
)
|
||||||
val imageContentDescription = rule.activity.getString(CommonStrings.common_image)
|
val imageContentDescription = rule.activity.getString(CommonStrings.common_image)
|
||||||
rule.onNodeWithContentDescription(imageContentDescription).performTouchInput { swipeDown() }
|
rule.onNodeWithContentDescription(imageContentDescription).performTouchInput { swipeDown(startY = centerY) }
|
||||||
rule.mainClock.advanceTimeBy(1_000)
|
rule.mainClock.advanceTimeBy(1_000)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue