Add settings to hide images and videos in the timeline.
Hide images, videos and stickers in the timeline. Disable click on hidden content. It must be revealed first. Add preview without BlurHash. Also hide image in thumbnails.
This commit is contained in:
parent
98d9abecd9
commit
dd2a1b3388
48 changed files with 775 additions and 140 deletions
|
|
@ -145,7 +145,7 @@ data class AttachmentThumbnailInfo(
|
|||
@Composable
|
||||
internal fun AttachmentThumbnailPreview(@PreviewParameter(AttachmentThumbnailInfoProvider::class) data: AttachmentThumbnailInfo) = ElementPreview {
|
||||
AttachmentThumbnail(
|
||||
data,
|
||||
info = data,
|
||||
modifier = Modifier
|
||||
.size(36.dp)
|
||||
.clip(RoundedCornerShape(4.dp))
|
||||
|
|
|
|||
|
|
@ -57,11 +57,11 @@ internal sealed interface InReplyToMetadata {
|
|||
* Metadata can be either a thumbnail with a text OR just a text.
|
||||
*/
|
||||
@Composable
|
||||
internal fun InReplyToDetails.Ready.metadata(): InReplyToMetadata? = when (eventContent) {
|
||||
internal fun InReplyToDetails.Ready.metadata(hideImage: Boolean): InReplyToMetadata? = when (eventContent) {
|
||||
is MessageContent -> when (val type = eventContent.type) {
|
||||
is ImageMessageType -> InReplyToMetadata.Thumbnail(
|
||||
AttachmentThumbnailInfo(
|
||||
thumbnailSource = type.info?.thumbnailSource ?: type.source,
|
||||
thumbnailSource = (type.info?.thumbnailSource ?: type.source).takeUnless { hideImage },
|
||||
textContent = eventContent.body,
|
||||
type = AttachmentThumbnailType.Image,
|
||||
blurHash = type.info?.blurhash,
|
||||
|
|
@ -69,7 +69,7 @@ internal fun InReplyToDetails.Ready.metadata(): InReplyToMetadata? = when (event
|
|||
)
|
||||
is VideoMessageType -> InReplyToMetadata.Thumbnail(
|
||||
AttachmentThumbnailInfo(
|
||||
thumbnailSource = type.info?.thumbnailSource,
|
||||
thumbnailSource = type.info?.thumbnailSource?.takeUnless { hideImage },
|
||||
textContent = eventContent.body,
|
||||
type = AttachmentThumbnailType.Video,
|
||||
blurHash = type.info?.blurhash,
|
||||
|
|
@ -77,7 +77,7 @@ internal fun InReplyToDetails.Ready.metadata(): InReplyToMetadata? = when (event
|
|||
)
|
||||
is FileMessageType -> InReplyToMetadata.Thumbnail(
|
||||
AttachmentThumbnailInfo(
|
||||
thumbnailSource = type.info?.thumbnailSource,
|
||||
thumbnailSource = type.info?.thumbnailSource?.takeUnless { hideImage },
|
||||
textContent = eventContent.body,
|
||||
type = AttachmentThumbnailType.File,
|
||||
)
|
||||
|
|
@ -104,7 +104,7 @@ internal fun InReplyToDetails.Ready.metadata(): InReplyToMetadata? = when (event
|
|||
}
|
||||
is StickerContent -> InReplyToMetadata.Thumbnail(
|
||||
AttachmentThumbnailInfo(
|
||||
thumbnailSource = eventContent.source,
|
||||
thumbnailSource = eventContent.source.takeUnless { hideImage },
|
||||
textContent = eventContent.body,
|
||||
type = AttachmentThumbnailType.Image,
|
||||
blurHash = eventContent.info.blurhash,
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ import io.element.android.libraries.ui.strings.CommonStrings
|
|||
@Composable
|
||||
fun InReplyToView(
|
||||
inReplyTo: InReplyToDetails,
|
||||
hideImage: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
when (inReplyTo) {
|
||||
|
|
@ -55,7 +56,7 @@ fun InReplyToView(
|
|||
ReplyToReadyContent(
|
||||
senderId = inReplyTo.senderId,
|
||||
senderProfile = inReplyTo.senderProfile,
|
||||
metadata = inReplyTo.metadata(),
|
||||
metadata = inReplyTo.metadata(hideImage),
|
||||
modifier = modifier
|
||||
)
|
||||
}
|
||||
|
|
@ -191,5 +192,8 @@ private fun ReplyToContentText(metadata: InReplyToMetadata?) {
|
|||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun InReplyToViewPreview(@PreviewParameter(provider = InReplyToDetailsProvider::class) inReplyTo: InReplyToDetails) = ElementPreview {
|
||||
InReplyToView(inReplyTo)
|
||||
InReplyToView(
|
||||
inReplyTo = inReplyTo,
|
||||
hideImage = false,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ class InReplyToMetadataKtTest {
|
|||
@Test
|
||||
fun `any message content`() = runTest {
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
anInReplyToDetailsReady(eventContent = aMessageContent()).metadata()
|
||||
anInReplyToDetailsReady(eventContent = aMessageContent()).metadata(hideImage = false)
|
||||
}.test {
|
||||
awaitItem().let {
|
||||
assertThat(it).isEqualTo(InReplyToMetadata.Text("textContent"))
|
||||
|
|
@ -82,7 +82,7 @@ class InReplyToMetadataKtTest {
|
|||
info = anImageInfo(),
|
||||
)
|
||||
)
|
||||
).metadata()
|
||||
).metadata(hideImage = false)
|
||||
}.test {
|
||||
awaitItem().let {
|
||||
assertThat(it).isEqualTo(
|
||||
|
|
@ -99,6 +99,36 @@ class InReplyToMetadataKtTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `an image message content, no thumbnail`() = runTest {
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
anInReplyToDetailsReady(
|
||||
eventContent = aMessageContent(
|
||||
messageType = ImageMessageType(
|
||||
body = "body",
|
||||
formatted = null,
|
||||
filename = null,
|
||||
source = aMediaSource(),
|
||||
info = anImageInfo(),
|
||||
)
|
||||
)
|
||||
).metadata(hideImage = true)
|
||||
}.test {
|
||||
awaitItem().let {
|
||||
assertThat(it).isEqualTo(
|
||||
InReplyToMetadata.Thumbnail(
|
||||
attachmentThumbnailInfo = AttachmentThumbnailInfo(
|
||||
thumbnailSource = null,
|
||||
textContent = "body",
|
||||
type = AttachmentThumbnailType.Image,
|
||||
blurHash = A_BLUR_HASH,
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `a sticker message content`() = runTest {
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
|
|
@ -108,7 +138,7 @@ class InReplyToMetadataKtTest {
|
|||
info = anImageInfo(),
|
||||
source = aMediaSource(url = "url")
|
||||
)
|
||||
).metadata()
|
||||
).metadata(hideImage = false)
|
||||
}.test {
|
||||
awaitItem().let {
|
||||
assertThat(it).isEqualTo(
|
||||
|
|
@ -125,6 +155,32 @@ class InReplyToMetadataKtTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `a sticker message content, no thumbnail`() = runTest {
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
anInReplyToDetailsReady(
|
||||
eventContent = StickerContent(
|
||||
body = "body",
|
||||
info = anImageInfo(),
|
||||
source = aMediaSource(url = "url")
|
||||
)
|
||||
).metadata(hideImage = true)
|
||||
}.test {
|
||||
awaitItem().let {
|
||||
assertThat(it).isEqualTo(
|
||||
InReplyToMetadata.Thumbnail(
|
||||
attachmentThumbnailInfo = AttachmentThumbnailInfo(
|
||||
thumbnailSource = null,
|
||||
textContent = "body",
|
||||
type = AttachmentThumbnailType.Image,
|
||||
blurHash = A_BLUR_HASH,
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `a video message content`() = runTest {
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
|
|
@ -138,7 +194,7 @@ class InReplyToMetadataKtTest {
|
|||
info = aVideoInfo(),
|
||||
)
|
||||
)
|
||||
).metadata()
|
||||
).metadata(hideImage = false)
|
||||
}.test {
|
||||
awaitItem().let {
|
||||
assertThat(it).isEqualTo(
|
||||
|
|
@ -155,6 +211,36 @@ class InReplyToMetadataKtTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `a video message content, no thumbnail`() = runTest {
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
anInReplyToDetailsReady(
|
||||
eventContent = aMessageContent(
|
||||
messageType = VideoMessageType(
|
||||
body = "body",
|
||||
formatted = null,
|
||||
filename = null,
|
||||
source = aMediaSource(),
|
||||
info = aVideoInfo(),
|
||||
)
|
||||
)
|
||||
).metadata(hideImage = true)
|
||||
}.test {
|
||||
awaitItem().let {
|
||||
assertThat(it).isEqualTo(
|
||||
InReplyToMetadata.Thumbnail(
|
||||
attachmentThumbnailInfo = AttachmentThumbnailInfo(
|
||||
thumbnailSource = null,
|
||||
textContent = "body",
|
||||
type = AttachmentThumbnailType.Video,
|
||||
blurHash = A_BLUR_HASH,
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `a file message content`() = runTest {
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
|
|
@ -171,7 +257,7 @@ class InReplyToMetadataKtTest {
|
|||
),
|
||||
)
|
||||
)
|
||||
).metadata()
|
||||
).metadata(hideImage = false)
|
||||
}.test {
|
||||
awaitItem().let {
|
||||
assertThat(it).isEqualTo(
|
||||
|
|
@ -188,6 +274,39 @@ class InReplyToMetadataKtTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `a file message content, no thumbnail`() = runTest {
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
anInReplyToDetailsReady(
|
||||
eventContent = aMessageContent(
|
||||
messageType = FileMessageType(
|
||||
body = "body",
|
||||
source = aMediaSource(),
|
||||
info = FileInfo(
|
||||
mimetype = null,
|
||||
size = null,
|
||||
thumbnailInfo = null,
|
||||
thumbnailSource = aMediaSource(),
|
||||
),
|
||||
)
|
||||
)
|
||||
).metadata(hideImage = true)
|
||||
}.test {
|
||||
awaitItem().let {
|
||||
assertThat(it).isEqualTo(
|
||||
InReplyToMetadata.Thumbnail(
|
||||
attachmentThumbnailInfo = AttachmentThumbnailInfo(
|
||||
thumbnailSource = null,
|
||||
textContent = "body",
|
||||
type = AttachmentThumbnailType.File,
|
||||
blurHash = null,
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `a audio message content`() = runTest {
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
|
|
@ -203,7 +322,7 @@ class InReplyToMetadataKtTest {
|
|||
),
|
||||
)
|
||||
)
|
||||
).metadata()
|
||||
).metadata(hideImage = false)
|
||||
}.test {
|
||||
awaitItem().let {
|
||||
assertThat(it).isEqualTo(
|
||||
|
|
@ -231,7 +350,7 @@ class InReplyToMetadataKtTest {
|
|||
description = null,
|
||||
)
|
||||
)
|
||||
).metadata()
|
||||
).metadata(hideImage = false)
|
||||
}
|
||||
}.test {
|
||||
awaitItem().let {
|
||||
|
|
@ -262,7 +381,7 @@ class InReplyToMetadataKtTest {
|
|||
details = null,
|
||||
)
|
||||
)
|
||||
).metadata()
|
||||
).metadata(hideImage = false)
|
||||
}
|
||||
}.test {
|
||||
awaitItem().let {
|
||||
|
|
@ -285,7 +404,7 @@ class InReplyToMetadataKtTest {
|
|||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
anInReplyToDetailsReady(
|
||||
eventContent = aPollContent()
|
||||
).metadata()
|
||||
).metadata(hideImage = false)
|
||||
}.test {
|
||||
awaitItem().let {
|
||||
assertThat(it).isEqualTo(
|
||||
|
|
@ -307,7 +426,7 @@ class InReplyToMetadataKtTest {
|
|||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
anInReplyToDetailsReady(
|
||||
eventContent = RedactedContent
|
||||
).metadata()
|
||||
).metadata(hideImage = false)
|
||||
}.test {
|
||||
awaitItem().let {
|
||||
assertThat(it).isEqualTo(InReplyToMetadata.Redacted)
|
||||
|
|
@ -320,7 +439,7 @@ class InReplyToMetadataKtTest {
|
|||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
anInReplyToDetailsReady(
|
||||
eventContent = UnableToDecryptContent(UnableToDecryptContent.Data.Unknown)
|
||||
).metadata()
|
||||
).metadata(hideImage = false)
|
||||
}.test {
|
||||
awaitItem().let {
|
||||
assertThat(it).isEqualTo(InReplyToMetadata.UnableToDecrypt)
|
||||
|
|
@ -333,7 +452,7 @@ class InReplyToMetadataKtTest {
|
|||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
anInReplyToDetailsReady(
|
||||
eventContent = FailedToParseMessageLikeContent("", "")
|
||||
).metadata()
|
||||
).metadata(hideImage = false)
|
||||
}.test {
|
||||
awaitItem().let {
|
||||
assertThat(it).isNull()
|
||||
|
|
@ -346,7 +465,7 @@ class InReplyToMetadataKtTest {
|
|||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
anInReplyToDetailsReady(
|
||||
eventContent = FailedToParseStateContent("", "", "")
|
||||
).metadata()
|
||||
).metadata(hideImage = false)
|
||||
}.test {
|
||||
awaitItem().let {
|
||||
assertThat(it).isNull()
|
||||
|
|
@ -359,7 +478,7 @@ class InReplyToMetadataKtTest {
|
|||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
anInReplyToDetailsReady(
|
||||
eventContent = ProfileChangeContent("", "", "", "")
|
||||
).metadata()
|
||||
).metadata(hideImage = false)
|
||||
}.test {
|
||||
awaitItem().let {
|
||||
assertThat(it).isNull()
|
||||
|
|
@ -372,7 +491,7 @@ class InReplyToMetadataKtTest {
|
|||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
anInReplyToDetailsReady(
|
||||
eventContent = RoomMembershipContent(A_USER_ID, null, null)
|
||||
).metadata()
|
||||
).metadata(hideImage = false)
|
||||
}.test {
|
||||
awaitItem().let {
|
||||
assertThat(it).isNull()
|
||||
|
|
@ -385,7 +504,7 @@ class InReplyToMetadataKtTest {
|
|||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
anInReplyToDetailsReady(
|
||||
eventContent = StateContent("", OtherState.RoomJoinRules)
|
||||
).metadata()
|
||||
).metadata(hideImage = false)
|
||||
}.test {
|
||||
awaitItem().let {
|
||||
assertThat(it).isNull()
|
||||
|
|
@ -398,7 +517,7 @@ class InReplyToMetadataKtTest {
|
|||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
anInReplyToDetailsReady(
|
||||
eventContent = UnknownContent
|
||||
).metadata()
|
||||
).metadata(hideImage = false)
|
||||
}.test {
|
||||
awaitItem().let {
|
||||
assertThat(it).isNull()
|
||||
|
|
@ -411,7 +530,7 @@ class InReplyToMetadataKtTest {
|
|||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
anInReplyToDetailsReady(
|
||||
eventContent = null
|
||||
).metadata()
|
||||
).metadata(hideImage = false)
|
||||
}.test {
|
||||
awaitItem().let {
|
||||
assertThat(it).isNull()
|
||||
|
|
|
|||
|
|
@ -22,5 +22,8 @@ interface AppPreferencesStore {
|
|||
suspend fun setSimplifiedSlidingSyncEnabled(enabled: Boolean)
|
||||
fun isSimplifiedSlidingSyncEnabledFlow(): Flow<Boolean>
|
||||
|
||||
suspend fun setHideImagesAndVideos(value: Boolean)
|
||||
fun doesHideImagesAndVideosFlow(): Flow<Boolean>
|
||||
|
||||
suspend fun reset()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ private val developerModeKey = booleanPreferencesKey("developerMode")
|
|||
private val customElementCallBaseUrlKey = stringPreferencesKey("elementCallBaseUrl")
|
||||
private val themeKey = stringPreferencesKey("theme")
|
||||
private val simplifiedSlidingSyncKey = booleanPreferencesKey("useSimplifiedSlidingSync")
|
||||
private val hideImagesAndVideosKey = booleanPreferencesKey("hideImagesAndVideos")
|
||||
|
||||
@ContributesBinding(AppScope::class)
|
||||
class DefaultAppPreferencesStore @Inject constructor(
|
||||
|
|
@ -91,6 +92,18 @@ class DefaultAppPreferencesStore @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun setHideImagesAndVideos(value: Boolean) {
|
||||
store.edit { prefs ->
|
||||
prefs[hideImagesAndVideosKey] = value
|
||||
}
|
||||
}
|
||||
|
||||
override fun doesHideImagesAndVideosFlow(): Flow<Boolean> {
|
||||
return store.data.map { prefs ->
|
||||
prefs[hideImagesAndVideosKey] ?: false
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun reset() {
|
||||
store.edit { it.clear() }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,11 +13,13 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|||
|
||||
class InMemoryAppPreferencesStore(
|
||||
isDeveloperModeEnabled: Boolean = false,
|
||||
hideImagesAndVideos: Boolean = false,
|
||||
customElementCallBaseUrl: String? = null,
|
||||
theme: String? = null,
|
||||
simplifiedSlidingSyncEnabled: Boolean = false
|
||||
) : AppPreferencesStore {
|
||||
private val isDeveloperModeEnabled = MutableStateFlow(isDeveloperModeEnabled)
|
||||
private val hideImagesAndVideos = MutableStateFlow(hideImagesAndVideos)
|
||||
private val customElementCallBaseUrl = MutableStateFlow(customElementCallBaseUrl)
|
||||
private val theme = MutableStateFlow(theme)
|
||||
private val simplifiedSlidingSyncEnabled = MutableStateFlow(simplifiedSlidingSyncEnabled)
|
||||
|
|
@ -54,6 +56,14 @@ class InMemoryAppPreferencesStore(
|
|||
return simplifiedSlidingSyncEnabled
|
||||
}
|
||||
|
||||
override suspend fun setHideImagesAndVideos(value: Boolean) {
|
||||
hideImagesAndVideos.value = value
|
||||
}
|
||||
|
||||
override fun doesHideImagesAndVideosFlow(): Flow<Boolean> {
|
||||
return hideImagesAndVideos
|
||||
}
|
||||
|
||||
override suspend fun reset() {
|
||||
// No op
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ internal fun ComposerModeView(
|
|||
ReplyToModeView(
|
||||
modifier = Modifier.padding(8.dp),
|
||||
replyToDetails = composerMode.replyToDetails,
|
||||
hideImage = composerMode.hideImage,
|
||||
onResetComposerMode = onResetComposerMode,
|
||||
)
|
||||
}
|
||||
|
|
@ -103,6 +104,7 @@ private fun EditingModeView(
|
|||
@Composable
|
||||
private fun ReplyToModeView(
|
||||
replyToDetails: InReplyToDetails,
|
||||
hideImage: Boolean,
|
||||
onResetComposerMode: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
|
|
@ -112,7 +114,11 @@ private fun ReplyToModeView(
|
|||
.background(MaterialTheme.colorScheme.surface)
|
||||
.padding(4.dp)
|
||||
) {
|
||||
InReplyToView(inReplyTo = replyToDetails, modifier = Modifier.weight(1f))
|
||||
InReplyToView(
|
||||
inReplyTo = replyToDetails,
|
||||
hideImage = hideImage,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
Icon(
|
||||
imageVector = CompoundIcons.Close(),
|
||||
contentDescription = stringResource(CommonStrings.action_close),
|
||||
|
|
|
|||
|
|
@ -118,8 +118,8 @@ fun TextComposer(
|
|||
}
|
||||
|
||||
val layoutModifier = modifier
|
||||
.fillMaxSize()
|
||||
.height(IntrinsicSize.Min)
|
||||
.fillMaxSize()
|
||||
.height(IntrinsicSize.Min)
|
||||
|
||||
val composerOptionsButton: @Composable () -> Unit = remember {
|
||||
@Composable {
|
||||
|
|
@ -316,8 +316,8 @@ private fun StandardLayout(
|
|||
if (voiceMessageState is VoiceMessageState.Preview || voiceMessageState is VoiceMessageState.Recording) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(bottom = 5.dp, top = 5.dp, end = 3.dp, start = 3.dp)
|
||||
.size(48.dp),
|
||||
.padding(bottom = 5.dp, top = 5.dp, end = 3.dp, start = 3.dp)
|
||||
.size(48.dp),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
voiceDeleteButton()
|
||||
|
|
@ -327,8 +327,8 @@ private fun StandardLayout(
|
|||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(bottom = 8.dp, top = 8.dp)
|
||||
.weight(1f)
|
||||
.padding(bottom = 8.dp, top = 8.dp)
|
||||
.weight(1f)
|
||||
) {
|
||||
voiceRecording()
|
||||
}
|
||||
|
|
@ -341,16 +341,16 @@ private fun StandardLayout(
|
|||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(bottom = 8.dp, top = 8.dp)
|
||||
.weight(1f)
|
||||
.padding(bottom = 8.dp, top = 8.dp)
|
||||
.weight(1f)
|
||||
) {
|
||||
textInput()
|
||||
}
|
||||
}
|
||||
Box(
|
||||
Modifier
|
||||
.padding(bottom = 5.dp, top = 5.dp, end = 6.dp, start = 6.dp)
|
||||
.size(48.dp),
|
||||
Modifier
|
||||
.padding(bottom = 5.dp, top = 5.dp, end = 6.dp, start = 6.dp)
|
||||
.size(48.dp),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
endButton()
|
||||
|
|
@ -372,8 +372,8 @@ private fun TextFormattingLayout(
|
|||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(horizontal = 12.dp)
|
||||
.weight(1f)
|
||||
.padding(horizontal = 12.dp)
|
||||
) {
|
||||
textInput()
|
||||
}
|
||||
|
|
@ -417,21 +417,24 @@ private fun TextInputBox(
|
|||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.clip(roundedCorners)
|
||||
.border(0.5.dp, borderColor, roundedCorners)
|
||||
.background(color = bgColor)
|
||||
.requiredHeightIn(min = 42.dp)
|
||||
.fillMaxSize(),
|
||||
.clip(roundedCorners)
|
||||
.border(0.5.dp, borderColor, roundedCorners)
|
||||
.background(color = bgColor)
|
||||
.requiredHeightIn(min = 42.dp)
|
||||
.fillMaxSize(),
|
||||
) {
|
||||
if (composerMode is MessageComposerMode.Special) {
|
||||
ComposerModeView(composerMode = composerMode, onResetComposerMode = onResetComposerMode)
|
||||
ComposerModeView(
|
||||
composerMode = composerMode,
|
||||
onResetComposerMode = onResetComposerMode,
|
||||
)
|
||||
}
|
||||
val defaultTypography = ElementTheme.typography.fontBodyLgRegular
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(top = 4.dp, bottom = 4.dp, start = 12.dp, end = 42.dp)
|
||||
// Apply test tag only once, otherwise 2 nodes will have it (both the normal and subcomposing one) and tests will fail
|
||||
.then(if (!subcomposing) Modifier.testTag(TestTags.textEditor) else Modifier),
|
||||
.padding(top = 4.dp, bottom = 4.dp, start = 12.dp, end = 42.dp)
|
||||
// Apply test tag only once, otherwise 2 nodes will have it (both the normal and subcomposing one) and tests will fail
|
||||
.then(if (!subcomposing) Modifier.testTag(TestTags.textEditor) else Modifier),
|
||||
contentAlignment = Alignment.CenterStart,
|
||||
) {
|
||||
// Placeholder
|
||||
|
|
@ -477,8 +480,8 @@ private fun TextInput(
|
|||
// This prevents it gaining focus and mutating the state.
|
||||
registerStateUpdates = !subcomposing,
|
||||
modifier = Modifier
|
||||
.padding(top = 6.dp, bottom = 6.dp)
|
||||
.fillMaxWidth(),
|
||||
.padding(top = 6.dp, bottom = 6.dp)
|
||||
.fillMaxWidth(),
|
||||
style = ElementRichTextEditorStyle.composerStyle(hasFocus = state.hasFocus),
|
||||
resolveMentionDisplay = resolveMentionDisplay,
|
||||
resolveRoomMentionDisplay = resolveRoomMentionDisplay,
|
||||
|
|
@ -603,6 +606,7 @@ internal fun TextComposerReplyPreview(@PreviewParameter(InReplyToDetailsProvider
|
|||
voiceMessageState = VoiceMessageState.Idle,
|
||||
composerMode = MessageComposerMode.Reply(
|
||||
replyToDetails = inReplyToDetails,
|
||||
hideImage = false,
|
||||
),
|
||||
enableVoiceMessages = true,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -27,7 +27,8 @@ sealed interface MessageComposerMode {
|
|||
) : Special
|
||||
|
||||
data class Reply(
|
||||
val replyToDetails: InReplyToDetails
|
||||
val replyToDetails: InReplyToDetails,
|
||||
val hideImage: Boolean,
|
||||
) : Special {
|
||||
val eventId: EventId = replyToDetails.eventId()
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue