diff --git a/app/src/main/kotlin/com/wire/android/di/accountScoped/ConversationModule.kt b/app/src/main/kotlin/com/wire/android/di/accountScoped/ConversationModule.kt index f0379b34b56..04967b9d58f 100644 --- a/app/src/main/kotlin/com/wire/android/di/accountScoped/ConversationModule.kt +++ b/app/src/main/kotlin/com/wire/android/di/accountScoped/ConversationModule.kt @@ -39,6 +39,7 @@ import com.wire.kalium.logic.feature.conversation.LeaveConversationUseCase import com.wire.kalium.logic.feature.conversation.ObserveEligibleMembersForConversationAdminRoleUseCase import com.wire.kalium.logic.feature.conversation.PromoteAdminAndLeaveConversationUseCase import com.wire.kalium.logic.feature.conversation.NotifyConversationIsOpenUseCase +import com.wire.kalium.logic.feature.conversation.ObserveSelfUserHasViewerAccessOnConversationUseCase import com.wire.kalium.logic.feature.conversation.ObserveArchivedUnreadConversationsCountUseCase import com.wire.kalium.logic.feature.conversation.ObserveConversationDetailsUseCase import com.wire.kalium.logic.feature.conversation.ObserveConversationInteractionAvailabilityUseCase @@ -419,4 +420,11 @@ class ConversationModule { conversationScope: ConversationScope ): ChangeAccessForAppsInConversationUseCase = conversationScope.changeAccessForAppsInConversation + + @ViewModelScoped + @Provides + fun provideObserveSelfUserHasViewerAccessUseCase( + conversationScope: ConversationScope + ): ObserveSelfUserHasViewerAccessOnConversationUseCase = + conversationScope.observeSelfUserHasViewerAccess } diff --git a/app/src/main/kotlin/com/wire/android/ui/common/AttachmentButton.kt b/app/src/main/kotlin/com/wire/android/ui/common/AttachmentButton.kt index 6c9f5d403c2..65c81021f9e 100644 --- a/app/src/main/kotlin/com/wire/android/ui/common/AttachmentButton.kt +++ b/app/src/main/kotlin/com/wire/android/ui/common/AttachmentButton.kt @@ -48,19 +48,42 @@ import com.wire.android.ui.theme.wireDimensions import com.wire.android.ui.theme.wireTypography @Composable -fun AttachmentButton(@DrawableRes icon: Int, labelStyle: TextStyle, modifier: Modifier = Modifier, text: String = "", onClick: () -> Unit) { +fun AttachmentButton( + @DrawableRes icon: Int, + labelStyle: TextStyle, + modifier: Modifier = Modifier, + text: String = "", + enabled: Boolean = true, + onClick: () -> Unit, +) { + val buttonBackgroundColor = if (enabled) { + MaterialTheme.wireColorScheme.primaryButtonEnabled + } else { + MaterialTheme.wireColorScheme.primaryButtonDisabled + } + val buttonContentColor = if (enabled) { + MaterialTheme.wireColorScheme.onPrimaryButtonEnabled + } else { + MaterialTheme.wireColorScheme.onPrimaryButtonDisabled + } + val labelColor = if (enabled) { + MaterialTheme.wireColorScheme.onBackground + } else { + MaterialTheme.wireColorScheme.secondaryText + } + Column( modifier = modifier .padding(dimensions().spacing4x) .clip(RoundedCornerShape(size = MaterialTheme.wireDimensions.buttonSmallCornerSize)) - .clickable { onClick() } + .clickable(enabled = enabled) { onClick() } .padding(dimensions().spacing8x), horizontalAlignment = Alignment.CenterHorizontally ) { Box( modifier = Modifier .size(dimensions().attachmentButtonSize) - .background(MaterialTheme.wireColorScheme.primaryButtonEnabled, CircleShape) + .background(buttonBackgroundColor, CircleShape) .padding(dimensions().spacing2x) ) { Image( @@ -70,7 +93,7 @@ fun AttachmentButton(@DrawableRes icon: Int, labelStyle: TextStyle, modifier: Mo modifier = Modifier .padding(dimensions().spacing8x) .align(Alignment.Center), - colorFilter = ColorFilter.tint(MaterialTheme.wireColorScheme.onPrimaryButtonEnabled) + colorFilter = ColorFilter.tint(buttonContentColor) ) } VerticalSpace.x4() @@ -80,7 +103,7 @@ fun AttachmentButton(@DrawableRes icon: Int, labelStyle: TextStyle, modifier: Mo maxLines = 2, textAlign = TextAlign.Center, style = labelStyle, - color = MaterialTheme.wireColorScheme.onBackground, + color = labelColor, ) Spacer(modifier = Modifier.weight(1F)) } diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/ConversationCoreViewModelFactory.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/ConversationCoreViewModelFactory.kt index 354b41216df..9f4bd62d862 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/ConversationCoreViewModelFactory.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/ConversationCoreViewModelFactory.kt @@ -85,6 +85,7 @@ import com.wire.kalium.logic.feature.conversation.ObserveConversationDetailsUseC import com.wire.kalium.logic.feature.conversation.ObserveConversationInteractionAvailabilityUseCase import com.wire.kalium.logic.feature.conversation.ObserveConversationUnderLegalHoldNotifiedUseCase import com.wire.kalium.logic.feature.conversation.ObserveDegradedConversationNotifiedUseCase +import com.wire.kalium.logic.feature.conversation.ObserveSelfUserHasViewerAccessOnConversationUseCase import com.wire.kalium.logic.feature.conversation.SendTypingEventUseCase import com.wire.kalium.logic.feature.conversation.SetNotifiedAboutConversationUnderLegalHoldUseCase import com.wire.kalium.logic.feature.conversation.SetUserInformedAboutVerificationUseCase @@ -192,6 +193,7 @@ class ConversationCoreViewModelFactory @Inject constructor( private val onlineEditor: OnlineEditor, private val featureFlags: KaliumConfigs, private val getWireCellsConfig: GetWireCellConfigurationUseCase, + private val observeSelfUserHasViewerAccess: ObserveSelfUserHasViewerAccessOnConversationUseCase, private val networkStateObserver: NetworkStateObserver, @CurrentAccount private val selfUserId: UserId, ) { @@ -234,6 +236,7 @@ class ConversationCoreViewModelFactory @Inject constructor( currentSessionFlowUseCase = currentSessionFlowUseCase, observeEstablishedCalls = observeEstablishedCalls, globalDataStore = globalDataStore, + observeSelfUserHasViewerAccess = observeSelfUserHasViewerAccess ) fun sendMessageViewModel(savedStateHandle: SavedStateHandle) = SendMessageViewModel( diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/MessageComposerViewState.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/MessageComposerViewState.kt index c6c15311abd..25bc058efbf 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/MessageComposerViewState.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/MessageComposerViewState.kt @@ -28,6 +28,7 @@ import com.wire.kalium.logic.data.id.MessageId data class MessageComposerViewState( val isFileSharingEnabled: Boolean = true, + val areAttachmentOptionsEnabled: Boolean = true, val interactionAvailability: InteractionAvailability = InteractionAvailability.ENABLED, val mentionSearchResult: List = listOf(), val mentionSearchQuery: String = String.EMPTY, diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/composer/MessageComposerViewModel.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/composer/MessageComposerViewModel.kt index f7a3735f8b5..90f52d8b813 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/composer/MessageComposerViewModel.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/composer/MessageComposerViewModel.kt @@ -25,6 +25,7 @@ import androidx.compose.runtime.setValue import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.ramcosta.composedestinations.generated.app.navArgs import com.wire.android.datastore.GlobalDataStore import com.wire.android.mapper.ContactMapper import com.wire.android.ui.home.conversations.ConversationNavArgs @@ -32,7 +33,6 @@ import com.wire.android.ui.home.conversations.InvalidLinkDialogState import com.wire.android.ui.home.conversations.MessageComposerViewState import com.wire.android.ui.home.conversations.VisitLinkDialogState import com.wire.android.ui.home.conversations.model.UIMessage -import com.ramcosta.composedestinations.generated.app.navArgs import com.wire.android.util.EMPTY import com.wire.android.util.FileManager import com.wire.android.util.dispatchers.DispatcherProvider @@ -45,9 +45,10 @@ import com.wire.kalium.logic.data.message.SelfDeletionTimer import com.wire.kalium.logic.data.user.OtherUser import com.wire.kalium.logic.feature.call.usecase.ObserveEstablishedCallsUseCase import com.wire.kalium.logic.feature.conversation.IsInteractionAvailableResult +import com.wire.kalium.logic.feature.conversation.MarkConversationAsReadLocallyUseCase import com.wire.kalium.logic.feature.conversation.MembersToMentionUseCase +import com.wire.kalium.logic.feature.conversation.ObserveSelfUserHasViewerAccessOnConversationUseCase import com.wire.kalium.logic.feature.conversation.ObserveConversationInteractionAvailabilityUseCase -import com.wire.kalium.logic.feature.conversation.MarkConversationAsReadLocallyUseCase import com.wire.kalium.logic.feature.conversation.SendTypingEventUseCase import com.wire.kalium.logic.feature.conversation.UpdateConversationReadDateUseCase import com.wire.kalium.logic.feature.message.ephemeral.EnqueueMessageSelfDeletionUseCase @@ -84,6 +85,7 @@ class MessageComposerViewModel( private val currentSessionFlowUseCase: CurrentSessionFlowUseCase, private val observeEstablishedCalls: ObserveEstablishedCallsUseCase, private val globalDataStore: GlobalDataStore, + private val observeSelfUserHasViewerAccess: ObserveSelfUserHasViewerAccessOnConversationUseCase, ) : ViewModel() { var messageComposerViewState = mutableStateOf(MessageComposerViewState()) @@ -113,6 +115,7 @@ class MessageComposerViewModel( initTempWritableImageUri() observeIsTypingAvailable() setFileSharingStatus() + observeAttachmentOptionsAvailability() getEnterToSendState() observeCallState() } @@ -196,6 +199,18 @@ class MessageComposerViewModel( } } + private fun observeAttachmentOptionsAvailability() { + viewModelScope.launch { + observeSelfUserHasViewerAccess(conversationId) + .collectLatest { areAttachmentOptionsEnabled -> + messageComposerViewState.value = messageComposerViewState.value.copy( + areAttachmentOptionsEnabled = areAttachmentOptionsEnabled + ) + } + } + } + + fun updateConversationReadDate(utcISO: String) { val instant = Instant.parse(utcISO) lastReadInstant = instant diff --git a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/AdditionalOptions.kt b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/AdditionalOptions.kt index 9d16458a4e0..726e21592b4 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/AdditionalOptions.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/AdditionalOptions.kt @@ -48,6 +48,7 @@ fun AdditionalOptionsMenu( isEditing: Boolean, isMentionActive: Boolean, isFileSharingEnabled: Boolean, + areAttachmentOptionsEnabled: Boolean, onAdditionalOptionsMenuClicked: () -> Unit, onMentionButtonClicked: (() -> Unit), onPingOptionClicked: () -> Unit, @@ -75,7 +76,8 @@ fun AdditionalOptionsMenu( onRichEditingButtonClicked = onRichEditingButtonClicked, onPingClicked = onPingOptionClicked, onDrawingModeClicked = onDrawingModeClicked, - isFileSharingEnabled = isFileSharingEnabled + isFileSharingEnabled = isFileSharingEnabled, + areAttachmentOptionsEnabled = areAttachmentOptionsEnabled, ) } @@ -94,6 +96,7 @@ fun AdditionalOptionsMenu( @Composable fun AdditionalOptionSubMenu( isFileSharingEnabled: Boolean, + areAttachmentOptionsEnabled: Boolean, optionsVisible: Boolean, onPermissionPermanentlyDenied: (type: ConversationActionPermissionType) -> Unit, onLocationPickerClicked: () -> Unit, @@ -115,6 +118,7 @@ fun AdditionalOptionSubMenu( tempWritableImageUri = tempWritableImageUri, tempWritableVideoUri = tempWritableVideoUri, isFileSharingEnabled = isFileSharingEnabled, + areAttachmentOptionsEnabled = areAttachmentOptionsEnabled, onRecordAudioMessageClicked = onRecordAudioMessageClicked, onLocationPickerClicked = onLocationPickerClicked, onPermissionPermanentlyDenied = onPermissionPermanentlyDenied, @@ -139,6 +143,7 @@ fun AttachmentAndAdditionalOptionsMenuItems( attachmentsVisible: Boolean, isMentionActive: Boolean, isFileSharingEnabled: Boolean, + areAttachmentOptionsEnabled: Boolean, onMentionButtonClicked: () -> Unit, onSelfDeletionOptionButtonClicked: (SelfDeletionTimer) -> Unit, modifier: Modifier = Modifier, @@ -163,7 +168,8 @@ fun AttachmentAndAdditionalOptionsMenuItems( onGifButtonClicked = onGifButtonClicked, onRichEditingButtonClicked = onRichEditingButtonClicked, onDrawingModeClicked = onDrawingModeClicked, - isFileSharingEnabled = isFileSharingEnabled + isFileSharingEnabled = isFileSharingEnabled, + areAttachmentOptionsEnabled = areAttachmentOptionsEnabled, ) } } diff --git a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/AttachmentOptions.kt b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/AttachmentOptions.kt index a9ea0fb8e8b..786527e0a6d 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/AttachmentOptions.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/AttachmentOptions.kt @@ -71,6 +71,7 @@ fun AttachmentOptionsComponent( tempWritableImageUri: Uri?, tempWritableVideoUri: Uri?, isFileSharingEnabled: Boolean, + areAttachmentOptionsEnabled: Boolean, onLocationPickerClicked: () -> Unit, onPermissionPermanentlyDenied: (type: ConversationActionPermissionType) -> Unit, modifier: Modifier = Modifier, @@ -80,6 +81,7 @@ fun AttachmentOptionsComponent( val attachmentOptions = buildAttachmentOptionItems( isFileSharingEnabled = isFileSharingEnabled, + areAttachmentOptionsEnabled = areAttachmentOptionsEnabled, tempWritableImageUri = tempWritableImageUri, tempWritableVideoUri = tempWritableVideoUri, onImagesPicked = onImagesPicked, @@ -163,7 +165,8 @@ fun AttachmentOptionsComponent( icon = option.icon, labelStyle = labelStyle, modifier = Modifier.scale(animatedScale), - text = stringResource(option.text) + text = stringResource(option.text), + enabled = option.isEnabled, ) { option.onClick() } } } @@ -278,6 +281,7 @@ private fun rememberCaptureVideoFlow( @Composable private fun buildAttachmentOptionItems( isFileSharingEnabled: Boolean, + areAttachmentOptionsEnabled: Boolean, tempWritableImageUri: Uri?, tempWritableVideoUri: Uri?, onImagesPicked: (List) -> Unit, @@ -311,39 +315,44 @@ private fun buildAttachmentOptionItems( with(localFeatureVisibilityFlags) { add( AttachmentOptionItem( - isFileSharingEnabled, - R.string.attachment_share_file, - R.drawable.ic_attach_file + shouldShow = isFileSharingEnabled, + isEnabled = areAttachmentOptionsEnabled, + text = R.string.attachment_share_file, + icon = R.drawable.ic_attach_file, ) { fileFlow.launch() } ) add( AttachmentOptionItem( - isFileSharingEnabled, - R.string.attachment_share_image, - R.drawable.ic_gallery + shouldShow = isFileSharingEnabled, + isEnabled = areAttachmentOptionsEnabled, + text = R.string.attachment_share_image, + icon = R.drawable.ic_gallery, ) { galleryFlow.launch() } ) add( AttachmentOptionItem( - isFileSharingEnabled, - R.string.attachment_take_photo, - R.drawable.ic_camera + shouldShow = isFileSharingEnabled, + isEnabled = areAttachmentOptionsEnabled, + text = R.string.attachment_take_photo, + icon = R.drawable.ic_camera, ) { takePictureFlow?.launch() } ) add( AttachmentOptionItem( - isFileSharingEnabled, - R.string.attachment_record_video, - R.drawable.ic_video + shouldShow = isFileSharingEnabled, + isEnabled = areAttachmentOptionsEnabled, + text = R.string.attachment_record_video, + icon = R.drawable.ic_video, ) { captureVideoFlow?.launch() } ) if (AudioMessagesIcon) { add( AttachmentOptionItem( - isFileSharingEnabled, - R.string.attachment_voice_message, - R.drawable.ic_mic_on, - onRecordAudioMessageClicked + shouldShow = isFileSharingEnabled, + isEnabled = areAttachmentOptionsEnabled, + text = R.string.attachment_voice_message, + icon = R.drawable.ic_mic_on, + onClick = onRecordAudioMessageClicked, ) ) } @@ -363,6 +372,7 @@ private fun buildAttachmentOptionItems( private data class AttachmentOptionItem( val shouldShow: Boolean = true, + val isEnabled: Boolean = true, @StringRes val text: Int, @DrawableRes val icon: Int, val onClick: () -> Unit @@ -377,6 +387,7 @@ fun PreviewAttachmentComponents() { onImagesPicked = {}, onAttachmentPicked = {}, isFileSharingEnabled = true, + areAttachmentOptionsEnabled = true, tempWritableImageUri = null, tempWritableVideoUri = null, onRecordAudioMessageClicked = {}, @@ -398,6 +409,7 @@ fun PreviewAttachmentOptionsComponentSmallScreen() { onAttachmentPicked = {}, onImagesPicked = {}, isFileSharingEnabled = true, + areAttachmentOptionsEnabled = true, tempWritableImageUri = null, tempWritableVideoUri = null, onRecordAudioMessageClicked = {}, @@ -420,6 +432,7 @@ fun PreviewAttachmentOptionsComponentNormalScreen() { onAttachmentPicked = {}, onImagesPicked = {}, isFileSharingEnabled = true, + areAttachmentOptionsEnabled = true, tempWritableImageUri = null, tempWritableVideoUri = null, onRecordAudioMessageClicked = {}, @@ -442,6 +455,7 @@ fun PreviewAttachmentOptionsComponentTabledScreen() { onAttachmentPicked = {}, onImagesPicked = {}, isFileSharingEnabled = true, + areAttachmentOptionsEnabled = true, tempWritableImageUri = null, tempWritableVideoUri = null, onRecordAudioMessageClicked = {}, diff --git a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/EnabledMessageComposer.kt b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/EnabledMessageComposer.kt index 93d95467d34..8c116c6f509 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/EnabledMessageComposer.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/EnabledMessageComposer.kt @@ -388,8 +388,13 @@ fun EnabledMessageComposer( additionalOptionStateHolder.toRichTextEditing() }, onCloseRichEditingButtonClicked = additionalOptionStateHolder::toAttachmentAndAdditionalOptionsMenu, - onDrawingModeClicked = openDrawingCanvas, - isFileSharingEnabled = messageComposerViewState.value.isFileSharingEnabled + onDrawingModeClicked = { + if (messageComposerViewState.value.areAttachmentOptionsEnabled) { + openDrawingCanvas() + } + }, + isFileSharingEnabled = messageComposerViewState.value.isFileSharingEnabled, + areAttachmentOptionsEnabled = messageComposerViewState.value.areAttachmentOptionsEnabled, ) } } @@ -453,6 +458,7 @@ fun EnabledMessageComposer( AdditionalOptionSubMenu( optionsVisible = inputStateHolder.optionsVisible, isFileSharingEnabled = messageComposerViewState.value.isFileSharingEnabled, + areAttachmentOptionsEnabled = messageComposerViewState.value.areAttachmentOptionsEnabled, additionalOptionsState = additionalOptionStateHolder.additionalOptionsSubMenuState, onRecordAudioMessageClicked = { if (!messageComposerViewState.value.isCallOngoing) { diff --git a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageComposeActions.kt b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageComposeActions.kt index bfb2e513f7e..1715cf969f3 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageComposeActions.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageComposeActions.kt @@ -58,6 +58,7 @@ fun MessageComposeActions( onGifButtonClicked: () -> Unit, onRichEditingButtonClicked: () -> Unit, isFileSharingEnabled: Boolean, + areAttachmentOptionsEnabled: Boolean, isMentionActive: Boolean = true, onDrawingModeClicked: () -> Unit ) { @@ -81,7 +82,8 @@ fun MessageComposeActions( onPingButtonClicked = onPingButtonClicked, onMentionButtonClicked = onMentionButtonClicked, onDrawingModeClicked = onDrawingModeClicked, - isFileSharingEnabled = isFileSharingEnabled + isFileSharingEnabled = isFileSharingEnabled, + areAttachmentOptionsEnabled = areAttachmentOptionsEnabled, ) } } @@ -91,6 +93,7 @@ private fun ComposingActions( conversationId: ConversationId, selectedOption: AdditionalOptionSelectItem, isFileSharingEnabled: Boolean, + areAttachmentOptionsEnabled: Boolean, attachmentsVisible: Boolean, isMentionActive: Boolean, onAdditionalOptionButtonClicked: () -> Unit, @@ -120,7 +123,10 @@ private fun ComposingActions( onRichEditingButtonClicked ) if (DrawingIcon && isFileSharingEnabled) { - DrawingModeAction(onDrawingModeClicked) + DrawingModeAction( + onButtonClicked = onDrawingModeClicked, + isEnabled = areAttachmentOptionsEnabled, + ) } if (EmojiIcon) AddEmojiAction({}) if (GifIcon) AddGifAction(onGifButtonClicked) @@ -175,12 +181,12 @@ private fun RichTextEditingAction(isSelected: Boolean, onButtonClicked: () -> Un } @Composable -private fun DrawingModeAction(onButtonClicked: () -> Unit) { +private fun DrawingModeAction(onButtonClicked: () -> Unit, isEnabled: Boolean) { WireSecondaryIconButton( onButtonClicked = onButtonClicked, clickBlockParams = ClickBlockParams(blockWhenSyncing = true, blockWhenConnecting = true), iconResource = R.drawable.ic_drawing, - state = WireButtonState.Default, + state = if (isEnabled) WireButtonState.Default else WireButtonState.Disabled, contentDescription = R.string.content_description_conversation_enable_drawing_mode ) } @@ -291,7 +297,7 @@ fun PreviewMessageActionsBox() { .height(dimensions().spacing56x) ) { AdditionalOptionButton(isSelected = false, onClick = {}) - DrawingModeAction {} + DrawingModeAction(onButtonClicked = {}, isEnabled = true) RichTextEditingAction(true) { } AddEmojiAction {} AddGifAction {} diff --git a/app/src/test/kotlin/com/wire/android/ui/home/conversations/composer/MessageComposerViewModelArrangement.kt b/app/src/test/kotlin/com/wire/android/ui/home/conversations/composer/MessageComposerViewModelArrangement.kt index 834bc59b8be..7df3d59bcfa 100644 --- a/app/src/test/kotlin/com/wire/android/ui/home/conversations/composer/MessageComposerViewModelArrangement.kt +++ b/app/src/test/kotlin/com/wire/android/ui/home/conversations/composer/MessageComposerViewModelArrangement.kt @@ -20,6 +20,7 @@ package com.wire.android.ui.home.conversations.composer import android.net.Uri import androidx.lifecycle.SavedStateHandle +import com.ramcosta.composedestinations.generated.app.navArgs import com.wire.android.config.TestDispatcherProvider import com.wire.android.config.mockUri import com.wire.android.datastore.GlobalDataStore @@ -37,7 +38,6 @@ import com.wire.android.ui.home.conversations.model.MessageStatus import com.wire.android.ui.home.conversations.model.MessageTime import com.wire.android.ui.home.conversations.model.UIMessage import com.wire.android.ui.home.conversations.model.UIMessageContent -import com.ramcosta.composedestinations.generated.app.navArgs import com.wire.android.util.FileManager import com.wire.android.util.ui.UIText import com.wire.kalium.logic.configuration.FileSharingStatus @@ -46,6 +46,7 @@ import com.wire.kalium.logic.data.conversation.Conversation import com.wire.kalium.logic.data.conversation.ConversationDetails import com.wire.kalium.logic.data.conversation.InteractionAvailability import com.wire.kalium.logic.data.id.ConversationId +import com.wire.kalium.logic.data.id.TeamId import com.wire.kalium.logic.data.sync.SyncState import com.wire.kalium.logic.data.user.AssetId import com.wire.kalium.logic.data.user.ConnectionState @@ -61,6 +62,7 @@ import com.wire.kalium.logic.feature.conversation.IsInteractionAvailableResult import com.wire.kalium.logic.feature.conversation.MarkConversationAsReadLocallyUseCase import com.wire.kalium.logic.feature.conversation.MarkConversationAsReadResult import com.wire.kalium.logic.feature.conversation.MembersToMentionUseCase +import com.wire.kalium.logic.feature.conversation.ObserveSelfUserHasViewerAccessOnConversationUseCase import com.wire.kalium.logic.feature.conversation.ObserveConversationInteractionAvailabilityUseCase import com.wire.kalium.logic.feature.conversation.SendTypingEventUseCase import com.wire.kalium.logic.feature.conversation.UpdateConversationReadDateUseCase @@ -100,6 +102,7 @@ internal class MessageComposerViewModelArrangement { coEvery { currentSessionFlowUseCase() } returns flowOf(CurrentSessionResult.Success(AccountInfo.Valid(TestUser.USER_ID))) + coEvery { observeSelfUserHasViewerAccessOnConversationUseCase(any()) } returns flowOf(true) coEvery { globalDataStore.enterToSendFlow() } returns flowOf(false) coEvery { observeEstablishedCalls() } returns emptyFlow() coEvery { markConversationAsReadLocallyUseCase(any(), any()) } returns MarkConversationAsReadResult.Success(false) @@ -120,6 +123,9 @@ internal class MessageComposerViewModelArrangement { @MockK private lateinit var observeConversationInteractionAvailabilityUseCase: ObserveConversationInteractionAvailabilityUseCase + @MockK + private lateinit var observeSelfUserHasViewerAccessOnConversationUseCase: ObserveSelfUserHasViewerAccessOnConversationUseCase + @MockK private lateinit var updateConversationReadDateUseCase: UpdateConversationReadDateUseCase @@ -176,7 +182,7 @@ internal class MessageComposerViewModelArrangement { currentSessionFlowUseCase = currentSessionFlowUseCase, observeEstablishedCalls = observeEstablishedCalls, globalDataStore = globalDataStore, - ) + observeSelfUserHasViewerAccess = observeSelfUserHasViewerAccessOnConversationUseCase, ) } fun withSuccessfulViewModelInit( @@ -197,6 +203,10 @@ internal class MessageComposerViewModelArrangement { coEvery { currentSessionFlowUseCase() } returns resultFlow } + fun withAttachmentOptionsAvailability(available: Boolean) = apply { + coEvery { observeSelfUserHasViewerAccessOnConversationUseCase(any()) } returns flowOf(available) + } + fun arrange() = this to viewModel } @@ -225,14 +235,16 @@ internal fun withMockConversationDetailsOneOnOne( internal fun mockConversationDetailsGroup( conversationName: String, - mockedConversationId: ConversationId = ConversationId("someId", "someDomain") + mockedConversationId: ConversationId = ConversationId("someId", "someDomain"), + teamId: TeamId? = TestConversation.GROUP().teamId, + wireCell: String? = null, ) = ConversationDetails.Group.Regular( conversation = TestConversation.GROUP() - .copy(name = conversationName, id = mockedConversationId), + .copy(name = conversationName, id = mockedConversationId, teamId = teamId), hasOngoingCall = false, isSelfUserMember = true, selfRole = Conversation.Member.Role.Member, - wireCell = null, + wireCell = wireCell, ) internal fun mockUITextMessage(id: String = "someId", userName: String = "mockUserName"): UIMessage { @@ -276,3 +288,5 @@ internal fun mockUIAudioMessage(id: String = "someId", userName: String = "mockU ) } } + + diff --git a/app/src/test/kotlin/com/wire/android/ui/home/conversations/composer/MessageComposerViewModelTest.kt b/app/src/test/kotlin/com/wire/android/ui/home/conversations/composer/MessageComposerViewModelTest.kt index a65c98da7ae..cf8471b6fea 100644 --- a/app/src/test/kotlin/com/wire/android/ui/home/conversations/composer/MessageComposerViewModelTest.kt +++ b/app/src/test/kotlin/com/wire/android/ui/home/conversations/composer/MessageComposerViewModelTest.kt @@ -40,6 +40,30 @@ import org.junit.jupiter.api.extension.ExtendWith @Suppress("LargeClass") class MessageComposerViewModelTest { + @Test + fun `given guest in foreign-team cells conversation when init then attachment options are disabled`() = runTest { + val (_, viewModel) = MessageComposerViewModelArrangement() + .withSuccessfulViewModelInit() + .withAttachmentOptionsAvailability(false) + .arrange() + + advanceUntilIdle() + + assertEquals(false, viewModel.messageComposerViewState.value.areAttachmentOptionsEnabled) + } + + @Test + fun `given guest in self-team cells conversation when init then attachment options stay enabled`() = runTest { + val (_, viewModel) = MessageComposerViewModelArrangement() + .withSuccessfulViewModelInit() + .withAttachmentOptionsAvailability(true) + .arrange() + + advanceUntilIdle() + + assertTrue(viewModel.messageComposerViewState.value.areAttachmentOptionsEnabled) + } + @Test fun `given that user types a text message, when invoked typing invoked, then send typing event is called`() = runTest { // given diff --git a/core/ui-common/src/main/kotlin/com/wire/android/ui/common/button/WireSecondaryIconButton.kt b/core/ui-common/src/main/kotlin/com/wire/android/ui/common/button/WireSecondaryIconButton.kt index 8b0cfd1a91b..9b1455e4885 100644 --- a/core/ui-common/src/main/kotlin/com/wire/android/ui/common/button/WireSecondaryIconButton.kt +++ b/core/ui-common/src/main/kotlin/com/wire/android/ui/common/button/WireSecondaryIconButton.kt @@ -31,13 +31,14 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Shape import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.DpSize import androidx.compose.ui.unit.dp import com.wire.android.model.ClickBlockParams import com.wire.android.ui.common.R import com.wire.android.ui.common.dimensions +import com.wire.android.ui.common.preview.MultipleThemePreviews +import com.wire.android.ui.theme.WireTheme import com.wire.android.ui.theme.wireDimensions @Composable @@ -79,38 +80,61 @@ fun WireSecondaryIconButton( ) } -@Preview +@MultipleThemePreviews @Composable fun PreviewWireSecondaryIconButton() { - WireSecondaryIconButton( - {}, - loading = false, - iconResource = R.drawable.ic_close, - contentDescription = 0 - ) + WireTheme { + WireSecondaryIconButton( + {}, + loading = false, + iconResource = R.drawable.ic_close, + contentDescription = 0 + ) + } } -@Preview +@MultipleThemePreviews @Composable fun PreviewWireSecondaryIconButtonLoading() { - WireSecondaryIconButton( - {}, - loading = true, - iconResource = R.drawable.ic_close, - contentDescription = 0 - ) + WireTheme { + WireSecondaryIconButton( + {}, + loading = true, + iconResource = R.drawable.ic_close, + contentDescription = 0 + ) + } } -@Preview +@MultipleThemePreviews @Composable fun PreviewWireSecondaryIconButtonRound() { - WireSecondaryIconButton( - {}, - loading = false, - iconResource = R.drawable.ic_close, - contentDescription = 0, - shape = CircleShape, - minSize = DpSize(40.dp, 40.dp), - minClickableSize = DpSize(48.dp, 48.dp) - ) + WireTheme { + WireSecondaryIconButton( + {}, + loading = false, + iconResource = R.drawable.ic_close, + contentDescription = 0, + shape = CircleShape, + minSize = DpSize(40.dp, 40.dp), + minClickableSize = DpSize(48.dp, 48.dp) + ) + } +} + +@MultipleThemePreviews +@Composable +fun PreviewWireSecondaryIconButtonRoundDisabled() { + WireTheme { + WireSecondaryIconButton( + {}, + loading = false, + iconResource = R.drawable.ic_close, + contentDescription = 0, + shape = CircleShape, + minSize = DpSize(40.dp, 40.dp), + minClickableSize = DpSize(48.dp, 48.dp), + state = WireButtonState.Disabled + ) + } } diff --git a/core/ui-common/src/main/kotlin/com/wire/android/ui/theme/WireColorScheme.kt b/core/ui-common/src/main/kotlin/com/wire/android/ui/theme/WireColorScheme.kt index 664dc71799b..e2eb6da271b 100644 --- a/core/ui-common/src/main/kotlin/com/wire/android/ui/theme/WireColorScheme.kt +++ b/core/ui-common/src/main/kotlin/com/wire/android/ui/theme/WireColorScheme.kt @@ -277,7 +277,7 @@ private val DarkWireColorScheme = WireColorScheme( secondaryButtonEnabled = WireColorPalette.Gray90, onSecondaryButtonEnabled = Color.White, secondaryButtonEnabledOutline = WireColorPalette.Gray100, secondaryButtonDisabled = WireColorPalette.Gray95, onSecondaryButtonDisabled = WireColorPalette.Gray50, - secondaryButtonDisabledOutline = WireColorPalette.Gray95, + secondaryButtonDisabledOutline = WireColorPalette.Gray90, secondaryButtonSelected = WireColorPalette.DarkBlue800, onSecondaryButtonSelected = Color.White, secondaryButtonSelectedOutline = WireColorPalette.DarkBlue600, secondaryButtonRipple = Color.White,