Skip to content

Commit

Permalink
fix: asset restriction [WPB-9947] (#3114) (#3147)
Browse files Browse the repository at this point in the history
Signed-off-by: MohamadJaara <[email protected]>
  • Loading branch information
MohamadJaara authored Jul 2, 2024
1 parent d37d51b commit 1a35424
Show file tree
Hide file tree
Showing 12 changed files with 190 additions and 56 deletions.
3 changes: 2 additions & 1 deletion app/src/main/kotlin/com/wire/android/ui/WireActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ import com.wire.android.ui.destinations.SelfUserProfileScreenDestination
import com.wire.android.ui.destinations.WelcomeScreenDestination
import com.wire.android.ui.home.E2EIRequiredDialog
import com.wire.android.ui.home.E2EISnoozeDialog
import com.wire.android.ui.home.FeatureFlagState
import com.wire.android.ui.home.appLock.LockCodeTimeManager
import com.wire.android.ui.home.sync.FeatureFlagNotificationViewModel
import com.wire.android.ui.theme.ThemeOption
Expand Down Expand Up @@ -295,7 +296,7 @@ class WireActivity : AppCompatActivity() {
} else {
if (showFileSharingDialog) {
FileRestrictionDialog(
isFileSharingEnabled = isFileSharingEnabledState,
isFileSharingEnabled = (isFileSharingState !is FeatureFlagState.FileSharingState.DisabledByTeam),
hideDialogStatus = featureFlagNotificationViewModel::dismissFileSharingDialog
)
}
Expand Down
11 changes: 7 additions & 4 deletions app/src/main/kotlin/com/wire/android/ui/home/FeatureFlagState.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@ import kotlin.time.Duration

data class FeatureFlagState(
val showFileSharingDialog: Boolean = false,
val isFileSharingEnabledState: Boolean = true,
val fileSharingRestrictedState: SharingRestrictedState? = null,
val isFileSharingState: FileSharingState = FileSharingState.NoUser,
val shouldShowGuestRoomLinkDialog: Boolean = false,
val shouldShowTeamAppLockDialog: Boolean = false,
val isTeamAppLockEnabled: Boolean = false,
Expand All @@ -38,8 +37,12 @@ data class FeatureFlagState(
val e2EISnoozeInfo: E2EISnooze? = null,
val showCallEndedBecauseOfConversationDegraded: Boolean = false
) {
enum class SharingRestrictedState {
NONE, NO_USER, RESTRICTED_IN_TEAM

sealed interface FileSharingState {
data object NoUser : FileSharingState
data object AllowAll : FileSharingState
data class AllowSome(val allowedList: List<String>) : FileSharingState
data object DisabledByTeam : FileSharingState
}

data class E2EISnooze(val timeLeft: Duration)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ sealed class ConversationSnackbarMessages(override val uiText: UIText) : SnackBa
object ErrorDownloadingAsset : ConversationSnackbarMessages(UIText.StringResource(R.string.error_conversation_downloading_asset))
object ErrorOpeningAssetFile : ConversationSnackbarMessages(UIText.StringResource(R.string.error_conversation_opening_asset_file))
object ErrorDeletingMessage : ConversationSnackbarMessages(UIText.StringResource(R.string.error_conversation_deleting_message))
data object ErrorAssetRestriction : ConversationSnackbarMessages(UIText.StringResource(R.string.restricted_asset_error_toast_message))
data class ErrorMaxAssetSize(val maxLimitInMB: Int) :
ConversationSnackbarMessages(UIText.StringResource(R.string.error_conversation_max_asset_size_limit, maxLimitInMB))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import com.wire.kalium.logic.data.id.QualifiedID
import com.wire.kalium.logic.data.message.SelfDeletionTimer
import com.wire.kalium.logic.data.user.OtherUser
import com.wire.kalium.logic.feature.asset.GetAssetSizeLimitUseCase
import com.wire.kalium.logic.feature.asset.ScheduleNewAssetMessageResult
import com.wire.kalium.logic.feature.asset.ScheduleNewAssetMessageUseCase
import com.wire.kalium.logic.feature.conversation.InteractionAvailability
import com.wire.kalium.logic.feature.conversation.IsInteractionAvailableResult
Expand Down Expand Up @@ -287,6 +288,7 @@ class MessageComposerViewModel @Inject constructor(
}
}

@Suppress("LongMethod")
internal fun sendAttachment(attachmentBundle: AssetBundle?) {
viewModelScope.launch {
withContext(dispatchers.io()) {
Expand All @@ -306,7 +308,9 @@ class MessageComposerViewModel @Inject constructor(
assetDataSize = dataSize,
assetMimeType = mimeType,
audioLengthInMs = 0L
)
).also {
handleAssetSendingResult(it)
}
}

AttachmentType.VIDEO,
Expand All @@ -325,7 +329,9 @@ class MessageComposerViewModel @Inject constructor(
dataPath = dataPath,
mimeType = mimeType
)
)
).also {
handleAssetSendingResult(it)
}
} catch (e: OutOfMemoryError) {
appLogger.e("There was an OutOfMemory error while uploading the asset")
onSnackbarMessage(ConversationSnackbarMessages.ErrorSendingAsset)
Expand All @@ -337,6 +343,21 @@ class MessageComposerViewModel @Inject constructor(
}
}

private suspend fun handleAssetSendingResult(result: ScheduleNewAssetMessageResult) {
when (result) {
is ScheduleNewAssetMessageResult.Failure.Generic,
is ScheduleNewAssetMessageResult.Success -> {
/* no-op */
}

ScheduleNewAssetMessageResult.Failure.DisabledByTeam,
ScheduleNewAssetMessageResult.Failure.RestrictedFileType -> {
withContext(dispatchers.main()) {
onSnackbarMessage(ConversationSnackbarMessages.ErrorAssetRestriction)
}
}
}
}
fun retrySendingMessage(messageId: String) {
viewModelScope.launch {
retryFailedMessage(messageId = messageId, conversationId = conversationId)
Expand Down Expand Up @@ -374,13 +395,12 @@ class MessageComposerViewModel @Inject constructor(
}

private fun setFileSharingStatus() {
// TODO: handle restriction when sending assets
viewModelScope.launch {
messageComposerViewState.value = when (isFileSharingEnabled().state) {
FileSharingStatus.Value.Disabled,
is FileSharingStatus.Value.EnabledSome ->
FileSharingStatus.Value.Disabled ->
messageComposerViewState.value.copy(isFileSharingEnabled = false)

is FileSharingStatus.Value.EnabledSome,
FileSharingStatus.Value.EnabledAll ->
messageComposerViewState.value.copy(isFileSharingEnabled = true)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ class FeatureFlagNotificationViewModel @Inject constructor(
currentUserId = null
appLogger.e("Failure while getting current session from FeatureFlagNotificationViewModel")
featureFlagState = FeatureFlagState( // no session, clear feature flag state to default and set NO_USER
fileSharingRestrictedState = FeatureFlagState.SharingRestrictedState.NO_USER
isFileSharingState = FeatureFlagState.FileSharingState.NoUser
)
}

Expand Down Expand Up @@ -115,22 +115,17 @@ class FeatureFlagNotificationViewModel @Inject constructor(

private suspend fun setFileSharingState(userId: UserId) {
coreLogic.getSessionScope(userId).observeFileSharingStatus().collect { fileSharingStatus ->
fileSharingStatus.state?.let {
// TODO: handle restriction when sending assets
val (fileSharingRestrictedState, state) = if (it is FileSharingStatus.Value.EnabledAll) {
FeatureFlagState.SharingRestrictedState.NONE to true
} else {
FeatureFlagState.SharingRestrictedState.RESTRICTED_IN_TEAM to false
}

featureFlagState = featureFlagState.copy(
fileSharingRestrictedState = fileSharingRestrictedState,
isFileSharingEnabledState = state
val state: FeatureFlagState.FileSharingState = when (fileSharingStatus.state) {
FileSharingStatus.Value.Disabled -> FeatureFlagState.FileSharingState.DisabledByTeam
FileSharingStatus.Value.EnabledAll -> FeatureFlagState.FileSharingState.AllowAll
is FileSharingStatus.Value.EnabledSome -> FeatureFlagState.FileSharingState.AllowSome(
(fileSharingStatus.state as FileSharingStatus.Value.EnabledSome).allowedType
)
}
fileSharingStatus.isStatusChanged?.let {
featureFlagState = featureFlagState.copy(showFileSharingDialog = it)
}
featureFlagState = featureFlagState.copy(
isFileSharingState = state,
showFileSharingDialog = fileSharingStatus.isStatusChanged ?: false
)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Parcelable
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
Expand All @@ -14,6 +15,7 @@ import androidx.core.app.ShareCompat
import androidx.core.net.toUri
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.wire.android.R
import com.wire.android.appLogger
import com.wire.android.mapper.UserTypeMapper
import com.wire.android.mapper.toUIPreview
Expand Down Expand Up @@ -52,6 +54,7 @@ import com.wire.kalium.logic.feature.selfDeletingMessages.ObserveSelfDeletionTim
import com.wire.kalium.logic.feature.selfDeletingMessages.PersistNewSelfDeletionTimerUseCase
import com.wire.kalium.logic.feature.user.GetSelfUserUseCase
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.Job
Expand All @@ -73,6 +76,7 @@ import javax.inject.Inject
@OptIn(FlowPreview::class)
@Suppress("LongParameterList", "TooManyFunctions")
class ImportMediaAuthenticatedViewModel @Inject constructor(
@ApplicationContext private val context: Context,
private val getSelf: GetSelfUserUseCase,
private val userTypeMapper: UserTypeMapper,
private val observeConversationListDetails: ObserveConversationListDetailsUseCase,
Expand Down Expand Up @@ -346,13 +350,26 @@ class ImportMediaAuthenticatedViewModel @Inject constructor(
mimeType = importedAsset.mimeType
)
).also {
val logConversationId = conversation.conversationId.toLogString()
if (it is ScheduleNewAssetMessageResult.Failure) {
appLogger.e("Failed to import asset message to " +
"conversationId=$logConversationId")
} else {
appLogger.d("Success importing asset message to " +
"conversationId=$logConversationId")
when (it) {
is ScheduleNewAssetMessageResult.Success -> appLogger.d(
"Successfully imported asset message to conversationId=${conversation.conversationId.toLogString()}"
)

is ScheduleNewAssetMessageResult.Failure.Generic -> appLogger.e(
"Failed to import asset message to conversationId=${conversation.conversationId.toLogString()}"
)

ScheduleNewAssetMessageResult.Failure.RestrictedFileType,
ScheduleNewAssetMessageResult.Failure.DisabledByTeam -> {
Toast.makeText(
context,
R.string.restricted_asset_error_toast_message,
Toast.LENGTH_SHORT
).show()
appLogger.e(
"Failed to import asset message to conversationId=${conversation.conversationId.toLogString()}"
)
}
}
}
}
Expand Down Expand Up @@ -482,5 +499,10 @@ data class ImportMediaAuthenticatedState(
val isImporting: Boolean = false,
val shareableConversationListState: ShareableConversationListState = ShareableConversationListState(),
val selectedConversationItem: List<ConversationItem> = emptyList(),
val selfDeletingTimer: SelfDeletionTimer = SelfDeletionTimer.Enabled(null)
val selfDeletingTimer: SelfDeletionTimer = SelfDeletionTimer.Enabled(null),
val assetSendError: AssetSendError? = null
)

enum class AssetSendError {
DISABLED_BY_TEAM, RESTRICTED_ASSET
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,16 +81,15 @@ fun ImportMediaScreen(
navigator: Navigator,
featureFlagNotificationViewModel: FeatureFlagNotificationViewModel = hiltViewModel()
) {
when (val fileSharingRestrictedState =
featureFlagNotificationViewModel.featureFlagState.fileSharingRestrictedState) {
FeatureFlagState.SharingRestrictedState.NO_USER -> {
when (val fileSharingRestrictedState = featureFlagNotificationViewModel.featureFlagState.isFileSharingState) {
FeatureFlagState.FileSharingState.NoUser -> {
ImportMediaLoggedOutContent(
fileSharingRestrictedState = fileSharingRestrictedState,
navigateBack = navigator::navigateBack
)
}

FeatureFlagState.SharingRestrictedState.RESTRICTED_IN_TEAM -> {
FeatureFlagState.FileSharingState.DisabledByTeam -> {
val importMediaViewModel: ImportMediaAuthenticatedViewModel = hiltViewModel()
ImportMediaRestrictedContent(
fileSharingRestrictedState = fileSharingRestrictedState,
Expand All @@ -99,7 +98,8 @@ fun ImportMediaScreen(
)
}

FeatureFlagState.SharingRestrictedState.NONE -> {
FeatureFlagState.FileSharingState.AllowAll,
is FeatureFlagState.FileSharingState.AllowSome -> {
val importMediaViewModel: ImportMediaAuthenticatedViewModel = hiltViewModel()
ImportMediaRegularContent(
importMediaAuthenticatedState = importMediaViewModel.importMediaState,
Expand Down Expand Up @@ -127,18 +127,14 @@ fun ImportMediaScreen(
}
}
}

null -> {
// state is not calculated yet, need to wait to avoid crash while requesting currentUser where it's absent
}
}

BackHandler { navigator.navigateBack() }
}

@Composable
fun ImportMediaRestrictedContent(
fileSharingRestrictedState: FeatureFlagState.SharingRestrictedState,
fileSharingRestrictedState: FeatureFlagState.FileSharingState,
importMediaAuthenticatedState: ImportMediaAuthenticatedState,
navigateBack: () -> Unit,
) {
Expand Down Expand Up @@ -230,7 +226,7 @@ fun ImportMediaRegularContent(

@Composable
fun ImportMediaLoggedOutContent(
fileSharingRestrictedState: FeatureFlagState.SharingRestrictedState,
fileSharingRestrictedState: FeatureFlagState.FileSharingState,
navigateBack: () -> Unit,
) {
WireScaffold(
Expand All @@ -255,7 +251,7 @@ fun ImportMediaLoggedOutContent(
@Composable
fun FileSharingRestrictedContent(
internalPadding: PaddingValues,
sharingRestrictedState: FeatureFlagState.SharingRestrictedState,
sharingRestrictedState: FeatureFlagState.FileSharingState,
openWireAction: () -> Unit
) {
val context = LocalContext.current
Expand All @@ -270,7 +266,7 @@ fun FileSharingRestrictedContent(
.padding(horizontal = dimensions().spacing48x)
) {
val textRes =
if (sharingRestrictedState == FeatureFlagState.SharingRestrictedState.NO_USER) {
if (sharingRestrictedState == FeatureFlagState.FileSharingState.NoUser) {
R.string.file_sharing_restricted_description_no_users
} else {
R.string.file_sharing_restricted_description_by_team
Expand All @@ -284,7 +280,7 @@ fun FileSharingRestrictedContent(

Spacer(modifier = Modifier.height(dimensions().spacing16x))

if (sharingRestrictedState == FeatureFlagState.SharingRestrictedState.NO_USER) {
if (sharingRestrictedState == FeatureFlagState.FileSharingState.NoUser) {
WirePrimaryButton(
onClick = openWireAction,
text = stringResource(R.string.file_sharing_restricted_button_text_no_users),
Expand Down Expand Up @@ -442,14 +438,14 @@ private fun SnackBarMessage(
@Preview(showBackground = true)
@Composable
fun PreviewImportMediaScreenLoggedOut() {
ImportMediaLoggedOutContent(FeatureFlagState.SharingRestrictedState.NO_USER) {}
ImportMediaLoggedOutContent(FeatureFlagState.FileSharingState.NoUser) {}
}

@Preview(showBackground = true)
@Composable
fun PreviewImportMediaScreenRestricted() {
ImportMediaRestrictedContent(
FeatureFlagState.SharingRestrictedState.RESTRICTED_IN_TEAM,
FeatureFlagState.FileSharingState.DisabledByTeam,
ImportMediaAuthenticatedState()
) {}
}
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1286,4 +1286,6 @@
<string name="e2ei_certificate_details_certificate_copied_to_clipboard">Certificate copied to clipboard</string>
<string name="call_ended_because_of_verification_title">Conversation no longer verified</string>
<string name="call_ended_because_of_verification_message">The call was disconnected because at least one participant is no longer a verified contact.</string>

<string name="restricted_asset_error_toast_message">Sending of files is forbidden due to company restrictions</string>
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,21 @@ internal class MessageComposerViewModelArrangement {
} returns Unit
}

fun withSendAssetsResult(result: ScheduleNewAssetMessageResult) = apply {
coEvery {
sendAssetMessage(
any(),
any(),
any(),
any(),
any(),
any(),
any(),
any()
)
} returns result
}

fun withFailureOnDeletingMessages() = apply {
coEvery { deleteMessage(any(), any(), any()) } returns Either.Left(CoreFailure.Unknown(null))
return this
Expand Down
Loading

0 comments on commit 1a35424

Please sign in to comment.