Remove FeatureFlag.VoiceMessages

This commit is contained in:
Benoit Marty 2025-08-12 14:37:45 +02:00 committed by Benoit Marty
parent 82c86a2b4d
commit 0ba783cd93
11 changed files with 19 additions and 122 deletions

View file

@ -187,11 +187,6 @@ class MessagesPresenter @AssistedInject constructor(
val snackbarMessage by snackbarDispatcher.collectSnackbarMessageAsState()
var enableVoiceMessages by remember { mutableStateOf(false) }
LaunchedEffect(featureFlagsService) {
enableVoiceMessages = featureFlagsService.isFeatureEnabled(FeatureFlags.VoiceMessages)
}
var dmUserVerificationState by remember { mutableStateOf<IdentityState?>(null) }
val membersState by room.membersStateFlow.collectAsState()
@ -261,7 +256,6 @@ class MessagesPresenter @AssistedInject constructor(
showReinvitePrompt = showReinvitePrompt,
inviteProgress = inviteProgress.value,
enableTextFormatting = MessageComposerConfig.ENABLE_RICH_TEXT_EDITING,
enableVoiceMessages = enableVoiceMessages,
appName = buildMeta.applicationName,
roomCallState = roomCallState,
pinnedMessagesBannerState = pinnedMessagesBannerState,

View file

@ -51,7 +51,6 @@ data class MessagesState(
val inviteProgress: AsyncData<Unit>,
val showReinvitePrompt: Boolean,
val enableTextFormatting: Boolean,
val enableVoiceMessages: Boolean,
val roomCallState: RoomCallState,
val appName: String,
val pinnedMessagesBannerState: PinnedMessagesBannerState,

View file

@ -61,14 +61,12 @@ open class MessagesStateProvider : PreviewParameterProvider<MessagesState> {
aMessagesState(roomName = null),
aMessagesState(composerState = aMessageComposerState(showTextFormatting = true)),
aMessagesState(
enableVoiceMessages = true,
voiceMessageComposerState = aVoiceMessageComposerState(showPermissionRationaleDialog = true),
),
aMessagesState(
roomCallState = anOngoingCallState(),
),
aMessagesState(
enableVoiceMessages = true,
voiceMessageComposerState = aVoiceMessageComposerState(
voiceMessageState = aVoiceMessagePreviewState(),
showSendFailureDialog = true
@ -113,7 +111,6 @@ fun aMessagesState(
reactionSummaryState: ReactionSummaryState = aReactionSummaryState(),
hasNetworkConnection: Boolean = true,
showReinvitePrompt: Boolean = false,
enableVoiceMessages: Boolean = true,
roomCallState: RoomCallState = aStandByCallState(),
pinnedMessagesBannerState: PinnedMessagesBannerState = aLoadedPinnedMessagesBannerState(),
dmUserVerificationState: IdentityState? = null,
@ -141,7 +138,6 @@ fun aMessagesState(
inviteProgress = AsyncData.Uninitialized,
showReinvitePrompt = showReinvitePrompt,
enableTextFormatting = true,
enableVoiceMessages = enableVoiceMessages,
roomCallState = roomCallState,
appName = "Element",
pinnedMessagesBannerState = pinnedMessagesBannerState,

View file

@ -377,7 +377,7 @@ private fun MessagesViewContent(
enableTextFormatting = state.enableTextFormatting,
)
if (state.enableVoiceMessages && state.voiceMessageComposerState.showPermissionRationaleDialog) {
if (state.voiceMessageComposerState.showPermissionRationaleDialog) {
VoiceMessagePermissionRationaleDialog(
onContinue = {
state.voiceMessageComposerState.eventSink(VoiceMessageComposerEvents.AcceptPermissionRationale)
@ -388,7 +388,7 @@ private fun MessagesViewContent(
appName = state.appName
)
}
if (state.enableVoiceMessages && state.voiceMessageComposerState.showSendFailureDialog) {
if (state.voiceMessageComposerState.showSendFailureDialog) {
VoiceMessageSendingFailedDialog(
onDismiss = { state.voiceMessageComposerState.eventSink(VoiceMessageComposerEvents.DismissSendFailureDialog) },
)
@ -464,7 +464,6 @@ private fun MessagesViewComposerBottomSheetContents(
MessageComposerView(
state = state.composerState,
voiceMessageState = state.voiceMessageComposerState,
enableVoiceMessages = state.enableVoiceMessages,
modifier = Modifier.fillMaxWidth(),
)
}

View file

@ -373,7 +373,6 @@ private fun AttachmentsPreviewBottomActions(
onResetComposerMode = {},
onAddAttachment = {},
onDismissTextFormatting = {},
enableVoiceMessages = false,
onVoiceRecorderEvent = {},
onVoicePlayerEvent = {},
onSendVoiceMessage = {},

View file

@ -33,7 +33,6 @@ import kotlinx.coroutines.launch
internal fun MessageComposerView(
state: MessageComposerState,
voiceMessageState: VoiceMessageComposerState,
enableVoiceMessages: Boolean,
modifier: Modifier = Modifier,
) {
val view = LocalView.current
@ -104,7 +103,6 @@ internal fun MessageComposerView(
onResetComposerMode = ::onCloseSpecialMode,
onAddAttachment = ::onAddAttachment,
onDismissTextFormatting = ::onDismissTextFormatting,
enableVoiceMessages = enableVoiceMessages,
onVoiceRecorderEvent = onVoiceRecorderEvent,
onVoicePlayerEvent = onVoicePlayerEvent,
onSendVoiceMessage = onSendVoiceMessage,
@ -128,13 +126,11 @@ internal fun MessageComposerViewPreview(
modifier = Modifier.height(IntrinsicSize.Min),
state = state,
voiceMessageState = aVoiceMessageComposerState(),
enableVoiceMessages = true,
)
MessageComposerView(
modifier = Modifier.height(200.dp),
state = state,
voiceMessageState = aVoiceMessageComposerState(),
enableVoiceMessages = true,
)
DisabledComposerView()
}
@ -150,7 +146,6 @@ internal fun MessageComposerViewVoicePreview(
modifier = Modifier.height(IntrinsicSize.Min),
state = aMessageComposerState(),
voiceMessageState = state,
enableVoiceMessages = true,
)
}
}

View file

@ -28,8 +28,6 @@ import io.element.android.features.messages.impl.utils.TextPillificationHelper
import io.element.android.libraries.androidutils.filesize.FileSizeFormatter
import io.element.android.libraries.androidutils.text.safeLinkify
import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.featureflag.api.FeatureFlagService
import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.permalink.PermalinkParser
import io.element.android.libraries.matrix.api.timeline.item.event.AudioMessageType
@ -56,7 +54,6 @@ import kotlin.time.Duration
class TimelineItemContentMessageFactory @Inject constructor(
private val fileSizeFormatter: FileSizeFormatter,
private val fileExtensionExtractor: FileExtensionExtractor,
private val featureFlagService: FeatureFlagService,
private val htmlConverterProvider: HtmlConverterProvider,
private val permalinkParser: PermalinkParser,
private val textPillificationHelper: TextPillificationHelper,
@ -177,38 +174,20 @@ class TimelineItemContentMessageFactory @Inject constructor(
)
}
is VoiceMessageType -> {
when (featureFlagService.isFeatureEnabled(FeatureFlags.VoiceMessages)) {
true -> {
TimelineItemVoiceContent(
eventId = eventId,
filename = messageType.filename,
fileSize = messageType.info?.size ?: 0,
caption = messageType.caption?.trimEnd(),
formattedCaption = parseHtml(messageType.formattedCaption) ?: messageType.caption?.withLinks(),
isEdited = content.isEdited,
mediaSource = messageType.source,
duration = messageType.info?.duration ?: Duration.ZERO,
mimeType = messageType.info?.mimetype ?: MimeTypes.OctetStream,
waveform = messageType.details?.waveform?.toImmutableList() ?: persistentListOf(),
formattedFileSize = fileSizeFormatter.format(messageType.info?.size ?: 0),
fileExtension = fileExtensionExtractor.extractFromName(messageType.filename)
)
}
false -> {
TimelineItemAudioContent(
filename = messageType.filename,
fileSize = messageType.info?.size ?: 0,
caption = messageType.caption?.trimEnd(),
formattedCaption = parseHtml(messageType.formattedCaption) ?: messageType.caption?.withLinks(),
isEdited = content.isEdited,
mediaSource = messageType.source,
duration = messageType.info?.duration ?: Duration.ZERO,
mimeType = messageType.info?.mimetype ?: MimeTypes.OctetStream,
formattedFileSize = fileSizeFormatter.format(messageType.info?.size ?: 0),
fileExtension = fileExtensionExtractor.extractFromName(messageType.filename),
)
}
}
TimelineItemVoiceContent(
eventId = eventId,
filename = messageType.filename,
fileSize = messageType.info?.size ?: 0,
caption = messageType.caption?.trimEnd(),
formattedCaption = parseHtml(messageType.formattedCaption) ?: messageType.caption?.withLinks(),
isEdited = content.isEdited,
mediaSource = messageType.source,
duration = messageType.info?.duration ?: Duration.ZERO,
mimeType = messageType.info?.mimetype ?: MimeTypes.OctetStream,
waveform = messageType.details?.waveform?.toImmutableList() ?: persistentListOf(),
formattedFileSize = fileSizeFormatter.format(messageType.info?.size ?: 0),
fileExtension = fileExtensionExtractor.extractFromName(messageType.filename)
)
}
is FileMessageType -> {
val fileExtension = fileExtensionExtractor.extractFromName(messageType.filename)

View file

@ -30,7 +30,6 @@ import io.element.android.features.poll.test.pollcontent.FakePollContentStateFac
import io.element.android.libraries.androidutils.filesize.FakeFileSizeFormatter
import io.element.android.libraries.dateformatter.test.FakeDateFormatter
import io.element.android.libraries.eventformatter.api.TimelineEventFormatter
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem
import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.libraries.matrix.test.permalink.FakePermalinkParser
@ -60,7 +59,6 @@ internal fun TestScope.aTimelineItemsFactory(
messageFactory = TimelineItemContentMessageFactory(
fileSizeFormatter = FakeFileSizeFormatter(),
fileExtensionExtractor = FileExtensionExtractorWithoutValidation(),
featureFlagService = FakeFeatureFlagService(),
htmlConverterProvider = FakeHtmlConverterProvider(),
permalinkParser = FakePermalinkParser(),
textPillificationHelper = FakeTextPillificationHelper(),

View file

@ -32,9 +32,6 @@ import io.element.android.features.messages.impl.utils.FakeTextPillificationHelp
import io.element.android.features.messages.test.timeline.FakeHtmlConverterProvider
import io.element.android.libraries.androidutils.filesize.FakeFileSizeFormatter
import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.featureflag.api.FeatureFlagService
import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
import io.element.android.libraries.matrix.api.media.AudioDetails
import io.element.android.libraries.matrix.api.media.AudioInfo
import io.element.android.libraries.matrix.api.media.FileInfo
@ -430,35 +427,6 @@ class TimelineItemContentMessageFactoryTest {
assertThat(result).isEqualTo(expected)
}
@Test
fun `test create VoiceMessageType feature disabled`() = runTest {
val sut = createTimelineItemContentMessageFactory(
featureFlagService = FakeFeatureFlagService(
initialState = mapOf(
FeatureFlags.VoiceMessages.key to false,
)
)
)
val result = sut.create(
content = createMessageContent(type = VoiceMessageType("filename", null, null, MediaSource("url"), null, null)),
senderDisambiguatedDisplayName = "Bob",
eventId = AN_EVENT_ID,
)
val expected = TimelineItemAudioContent(
filename = "filename",
fileSize = 0L,
caption = null,
formattedCaption = null,
isEdited = false,
duration = Duration.ZERO,
mediaSource = MediaSource(url = "url", json = null),
mimeType = MimeTypes.OctetStream,
formattedFileSize = "0 Bytes",
fileExtension = ""
)
assertThat(result).isEqualTo(expected)
}
@Test
fun `test create ImageMessageType`() = runTest {
val sut = createTimelineItemContentMessageFactory()
@ -794,13 +762,11 @@ class TimelineItemContentMessageFactoryTest {
}
private fun createTimelineItemContentMessageFactory(
featureFlagService: FeatureFlagService = FakeFeatureFlagService(),
htmlConverterTransform: (String) -> CharSequence = { it },
permalinkParser: FakePermalinkParser = FakePermalinkParser(),
) = TimelineItemContentMessageFactory(
fileSizeFormatter = FakeFileSizeFormatter(),
fileExtensionExtractor = FileExtensionExtractorWithoutValidation(),
featureFlagService = featureFlagService,
htmlConverterProvider = FakeHtmlConverterProvider(htmlConverterTransform),
permalinkParser = permalinkParser,
textPillificationHelper = FakeTextPillificationHelper(),

View file

@ -21,13 +21,6 @@ enum class FeatureFlags(
override val defaultValue: (BuildMeta) -> Boolean,
override val isFinished: Boolean,
) : Feature {
VoiceMessages(
key = "feature.voicemessages",
title = "Voice messages",
description = "Send and receive voice messages",
defaultValue = { true },
isFinished = true,
),
MarkAsUnread(
key = "feature.markAsUnread",
title = "Mark as unread",

View file

@ -101,7 +101,6 @@ fun TextComposer(
state: TextEditorState,
voiceMessageState: VoiceMessageState,
composerMode: MessageComposerMode,
enableVoiceMessages: Boolean,
onRequestFocus: () -> Unit,
onSendMessage: () -> Unit,
onResetComposerMode: () -> Unit,
@ -273,7 +272,7 @@ fun TextComposer(
}
val sendOrRecordButton = when {
enableVoiceMessages && !canSendMessage ->
!canSendMessage ->
when (voiceMessageState) {
VoiceMessageState.Idle,
is VoiceMessageState.Recording -> recordVoiceButton
@ -288,7 +287,6 @@ fun TextComposer(
val endButtonA11y = endButtonA11y(
composerMode = composerMode,
voiceMessageState = voiceMessageState,
enableVoiceMessages = enableVoiceMessages,
canSendMessage = canSendMessage,
)
@ -341,7 +339,6 @@ fun TextComposer(
} else {
StandardLayout(
voiceMessageState = voiceMessageState,
enableVoiceMessages = enableVoiceMessages,
isRoomEncrypted = state.isRoomEncrypted,
modifier = layoutModifier,
composerOptionsButton = composerOptionsButton,
@ -378,12 +375,11 @@ fun TextComposer(
private fun endButtonA11y(
composerMode: MessageComposerMode,
voiceMessageState: VoiceMessageState,
enableVoiceMessages: Boolean,
canSendMessage: Boolean,
): (SemanticsPropertyReceiver) -> Unit {
val a11ySendButtonDescription = stringResource(
id = when {
enableVoiceMessages && !canSendMessage ->
!canSendMessage ->
when (voiceMessageState) {
VoiceMessageState.Idle,
is VoiceMessageState.Recording -> if (voiceMessageState is VoiceMessageState.Recording) {
@ -410,7 +406,6 @@ private fun endButtonA11y(
@Composable
private fun StandardLayout(
voiceMessageState: VoiceMessageState,
enableVoiceMessages: Boolean,
isRoomEncrypted: Boolean?,
textInput: @Composable () -> Unit,
composerOptionsButton: @Composable () -> Unit,
@ -427,7 +422,7 @@ private fun StandardLayout(
Spacer(Modifier.height(4.dp))
}
Row(verticalAlignment = Alignment.Bottom) {
if (enableVoiceMessages && voiceMessageState !is VoiceMessageState.Idle) {
if (voiceMessageState !is VoiceMessageState.Idle) {
if (voiceMessageState is VoiceMessageState.Preview || voiceMessageState is VoiceMessageState.Recording) {
Box(
modifier = Modifier
@ -636,7 +631,6 @@ internal fun TextComposerSimplePreview() = ElementPreview {
state = textEditorState,
voiceMessageState = VoiceMessageState.Idle,
composerMode = MessageComposerMode.Normal,
enableVoiceMessages = true,
)
}
}
@ -651,7 +645,6 @@ internal fun TextComposerSimpleNotEncryptedPreview() = ElementPreview {
state = textEditorState,
voiceMessageState = VoiceMessageState.Idle,
composerMode = MessageComposerMode.Normal,
enableVoiceMessages = true,
)
}
}
@ -667,7 +660,6 @@ internal fun TextComposerFormattingPreview() = ElementPreview {
voiceMessageState = VoiceMessageState.Idle,
showTextFormatting = true,
composerMode = MessageComposerMode.Normal,
enableVoiceMessages = true,
)
}
}
@ -683,7 +675,6 @@ internal fun TextComposerFormattingNotEncryptedPreview() = ElementPreview {
voiceMessageState = VoiceMessageState.Idle,
showTextFormatting = true,
composerMode = MessageComposerMode.Normal,
enableVoiceMessages = true,
)
}
}
@ -698,7 +689,6 @@ internal fun TextComposerEditPreview() = ElementPreview {
state = textEditorState,
voiceMessageState = VoiceMessageState.Idle,
composerMode = aMessageComposerModeEdit(),
enableVoiceMessages = true,
)
}
}
@ -713,7 +703,6 @@ internal fun TextComposerEditNotEncryptedPreview() = ElementPreview {
state = textEditorState,
voiceMessageState = VoiceMessageState.Idle,
composerMode = aMessageComposerModeEdit(),
enableVoiceMessages = true,
)
}
}
@ -731,7 +720,6 @@ internal fun TextComposerEditCaptionPreview() = ElementPreview {
// Set an existing caption so that the UI will be in edit caption mode
content = "An existing caption",
),
enableVoiceMessages = false,
)
}
}
@ -750,7 +738,6 @@ internal fun TextComposerAddCaptionPreview() = ElementPreview {
content = "",
showCompatibilityWarning = index == 0,
),
enableVoiceMessages = false,
)
}
}
@ -765,7 +752,6 @@ internal fun MarkdownTextComposerEditPreview() = ElementPreview {
state = textEditorState,
voiceMessageState = VoiceMessageState.Idle,
composerMode = aMessageComposerModeEdit(),
enableVoiceMessages = true,
)
}
}
@ -782,7 +768,6 @@ internal fun TextComposerReplyPreview(@PreviewParameter(InReplyToDetailsProvider
composerMode = aMessageComposerModeReply(
replyToDetails = inReplyToDetails,
),
enableVoiceMessages = true,
)
}
}
@ -807,7 +792,6 @@ internal fun TextComposerReplyNotEncryptedPreview(@PreviewParameter(InReplyToDet
composerMode = aMessageComposerModeReply(
replyToDetails = inReplyToDetails,
),
enableVoiceMessages = true,
)
}
}
@ -826,7 +810,6 @@ internal fun TextComposerCaptionPreview() = ElementPreview {
allowCaption = index < list.size,
showCaptionCompatibilityWarning = index == 0,
),
enableVoiceMessages = false,
)
}
}
@ -867,7 +850,6 @@ internal fun TextComposerVoicePreview() = ElementPreview {
state = aTextEditorStateRich(initialFocus = true),
voiceMessageState = voiceMessageState,
composerMode = MessageComposerMode.Normal,
enableVoiceMessages = true,
)
}
}
@ -908,7 +890,6 @@ internal fun TextComposerVoiceNotEncryptedPreview() = ElementPreview {
state = aTextEditorStateRich(initialFocus = true, isRoomEncrypted = false),
voiceMessageState = voiceMessageState,
composerMode = MessageComposerMode.Normal,
enableVoiceMessages = true,
)
}
}
@ -935,7 +916,6 @@ private fun ATextComposer(
state: TextEditorState,
voiceMessageState: VoiceMessageState,
composerMode: MessageComposerMode,
enableVoiceMessages: Boolean,
showTextFormatting: Boolean = false,
) {
TextComposer(
@ -943,7 +923,6 @@ private fun ATextComposer(
showTextFormatting = showTextFormatting,
voiceMessageState = voiceMessageState,
composerMode = composerMode,
enableVoiceMessages = enableVoiceMessages,
onRequestFocus = {},
onSendMessage = {},
onResetComposerMode = {},