Skip to content

Commit

Permalink
fix: asset restriction [WPB-9947] (#3114) (#3147) (#3150)
Browse files Browse the repository at this point in the history
  • Loading branch information
MohamadJaara authored Jul 3, 2024
1 parent ddc4708 commit 0f8ae93
Show file tree
Hide file tree
Showing 12 changed files with 192 additions and 63 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 @@ -85,6 +85,7 @@ import com.wire.android.ui.home.E2EICertificateRevokedDialog
import com.wire.android.ui.home.E2EIRequiredDialog
import com.wire.android.ui.home.E2EIResultDialog
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.legalhold.dialog.deactivated.LegalHoldDeactivatedDialog
Expand Down Expand Up @@ -328,7 +329,7 @@ class WireActivity : AppCompatActivity() {
}
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 @@ -23,8 +23,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 shouldShowE2eiCertificateRevokedDialog: Boolean = false,
val shouldShowTeamAppLockDialog: Boolean = false,
Expand All @@ -40,8 +39,12 @@ data class FeatureFlagState(
val showCallEndedBecauseOfConversationDegraded: Boolean = false,
val startGettingE2EICertificate: 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 @@ -29,6 +29,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 @@ -347,6 +347,7 @@ class MessageComposerViewModel @Inject constructor(
}
}

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

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

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

ScheduleNewAssetMessageResult.Failure.DisabledByTeam,
ScheduleNewAssetMessageResult.Failure.RestrictedFileType -> {
withContext(dispatchers.main()) {
onSnackbarMessage(ConversationSnackbarMessages.ErrorAssetRestriction)
}
}
}
}
private fun CoreFailure.handleLegalHoldFailureAfterSendingMessage() = also {
if (this is LegalHoldEnabledForConversationFailure) {
sureAboutMessagingDialogState = SureAboutMessagingDialogState.Visible.ConversationUnderLegalHold.AfterSending(this.messageId)
Expand All @@ -406,12 +428,6 @@ class MessageComposerViewModel @Inject constructor(
private fun Either<CoreFailure, Unit>.handleLegalHoldFailureAfterSendingMessage() =
onFailure { it.handleLegalHoldFailureAfterSendingMessage() }

private fun ScheduleNewAssetMessageResult.handleLegalHoldFailureAfterSendingMessage() = also {
if (it is ScheduleNewAssetMessageResult.Failure) {
it.coreFailure.handleLegalHoldFailureAfterSendingMessage()
}
}

fun retrySendingMessage(messageId: String) {
viewModelScope.launch {
retryFailedMessage(messageId = messageId, conversationId = conversationId)
Expand Down Expand Up @@ -449,13 +465,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 @@ -87,14 +87,14 @@ class FeatureFlagNotificationViewModel @Inject constructor(
currentUserId = null
appLogger.i("$TAG: Failure while getting current session")
featureFlagState = FeatureFlagState( // no session, clear feature flag state to default and set NO_USER
fileSharingRestrictedState = FeatureFlagState.SharingRestrictedState.NO_USER
isFileSharingState = FeatureFlagState.FileSharingState.NoUser
)
}

currentSessionResult is CurrentSessionResult.Success && !currentSessionResult.accountInfo.isValid() -> {
appLogger.i("$TAG: Invalid current session")
featureFlagState = FeatureFlagState( // invalid session, clear feature flag state to default and set NO_USER
fileSharingRestrictedState = FeatureFlagState.SharingRestrictedState.NO_USER
isFileSharingState = FeatureFlagState.FileSharingState.NoUser
)
}

Expand Down Expand Up @@ -132,22 +132,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 @@ -21,6 +21,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 @@ -31,6 +32,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 @@ -69,6 +71,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 @@ -90,6 +93,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 @@ -363,13 +367,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 @@ -497,5 +514,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 @@ -98,16 +98,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 @@ -116,7 +115,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 @@ -144,18 +144,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 @@ -247,7 +243,7 @@ fun ImportMediaRegularContent(

@Composable
fun ImportMediaLoggedOutContent(
fileSharingRestrictedState: FeatureFlagState.SharingRestrictedState,
fileSharingRestrictedState: FeatureFlagState.FileSharingState,
navigateBack: () -> Unit,
) {
WireScaffold(
Expand All @@ -272,7 +268,7 @@ fun ImportMediaLoggedOutContent(
@Composable
fun FileSharingRestrictedContent(
internalPadding: PaddingValues,
sharingRestrictedState: FeatureFlagState.SharingRestrictedState,
sharingRestrictedState: FeatureFlagState.FileSharingState,
openWireAction: () -> Unit
) {
val context = LocalContext.current
Expand All @@ -287,7 +283,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 @@ -301,7 +297,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 @@ -459,14 +455,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 @@ -1396,4 +1396,6 @@
<string name="location_app_permission_dialog_body">Allow Wire to access your device location to send your location.</string>
<string name="location_loading_label">Please wait...</string>
<string name="location_could_not_be_shared">Location could not be shared</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 @@ -335,6 +335,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 0f8ae93

Please sign in to comment.