From b9ec84fff7bdae64bf4d390aab3e6e30b5aed08a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Saleniuk?= Date: Thu, 28 Nov 2024 14:41:47 +0100 Subject: [PATCH 1/2] update kalium ref --- kalium | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kalium b/kalium index 786a7b83e8..bee4d00171 160000 --- a/kalium +++ b/kalium @@ -1 +1 @@ -Subproject commit 786a7b83e84663909c6c40b3f6c72263fe935661 +Subproject commit bee4d00171b79c0a2506c4a2b6eb2f45a250c52c From 4f12d54c3b1c238bd5bfd3b6a4eefd9af4077a2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Saleniuk?= Date: Thu, 28 Nov 2024 16:07:08 +0100 Subject: [PATCH 2/2] fix: crash when saving bottom sheet state [WPB-14433] --- .../com/wire/android/mapper/ContactMapper.kt | 8 +- .../wire/android/mapper/ConversationMapper.kt | 6 +- .../com/wire/android/mapper/MessageMapper.kt | 5 +- .../wire/android/mapper/OtherAccountMapper.kt | 7 +- .../mapper/RegularMessageContentMapper.kt | 4 - .../mapper/SystemMessageContentMapper.kt | 6 +- .../android/mapper/UICallParticipantMapper.kt | 4 +- .../android/mapper/UIParticipantMapper.kt | 10 +- .../com/wire/android/model/ImageAsset.kt | 54 +- .../com/wire/android/model/UserAvatarData.kt | 3 + .../ui/calling/SharedCallingViewModel.kt | 4 +- .../com/wire/android/ui/home/HomeViewModel.kt | 7 +- .../conversations/ConversationMemberExt.kt | 15 +- .../info/ConversationInfoViewModel.kt | 4 +- .../messages/item/SystemMessageItem.kt | 29 +- .../messages/item/SystemMessageItemLeading.kt | 4 +- .../ui/home/conversations/mock/Mock.kt | 33 +- .../ui/home/conversations/model/UIMessage.kt | 611 ++++++++++-------- .../conversations/model/UIQuotedMessage.kt | 6 +- .../GetConversationsFromSearchUseCase.kt | 3 - .../ConversationListViewModel.kt | 3 - .../conversationslist/model/BadgeEventType.kt | 41 +- .../model/ConversationItem.kt | 6 + .../ui/home/gallery/MediaGalleryViewModel.kt | 3 - .../ImportMediaAuthenticatedViewModel.kt | 4 +- .../other/OtherUserProfileScreenViewModel.kt | 4 +- .../self/SelfUserProfileViewModel.kt | 4 +- .../service/ServiceDetailsViewModel.kt | 4 +- .../util/ui/LocalizedStringResource.kt | 33 +- .../kotlin/com/wire/android/util/ui/UIText.kt | 11 +- .../wire/android/mapper/MessageMapperTest.kt | 6 +- .../android/mapper/OtherAccountMapperTest.kt | 12 +- .../mapper/RegularMessageContentMapperTest.kt | 6 +- .../mapper/UICallParticipantMapperTest.kt | 3 +- .../android/mapper/UIParticipantMapperTest.kt | 12 +- .../com/wire/android/model/ImageAssetTest.kt | 11 +- .../android/navigation/NavigationUtilsTest.kt | 13 +- .../ui/calling/SharedCallingViewModelTest.kt | 10 +- .../ConnectionActionButtonViewModelTest.kt | 4 - .../wire/android/ui/home/HomeViewModelTest.kt | 5 - ...eParticipantsForConversationUseCaseTest.kt | 5 +- .../ConversationInfoViewModelArrangement.kt | 5 - .../GetConversationsFromSearchUseCaseTest.kt | 5 - .../ConversationListViewModelTest.kt | 5 - .../home/gallery/MediaGalleryViewModelTest.kt | 5 - .../ImportMediaAuthenticatedViewModelTest.kt | 5 - .../OtherUserProfileViewModelArrangement.kt | 7 +- .../SelfUserProfileViewModelArrangement.kt | 5 - .../service/ServiceDetailsViewModelTest.kt | 5 - .../android/util/ui/AssetImageFetcherTest.kt | 22 +- core/ui-common/build.gradle.kts | 3 + .../common/bottomsheet/WireModalSheetState.kt | 59 +- 52 files changed, 575 insertions(+), 574 deletions(-) diff --git a/app/src/main/kotlin/com/wire/android/mapper/ContactMapper.kt b/app/src/main/kotlin/com/wire/android/mapper/ContactMapper.kt index af690cfbd0..3644579c3b 100644 --- a/app/src/main/kotlin/com/wire/android/mapper/ContactMapper.kt +++ b/app/src/main/kotlin/com/wire/android/mapper/ContactMapper.kt @@ -25,7 +25,6 @@ import com.wire.android.ui.home.conversationslist.model.Membership import com.wire.android.ui.home.newconversation.model.Contact import com.wire.android.ui.userprofile.common.UsernameMapper import com.wire.android.util.EMPTY -import com.wire.android.util.ui.WireSessionImageLoader import com.wire.kalium.logic.data.publicuser.model.UserSearchDetails import com.wire.kalium.logic.data.service.ServiceDetails import com.wire.kalium.logic.data.user.ConnectionState @@ -36,7 +35,6 @@ import javax.inject.Inject class ContactMapper @Inject constructor( private val userTypeMapper: UserTypeMapper, - private val wireSessionImageLoader: WireSessionImageLoader, ) { fun fromOtherUser(otherUser: OtherUser): Contact { @@ -48,7 +46,7 @@ class ContactMapper handle = handle.orEmpty(), label = UsernameMapper.fromOtherUser(otherUser), avatarData = UserAvatarData( - asset = previewPicture?.let { ImageAsset.UserAvatarAsset(wireSessionImageLoader, it) }, + asset = previewPicture?.let { ImageAsset.UserAvatarAsset(it) }, connectionState = connectionStatus, nameBasedAvatar = NameBasedAvatar(fullName = name, accentColor = otherUser.accentId) ), @@ -67,7 +65,7 @@ class ContactMapper handle = String.EMPTY, label = String.EMPTY, avatarData = UserAvatarData( - asset = previewAssetId?.let { ImageAsset.UserAvatarAsset(wireSessionImageLoader, it) }, + asset = previewAssetId?.let { ImageAsset.UserAvatarAsset(it) }, membership = Membership.Service ), membership = Membership.Service, @@ -85,7 +83,7 @@ class ContactMapper handle = handle.orEmpty(), label = mapUserHandle(user), avatarData = UserAvatarData( - asset = previewAssetId?.let { ImageAsset.UserAvatarAsset(wireSessionImageLoader, it) }, + asset = previewAssetId?.let { ImageAsset.UserAvatarAsset(it) }, nameBasedAvatar = NameBasedAvatar(fullName = name, accentColor = -1) ), membership = userTypeMapper.toMembership(type), diff --git a/app/src/main/kotlin/com/wire/android/mapper/ConversationMapper.kt b/app/src/main/kotlin/com/wire/android/mapper/ConversationMapper.kt index 99820e48b3..4098405e5c 100644 --- a/app/src/main/kotlin/com/wire/android/mapper/ConversationMapper.kt +++ b/app/src/main/kotlin/com/wire/android/mapper/ConversationMapper.kt @@ -26,7 +26,6 @@ import com.wire.android.ui.home.conversationslist.model.BlockState import com.wire.android.ui.home.conversationslist.model.ConversationInfo import com.wire.android.ui.home.conversationslist.model.ConversationItem import com.wire.android.ui.home.conversationslist.showLegalHoldIndicator -import com.wire.android.util.ui.WireSessionImageLoader import com.wire.kalium.logic.data.conversation.ConversationDetails.Connection import com.wire.kalium.logic.data.conversation.ConversationDetails.Group import com.wire.kalium.logic.data.conversation.ConversationDetails.OneOne @@ -41,7 +40,6 @@ import com.wire.kalium.logic.data.user.UserAvailabilityStatus @Suppress("LongMethod") fun ConversationDetailsWithEvents.toConversationItem( - wireSessionImageLoader: WireSessionImageLoader, userTypeMapper: UserTypeMapper, searchQuery: String, selfUserTeamId: TeamId? @@ -74,7 +72,7 @@ fun ConversationDetailsWithEvents.toConversationItem( is OneOne -> { ConversationItem.PrivateConversation( userAvatarData = UserAvatarData( - asset = conversationDetails.otherUser.previewPicture?.let { UserAvatarAsset(wireSessionImageLoader, it) }, + asset = conversationDetails.otherUser.previewPicture?.let { UserAvatarAsset(it) }, availabilityStatus = conversationDetails.otherUser.availabilityStatus, connectionState = conversationDetails.otherUser.connectionStatus, nameBasedAvatar = NameBasedAvatar(conversationDetails.otherUser.name, conversationDetails.otherUser.accentId) @@ -111,7 +109,7 @@ fun ConversationDetailsWithEvents.toConversationItem( is Connection -> { ConversationItem.ConnectionConversation( userAvatarData = UserAvatarData( - asset = conversationDetails.otherUser?.previewPicture?.let { UserAvatarAsset(wireSessionImageLoader, it) }, + asset = conversationDetails.otherUser?.previewPicture?.let { UserAvatarAsset(it) }, availabilityStatus = conversationDetails.otherUser?.availabilityStatus ?: UserAvailabilityStatus.NONE, nameBasedAvatar = NameBasedAvatar(conversationDetails.otherUser?.name, conversationDetails.otherUser?.accentId ?: -1) ), diff --git a/app/src/main/kotlin/com/wire/android/mapper/MessageMapper.kt b/app/src/main/kotlin/com/wire/android/mapper/MessageMapper.kt index cb0680e6e3..386c7841ad 100644 --- a/app/src/main/kotlin/com/wire/android/mapper/MessageMapper.kt +++ b/app/src/main/kotlin/com/wire/android/mapper/MessageMapper.kt @@ -37,7 +37,6 @@ import com.wire.android.ui.home.conversationslist.model.Membership import com.wire.android.ui.theme.Accent import com.wire.android.util.time.ISOFormatter import com.wire.android.util.ui.UIText -import com.wire.android.util.ui.WireSessionImageLoader import com.wire.kalium.logic.data.message.DeliveryStatus import com.wire.kalium.logic.data.message.Message import com.wire.kalium.logic.data.message.MessageContent @@ -52,8 +51,6 @@ class MessageMapper @Inject constructor( private val userTypeMapper: UserTypeMapper, private val messageContentMapper: MessageContentMapper, private val isoFormatter: ISOFormatter, - // TODO(qol): a message mapper should not depend on a UI related component - private val wireSessionImageLoader: WireSessionImageLoader ) { fun memberIdList(messages: List): List = messages.flatMap { message -> @@ -200,7 +197,7 @@ class MessageMapper @Inject constructor( } private fun getUserAvatarData(sender: User?) = UserAvatarData( - asset = sender?.previewAsset(wireSessionImageLoader), + asset = sender?.previewAsset(), availabilityStatus = sender?.availabilityStatus ?: UserAvailabilityStatus.NONE, membership = sender?.userType?.let { userTypeMapper.toMembership(it) } ?: Membership.None, connectionState = getConnectionState(sender), diff --git a/app/src/main/kotlin/com/wire/android/mapper/OtherAccountMapper.kt b/app/src/main/kotlin/com/wire/android/mapper/OtherAccountMapper.kt index 60a9077efa..3a9b0d2889 100644 --- a/app/src/main/kotlin/com/wire/android/mapper/OtherAccountMapper.kt +++ b/app/src/main/kotlin/com/wire/android/mapper/OtherAccountMapper.kt @@ -20,18 +20,15 @@ package com.wire.android.mapper import com.wire.android.ui.home.conversations.avatar import com.wire.android.ui.userprofile.self.model.OtherAccount -import com.wire.android.util.ui.WireSessionImageLoader import com.wire.kalium.logic.data.team.Team import com.wire.kalium.logic.data.user.SelfUser import javax.inject.Inject -class OtherAccountMapper @Inject constructor( - private val wireSessionImageLoader: WireSessionImageLoader -) { +class OtherAccountMapper @Inject constructor() { fun toOtherAccount(selfUser: SelfUser, team: Team?): OtherAccount = OtherAccount( id = selfUser.id, fullName = selfUser.name ?: "", - avatarData = selfUser.avatar(wireSessionImageLoader, selfUser.connectionStatus), + avatarData = selfUser.avatar(selfUser.connectionStatus), teamName = team?.name ) } diff --git a/app/src/main/kotlin/com/wire/android/mapper/RegularMessageContentMapper.kt b/app/src/main/kotlin/com/wire/android/mapper/RegularMessageContentMapper.kt index 6336f558bb..eabb1d96be 100644 --- a/app/src/main/kotlin/com/wire/android/mapper/RegularMessageContentMapper.kt +++ b/app/src/main/kotlin/com/wire/android/mapper/RegularMessageContentMapper.kt @@ -29,7 +29,6 @@ import com.wire.android.ui.home.conversations.model.UIMessageContent import com.wire.android.ui.home.conversations.model.UIQuotedMessage import com.wire.android.util.time.ISOFormatter import com.wire.android.util.ui.UIText -import com.wire.android.util.ui.WireSessionImageLoader import com.wire.kalium.logic.data.asset.AttachmentType import com.wire.kalium.logic.data.asset.isDisplayableImageMimeType import com.wire.kalium.logic.data.id.ConversationId @@ -50,7 +49,6 @@ import javax.inject.Inject @Suppress("TooManyFunctions") class RegularMessageMapper @Inject constructor( private val messageResourceProvider: MessageResourceProvider, - private val wireSessionImageLoader: WireSessionImageLoader, private val isoFormatter: ISOFormatter, ) { @@ -207,7 +205,6 @@ class RegularMessageMapper @Inject constructor( is MessageContent.QuotedMessageDetails.Asset -> when (AttachmentType.fromMimeTypeString(quotedContent.assetMimeType)) { AttachmentType.IMAGE -> UIQuotedMessage.UIQuotedData.DisplayableImage( ImageAsset.PrivateAsset( - wireSessionImageLoader, conversationId, it.messageId, it.isQuotingSelfUser @@ -249,7 +246,6 @@ class RegularMessageMapper @Inject constructor( UIMessageContent.ImageMessage( assetId = AssetId(remoteData.assetId, remoteData.assetDomain.orEmpty()), asset = ImageAsset.PrivateAsset( - wireSessionImageLoader, message.conversationId, message.id, sender is SelfUser diff --git a/app/src/main/kotlin/com/wire/android/mapper/SystemMessageContentMapper.kt b/app/src/main/kotlin/com/wire/android/mapper/SystemMessageContentMapper.kt index 611c6f1d0e..8018325628 100644 --- a/app/src/main/kotlin/com/wire/android/mapper/SystemMessageContentMapper.kt +++ b/app/src/main/kotlin/com/wire/android/mapper/SystemMessageContentMapper.kt @@ -185,7 +185,7 @@ class SystemMessageContentMapper @Inject constructor( private fun mapTeamMemberRemovedMessage( content: MessageContent.TeamMemberRemoved - ): UIMessageContent.SystemMessage = UIMessageContent.SystemMessage.TeamMemberRemoved_Legacy(content) + ): UIMessageContent.SystemMessage = UIMessageContent.SystemMessage.TeamMemberRemoved_Legacy(content.userName) private fun mapConversationRenamedMessage( senderUserId: UserId, @@ -197,7 +197,7 @@ class SystemMessageContentMapper @Inject constructor( user = sender, type = SelfNameType.ResourceTitleCase ) - return UIMessageContent.SystemMessage.RenamedConversation(authorName, content) + return UIMessageContent.SystemMessage.RenamedConversation(authorName, content.conversationName) } fun mapMemberChangeMessage( @@ -271,7 +271,7 @@ class SystemMessageContentMapper @Inject constructor( UIMessageContent.SystemMessage.HistoryLost private fun mapMLSWrongEpochWarning(): UIMessageContent.SystemMessage = - UIMessageContent.SystemMessage.MLSWrongEpochWarning() + UIMessageContent.SystemMessage.MLSWrongEpochWarning private fun mapConversationHistoryListProtocolChanged(): UIMessageContent.SystemMessage = UIMessageContent.SystemMessage.HistoryLostProtocolChanged diff --git a/app/src/main/kotlin/com/wire/android/mapper/UICallParticipantMapper.kt b/app/src/main/kotlin/com/wire/android/mapper/UICallParticipantMapper.kt index 4b84d17ca8..3ce437b99d 100644 --- a/app/src/main/kotlin/com/wire/android/mapper/UICallParticipantMapper.kt +++ b/app/src/main/kotlin/com/wire/android/mapper/UICallParticipantMapper.kt @@ -20,12 +20,10 @@ package com.wire.android.mapper import com.wire.android.model.ImageAsset import com.wire.android.ui.calling.model.UICallParticipant -import com.wire.android.util.ui.WireSessionImageLoader import com.wire.kalium.logic.data.call.Participant import javax.inject.Inject class UICallParticipantMapper @Inject constructor( - private val wireSessionImageLoader: WireSessionImageLoader, private val userTypeMapper: UserTypeMapper, ) { fun toUICallParticipant(participant: Participant) = UICallParticipant( @@ -36,7 +34,7 @@ class UICallParticipantMapper @Inject constructor( isSpeaking = participant.isSpeaking, isCameraOn = participant.isCameraOn, isSharingScreen = participant.isSharingScreen, - avatar = participant.avatarAssetId?.let { ImageAsset.UserAvatarAsset(wireSessionImageLoader, it) }, + avatar = participant.avatarAssetId?.let { ImageAsset.UserAvatarAsset(it) }, membership = userTypeMapper.toMembership(participant.userType), hasEstablishedAudio = participant.hasEstablishedAudio, accentId = participant.accentId diff --git a/app/src/main/kotlin/com/wire/android/mapper/UIParticipantMapper.kt b/app/src/main/kotlin/com/wire/android/mapper/UIParticipantMapper.kt index 87541dd46a..3610b892df 100644 --- a/app/src/main/kotlin/com/wire/android/mapper/UIParticipantMapper.kt +++ b/app/src/main/kotlin/com/wire/android/mapper/UIParticipantMapper.kt @@ -21,7 +21,6 @@ package com.wire.android.mapper import com.wire.android.ui.home.conversations.avatar import com.wire.android.ui.home.conversations.details.participants.model.UIParticipant import com.wire.android.ui.home.conversations.previewAsset -import com.wire.android.util.ui.WireSessionImageLoader import com.wire.kalium.logic.data.message.UserSummary import com.wire.kalium.logic.data.message.reaction.MessageReaction import com.wire.kalium.logic.data.message.receipt.DetailedReceipt @@ -33,7 +32,6 @@ import javax.inject.Inject class UIParticipantMapper @Inject constructor( private val userTypeMapper: UserTypeMapper, - private val wireSessionImageLoader: WireSessionImageLoader ) { fun toUIParticipant(user: User, isMLSVerified: Boolean = false): UIParticipant = with(user) { val (userType, connectionState, unavailable) = when (this) { @@ -45,7 +43,7 @@ class UIParticipantMapper @Inject constructor( id = id, name = name.orEmpty(), handle = handle.orEmpty(), - avatarData = avatar(wireSessionImageLoader, connectionState), + avatarData = avatar(connectionState), isSelf = user is SelfUser, isService = userType == UserType.SERVICE, membership = userTypeMapper.toMembership(userType), @@ -67,7 +65,7 @@ class UIParticipantMapper @Inject constructor( id = userSummary.userId, name = userSummary.userName.orEmpty(), handle = userSummary.userHandle.orEmpty(), - avatarData = userSummary.previewAsset(wireSessionImageLoader), + avatarData = userSummary.previewAsset(), membership = userTypeMapper.toMembership(userSummary.userType), unavailable = !userSummary.isUserDeleted && userSummary.userName.orEmpty().isEmpty(), isDeleted = userSummary.isUserDeleted, @@ -83,7 +81,7 @@ class UIParticipantMapper @Inject constructor( id = userSummary.userId, name = userSummary.userName.orEmpty(), handle = userSummary.userHandle.orEmpty(), - avatarData = userSummary.previewAsset(wireSessionImageLoader), + avatarData = userSummary.previewAsset(), membership = userTypeMapper.toMembership(userSummary.userType), unavailable = !userSummary.isUserDeleted && userSummary.userName.orEmpty().isEmpty(), isDeleted = userSummary.isUserDeleted, @@ -100,7 +98,7 @@ class UIParticipantMapper @Inject constructor( id = userSummary.userId, name = userSummary.userName.orEmpty(), handle = userSummary.userHandle.orEmpty(), - avatarData = previewAsset(wireSessionImageLoader), + avatarData = previewAsset(), membership = userTypeMapper.toMembership(userSummary.userType), unavailable = !userSummary.isUserDeleted && userSummary.userName.orEmpty().isEmpty(), isDeleted = userSummary.isUserDeleted, diff --git a/app/src/main/kotlin/com/wire/android/model/ImageAsset.kt b/app/src/main/kotlin/com/wire/android/model/ImageAsset.kt index 358345a886..b39c78d509 100644 --- a/app/src/main/kotlin/com/wire/android/model/ImageAsset.kt +++ b/app/src/main/kotlin/com/wire/android/model/ImageAsset.kt @@ -18,18 +18,33 @@ package com.wire.android.model +import androidx.appcompat.app.AppCompatActivity import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.res.painterResource +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner import com.wire.android.R +import com.wire.android.ui.LocalActivity import com.wire.android.util.ui.WireSessionImageLoader import com.wire.kalium.logic.data.id.ConversationId import com.wire.kalium.logic.data.id.QualifiedIdMapper import com.wire.kalium.logic.data.user.UserAssetId +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializable +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder import okio.Path +import okio.Path.Companion.toPath +import javax.inject.Inject @Stable +@Serializable sealed class ImageAsset { /** @@ -37,12 +52,13 @@ sealed class ImageAsset { * message, i.e. some preview images that the user selected from local device gallery. */ @Stable + @Serializable data class Local( - val dataPath: Path, + val dataPath: @Serializable(with = PathAsStringSerializer::class) Path, val idKey: String ) : ImageAsset() - sealed class Remote(private val imageLoader: WireSessionImageLoader) : ImageAsset() { + sealed class Remote : ImageAsset() { /** * Value that uniquely identifies this Asset, @@ -56,39 +72,53 @@ sealed class ImageAsset { withCrossfadeAnimation: Boolean = false ) = when { LocalInspectionMode.current -> painterResource(id = R.drawable.ic_welcome_1) - else -> imageLoader.paint(asset = this, fallbackData = fallbackData, withCrossfadeAnimation = withCrossfadeAnimation) + else -> { + hiltViewModel( + // limit the scope of the ViewModel to the current activity so that there's one image loader instance for the Activity + viewModelStoreOwner = checkNotNull(LocalActivity.current as? AppCompatActivity ?: LocalViewModelStoreOwner.current) { + "No ViewModelStoreOwner was provided via LocalViewModelStoreOwner" + }, + key = "remote_asset_image_loader" + ).imageLoader.paint(asset = this, fallbackData = fallbackData, withCrossfadeAnimation = withCrossfadeAnimation) + } } } @Stable + @Serializable data class UserAvatarAsset( - private val imageLoader: WireSessionImageLoader, val userAssetId: UserAssetId - ) : Remote(imageLoader) { + ) : Remote() { override val uniqueKey: String get() = userAssetId.toString() } @Stable + @Serializable data class PrivateAsset( - private val imageLoader: WireSessionImageLoader, val conversationId: ConversationId, val messageId: String, val isSelfAsset: Boolean, val isEphemeral: Boolean = false - ) : Remote(imageLoader) { + ) : Remote() { override fun toString(): String = "$conversationId:$messageId:$isSelfAsset:$isEphemeral" override val uniqueKey: String get() = toString() } } -fun String.parseIntoPrivateImageAsset( - imageLoader: WireSessionImageLoader, - qualifiedIdMapper: QualifiedIdMapper, -): ImageAsset.PrivateAsset { +fun String.parseIntoPrivateImageAsset(qualifiedIdMapper: QualifiedIdMapper): ImageAsset.PrivateAsset { val (conversationIdString, messageId, isSelfAsset, isEphemeral) = split(":") val conversationIdParam = qualifiedIdMapper.fromStringToQualifiedID(conversationIdString) - return ImageAsset.PrivateAsset(imageLoader, conversationIdParam, messageId, isSelfAsset.toBoolean(), isEphemeral.toBoolean()) + return ImageAsset.PrivateAsset(conversationIdParam, messageId, isSelfAsset.toBoolean(), isEphemeral.toBoolean()) } + +object PathAsStringSerializer : KSerializer { + override val descriptor = PrimitiveSerialDescriptor("Path", PrimitiveKind.STRING) + override fun serialize(encoder: Encoder, value: Path) = encoder.encodeString(value.toString()) + override fun deserialize(decoder: Decoder): Path = decoder.decodeString().toPath(normalize = true) +} + +@HiltViewModel +class RemoteAssetImageViewModel @Inject constructor(val imageLoader: WireSessionImageLoader) : ViewModel() diff --git a/app/src/main/kotlin/com/wire/android/model/UserAvatarData.kt b/app/src/main/kotlin/com/wire/android/model/UserAvatarData.kt index 372e2ee474..ed87882042 100644 --- a/app/src/main/kotlin/com/wire/android/model/UserAvatarData.kt +++ b/app/src/main/kotlin/com/wire/android/model/UserAvatarData.kt @@ -24,8 +24,10 @@ import com.wire.android.ui.home.conversationslist.model.Membership import com.wire.android.util.EMPTY import com.wire.kalium.logic.data.user.ConnectionState import com.wire.kalium.logic.data.user.UserAvailabilityStatus +import kotlinx.serialization.Serializable @Stable +@Serializable data class UserAvatarData( val asset: ImageAsset.UserAvatarAsset? = null, val availabilityStatus: UserAvailabilityStatus = UserAvailabilityStatus.NONE, @@ -50,6 +52,7 @@ data class UserAvatarData( /** * Holder that can be used to generate an avatar based on the user's full name initials and accent color. */ +@Serializable data class NameBasedAvatar(val fullName: String?, val accentColor: Int) { val initials: String get() { diff --git a/app/src/main/kotlin/com/wire/android/ui/calling/SharedCallingViewModel.kt b/app/src/main/kotlin/com/wire/android/ui/calling/SharedCallingViewModel.kt index 975bfae950..807f0600b4 100644 --- a/app/src/main/kotlin/com/wire/android/ui/calling/SharedCallingViewModel.kt +++ b/app/src/main/kotlin/com/wire/android/ui/calling/SharedCallingViewModel.kt @@ -31,7 +31,6 @@ import com.wire.android.media.CallRinger import com.wire.android.model.ImageAsset import com.wire.android.ui.calling.model.UICallParticipant import com.wire.android.util.dispatchers.DispatcherProvider -import com.wire.android.util.ui.WireSessionImageLoader import com.wire.kalium.logic.data.call.Call import com.wire.kalium.logic.data.call.ConversationTypeForCall import com.wire.kalium.logic.data.call.VideoState @@ -86,7 +85,6 @@ class SharedCallingViewModel @AssistedInject constructor( private val observeSpeaker: ObserveSpeakerUseCase, private val callRinger: CallRinger, private val uiCallParticipantMapper: UICallParticipantMapper, - private val wireSessionImageLoader: WireSessionImageLoader, private val userTypeMapper: UserTypeMapper, private val dispatchers: DispatcherProvider ) : ViewModel() { @@ -137,7 +135,7 @@ class SharedCallingViewModel @AssistedInject constructor( callState.copy( conversationName = getConversationName(details.otherUser.name), avatarAssetId = details.otherUser.completePicture?.let { assetId -> - ImageAsset.UserAvatarAsset(wireSessionImageLoader, assetId) + ImageAsset.UserAvatarAsset(assetId) }, conversationTypeForCall = ConversationTypeForCall.OneOnOne, membership = userTypeMapper.toMembership(details.otherUser.userType), diff --git a/app/src/main/kotlin/com/wire/android/ui/home/HomeViewModel.kt b/app/src/main/kotlin/com/wire/android/ui/home/HomeViewModel.kt index 4e4667ad18..8a549a03c4 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/HomeViewModel.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/HomeViewModel.kt @@ -33,7 +33,6 @@ import com.wire.android.model.ImageAsset.UserAvatarAsset import com.wire.android.model.NameBasedAvatar import com.wire.android.model.UserAvatarData import com.wire.android.navigation.SavedStateViewModel -import com.wire.android.util.ui.WireSessionImageLoader import com.wire.kalium.logic.feature.client.NeedsToRegisterClientUseCase import com.wire.kalium.logic.feature.legalhold.LegalHoldStateForSelfUser import com.wire.kalium.logic.feature.legalhold.ObserveLegalHoldStateForSelfUserUseCase @@ -55,7 +54,6 @@ class HomeViewModel @Inject constructor( private val needsToRegisterClient: NeedsToRegisterClientUseCase, private val canMigrateFromPersonalToTeam: CanMigrateFromPersonalToTeamUseCase, private val observeLegalHoldStatusForSelfUser: ObserveLegalHoldStateForSelfUserUseCase, - private val wireSessionImageLoader: WireSessionImageLoader, private val shouldTriggerMigrationForUser: ShouldTriggerMigrationForUserUserCase, private val analyticsManager: AnonymousAnalyticsManager ) : SavedStateViewModel(savedStateHandle) { @@ -126,10 +124,7 @@ class HomeViewModel @Inject constructor( homeState = homeState.copy( userAvatarData = UserAvatarData( asset = selfUser.previewPicture?.let { - UserAvatarAsset( - wireSessionImageLoader, - it - ) + UserAvatarAsset(it) }, availabilityStatus = selfUser.availabilityStatus, nameBasedAvatar = NameBasedAvatar(selfUser.name, selfUser.accentId) diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/ConversationMemberExt.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/ConversationMemberExt.kt index f612d4ea10..63c309e000 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/ConversationMemberExt.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/ConversationMemberExt.kt @@ -22,7 +22,6 @@ import com.wire.android.model.ImageAsset.UserAvatarAsset import com.wire.android.model.NameBasedAvatar import com.wire.android.model.UserAvatarData import com.wire.android.ui.home.conversationslist.model.Membership -import com.wire.android.util.ui.WireSessionImageLoader import com.wire.kalium.logic.data.conversation.MemberDetails import com.wire.kalium.logic.data.message.UserSummary import com.wire.kalium.logic.data.user.ConnectionState @@ -51,24 +50,22 @@ val MemberDetails.availabilityStatus: UserAvailabilityStatus is SelfUser -> (user as SelfUser).availabilityStatus } -fun User.previewAsset(wireSessionImageLoader: WireSessionImageLoader): UserAvatarAsset? = when (this) { +fun User.previewAsset(): UserAvatarAsset? = when (this) { is OtherUser -> previewPicture is SelfUser -> previewPicture -}?.let { UserAvatarAsset(wireSessionImageLoader, it) } +}?.let { UserAvatarAsset(it) } -fun User.avatar(wireSessionImageLoader: WireSessionImageLoader, connectionState: ConnectionState?): UserAvatarData = +fun User.avatar(connectionState: ConnectionState?): UserAvatarData = UserAvatarData( - asset = this.previewAsset(wireSessionImageLoader), + asset = this.previewAsset(), availabilityStatus = availabilityStatus, connectionState = connectionState, membership = if (userType == UserType.SERVICE) Membership.Service else Membership.None, nameBasedAvatar = NameBasedAvatar(fullName = name, accentColor = accentId) ) -fun UserSummary.previewAsset( - wireSessionImageLoader: WireSessionImageLoader -) = UserAvatarData( - asset = this.userPreviewAssetId?.let { UserAvatarAsset(wireSessionImageLoader, it) }, +fun UserSummary.previewAsset() = UserAvatarData( + asset = this.userPreviewAssetId?.let { UserAvatarAsset(it) }, availabilityStatus = this.availabilityStatus, connectionState = this.connectionStatus, nameBasedAvatar = NameBasedAvatar(fullName = userName, accentColor = accentId) diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/info/ConversationInfoViewModel.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/info/ConversationInfoViewModel.kt index 5e36411a49..50fe76c34f 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/info/ConversationInfoViewModel.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/info/ConversationInfoViewModel.kt @@ -31,7 +31,6 @@ import com.wire.android.navigation.SavedStateViewModel import com.wire.android.ui.home.conversations.ConversationNavArgs import com.wire.android.ui.navArgs import com.wire.android.util.ui.UIText -import com.wire.android.util.ui.WireSessionImageLoader import com.wire.android.util.ui.toUIText import com.wire.kalium.logic.StorageFailure import com.wire.kalium.logic.data.conversation.ConversationDetails @@ -52,7 +51,6 @@ class ConversationInfoViewModel @Inject constructor( override val savedStateHandle: SavedStateHandle, private val observeConversationDetails: ObserveConversationDetailsUseCase, private val fetchConversationMLSVerificationStatus: FetchConversationMLSVerificationStatusUseCase, - private val wireSessionImageLoader: WireSessionImageLoader, @CurrentAccount private val selfUserId: UserId, ) : SavedStateViewModel(savedStateHandle) { @@ -154,7 +152,7 @@ class ConversationInfoViewModel @Inject constructor( is ConversationDetails.OneOne -> ConversationAvatar.OneOne( conversationDetails.otherUser.previewPicture?.let { - ImageAsset.UserAvatarAsset(wireSessionImageLoader, it) + ImageAsset.UserAvatarAsset(it) }, conversationDetails.otherUser.availabilityStatus ) diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/SystemMessageItem.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/SystemMessageItem.kt index e744117517..914aed7af5 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/SystemMessageItem.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/SystemMessageItem.kt @@ -235,7 +235,7 @@ fun SystemMessage.annotatedString( is SystemMessage.MemberJoined -> arrayOf(author.asString(res).markdownBold()) is SystemMessage.MemberLeft -> arrayOf(author.asString(res).markdownBold()) is SystemMessage.MissedCall -> arrayOf(author.asString(res).markdownBold()) - is SystemMessage.RenamedConversation -> arrayOf(author.asString(res).markdownBold(), content.conversationName.markdownBold()) + is SystemMessage.RenamedConversation -> arrayOf(author.asString(res).markdownBold(), conversationName.markdownBold()) is SystemMessage.CryptoSessionReset -> arrayOf(author.asString(res).markdownBold()) is SystemMessage.NewConversationReceiptMode -> arrayOf(receiptMode.asString(res).markdownBold()) is SystemMessage.ConversationReceiptModeChanged -> arrayOf( @@ -243,7 +243,7 @@ fun SystemMessage.annotatedString( receiptMode.asString(res).markdownBold() ) - is SystemMessage.TeamMemberRemoved_Legacy -> arrayOf(content.userName) + is SystemMessage.TeamMemberRemoved_Legacy -> arrayOf(userName) is SystemMessage.Knock -> arrayOf(author.asString(res).markdownBold()) is SystemMessage.HistoryLost -> arrayOf() is SystemMessage.MLSWrongEpochWarning -> arrayOf() @@ -274,15 +274,15 @@ fun SystemMessage.annotatedString( arrayOf(memberNames.limitUserNamesList(res, true).toUserNamesListMarkdownString(res)) } ?: arrayOf() } - val markdownString = when (stringResId) { - is LocalizedStringResource.PluralResource -> res.getQuantityString( - (stringResId as LocalizedStringResource.PluralResource).id, - (stringResId as LocalizedStringResource.PluralResource).quantity, + val markdownString = when (stringRes) { + is LocalizedStringResource.Plural -> res.getQuantityString( + (stringRes as LocalizedStringResource.Plural).id, + (stringRes as LocalizedStringResource.Plural).quantity, *markdownArgs ) - is LocalizedStringResource.StringResource -> res.getString( - (stringResId as LocalizedStringResource.StringResource).id, + is LocalizedStringResource.String -> res.getString( + (stringRes as LocalizedStringResource.String).id, *markdownArgs ) } @@ -321,18 +321,7 @@ private fun SystemMessage.MemberFailedToAdd.toFailedToAddMarkdownText( if (isMultipleUsersFailure) failedToAddAnnotatedText.append("\n\n") failedToAddAnnotatedText.append( markdownText( - when (stringResId) { - is LocalizedStringResource.PluralResource -> res.getQuantityString( - stringResId.id, - stringResId.quantity, - stringResId.formatArgs - ) - - is LocalizedStringResource.StringResource -> res.getString( - stringResId.id, - memberNames.limitUserNamesList(res, true).toUserNamesListMarkdownString(res) - ) - }, + res.getString(stringRes.id, memberNames.limitUserNamesList(res, true).toUserNamesListMarkdownString(res)), normalStyle, boldStyle, normalColor, diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/SystemMessageItemLeading.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/SystemMessageItemLeading.kt index 3d89e323a0..90d90139d6 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/SystemMessageItemLeading.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/SystemMessageItemLeading.kt @@ -30,9 +30,9 @@ import com.wire.android.ui.home.conversations.model.UIMessageContent.SystemMessa @Composable fun SystemMessageItemLeading(messageContent: SystemMessage, modifier: Modifier = Modifier) { - if (messageContent.iconResId != null) { + messageContent.iconResId?.let { iconResId -> Image( - painter = painterResource(id = messageContent.iconResId), + painter = painterResource(id = iconResId), contentDescription = null, colorFilter = getColorFilter(messageContent), modifier = modifier.size( diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/mock/Mock.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/mock/Mock.kt index b995681aed..d6fc0329e1 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/mock/Mock.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/mock/Mock.kt @@ -18,14 +18,6 @@ package com.wire.android.ui.home.conversations.mock -import coil.ComponentRegistry -import coil.ImageLoader -import coil.disk.DiskCache -import coil.memory.MemoryCache -import coil.request.DefaultRequestOptions -import coil.request.Disposable -import coil.request.ImageRequest -import coil.request.ImageResult import com.wire.android.model.ImageAsset import com.wire.android.model.ImageAsset.UserAvatarAsset import com.wire.android.model.UserAvatarData @@ -43,17 +35,12 @@ import com.wire.android.ui.home.conversations.model.UIMessageContent import com.wire.android.ui.home.conversations.model.messagetypes.asset.UIAssetMessage import com.wire.android.ui.home.conversationslist.model.Membership import com.wire.android.util.ui.UIText -import com.wire.android.util.ui.WireSessionImageLoader import com.wire.android.util.ui.toUIText import com.wire.kalium.logic.data.id.ConversationId import com.wire.kalium.logic.data.id.QualifiedID import com.wire.kalium.logic.data.user.ConnectionState import com.wire.kalium.logic.data.user.UserAssetId import com.wire.kalium.logic.data.user.UserAvailabilityStatus -import com.wire.kalium.network.NetworkState -import com.wire.kalium.network.NetworkStateObserver -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow import kotlinx.datetime.Instant import okio.Path.Companion.toPath @@ -283,25 +270,10 @@ val mockUsersUITexts = listOf( "Gudrun Gut".toUIText() ) -val mockImageLoader = WireSessionImageLoader(object : ImageLoader { - override val components: ComponentRegistry get() = TODO("Not yet implemented") - override val defaults: DefaultRequestOptions get() = TODO("Not yet implemented") - override val diskCache: DiskCache get() = TODO("Not yet implemented") - override val memoryCache: MemoryCache get() = TODO("Not yet implemented") - override fun enqueue(request: ImageRequest): Disposable = TODO("Not yet implemented") - override suspend fun execute(request: ImageRequest): ImageResult = TODO("Not yet implemented") - override fun newBuilder(): ImageLoader.Builder = TODO("Not yet implemented") - override fun shutdown() = TODO("Not yet implemented") -}, - object : NetworkStateObserver { - override fun observeNetworkState(): StateFlow = MutableStateFlow(NetworkState.ConnectedWithInternet) - } -) - fun mockAssetMessage(assetId: String = "asset1", messageId: String = "msg1") = UIMessage.Regular( conversationId = ConversationId("value", "domain"), userAvatarData = UserAvatarData( - UserAvatarAsset(mockImageLoader, UserAssetId("a", "domain")), + UserAvatarAsset(UserAssetId("a", "domain")), UserAvailabilityStatus.AVAILABLE ), header = MessageHeader( @@ -331,7 +303,7 @@ fun mockAssetMessage(assetId: String = "asset1", messageId: String = "msg1") = U fun mockAssetAudioMessage(assetId: String = "asset1", messageId: String = "msg1") = UIMessage.Regular( conversationId = ConversationId("value", "domain"), userAvatarData = UserAvatarData( - UserAvatarAsset(mockImageLoader, UserAssetId("a", "domain")), + UserAvatarAsset(UserAssetId("a", "domain")), UserAvailabilityStatus.AVAILABLE ), header = MessageHeader( @@ -377,7 +349,6 @@ fun mockedImg() = UIMessageContent.ImageMessage( ) fun mockedPrivateAsset() = ImageAsset.PrivateAsset( - imageLoader = mockImageLoader, conversationId = ConversationId("id", "domain"), messageId = "messageId", isSelfAsset = true diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/model/UIMessage.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/model/UIMessage.kt index bd507cdf38..e9c53a7269 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/model/UIMessage.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/model/UIMessage.kt @@ -19,7 +19,6 @@ package com.wire.android.ui.home.conversations.model import android.content.res.Resources -import androidx.annotation.DrawableRes import androidx.annotation.PluralsRes import androidx.annotation.StringRes import androidx.compose.runtime.Stable @@ -41,7 +40,6 @@ import com.wire.kalium.logic.data.conversation.ClientId import com.wire.kalium.logic.data.conversation.Conversation import com.wire.kalium.logic.data.id.ConversationId import com.wire.kalium.logic.data.message.Message -import com.wire.kalium.logic.data.message.MessageContent import com.wire.kalium.logic.data.user.AssetId import com.wire.kalium.logic.data.user.ConnectionState import com.wire.kalium.logic.data.user.UserId @@ -53,8 +51,10 @@ import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentMapOf import kotlinx.collections.immutable.toImmutableList import kotlinx.datetime.Instant +import kotlinx.serialization.Serializable import kotlin.time.Duration +@Serializable sealed interface UIMessage { val conversationId: ConversationId val header: MessageHeader @@ -64,6 +64,7 @@ sealed interface UIMessage { val decryptionFailed: Boolean val isPending: Boolean + @Serializable data class Regular( override val conversationId: ConversationId, override val header: MessageHeader, @@ -110,6 +111,7 @@ sealed interface UIMessage { val isLocation: Boolean = messageContent is UIMessageContent.Location } + @Serializable data class System( override val conversationId: ConversationId, override val header: MessageHeader, @@ -124,6 +126,7 @@ sealed interface UIMessage { } @Stable +@Serializable data class MessageHeader( val username: UIText, val membership: Membership, @@ -141,68 +144,85 @@ data class MessageHeader( ) @Stable +@Serializable data class MessageFooter( val messageId: String, val reactions: Map = emptyMap(), val ownReactions: Set = emptySet() ) -sealed class ExpirationStatus { +@Serializable +sealed interface ExpirationStatus { + + @Serializable data class Expirable( val expireAfter: Duration, val selfDeletionStatus: Message.ExpirationData.SelfDeletionStatus - ) : ExpirationStatus() + ) : ExpirationStatus - object NotExpirable : ExpirationStatus() + @Serializable + data object NotExpirable : ExpirationStatus } -sealed class MessageEditStatus { - object NonEdited : MessageEditStatus() - data class Edited(val formattedEditTimeStamp: String) : MessageEditStatus() +@Serializable +sealed interface MessageEditStatus { + + @Serializable + data object NonEdited : MessageEditStatus + + @Serializable + data class Edited(val formattedEditTimeStamp: String) : MessageEditStatus } -sealed class MessageFlowStatus { - - data object Sending : MessageFlowStatus() - data object Sent : MessageFlowStatus() - sealed class Failure(val errorText: UIText) : MessageFlowStatus() { - sealed class Send(errorText: UIText) : Failure(errorText) { - data class Locally(val isEdited: Boolean) : Send( - if (isEdited) { - UIText.StringResource(R.string.label_message_edit_sent_failure) - } else { - UIText.StringResource(R.string.label_message_sent_failure) +@Serializable +sealed interface MessageFlowStatus { + + @Serializable + data object Sending : MessageFlowStatus + + @Serializable + data object Sent : MessageFlowStatus + + @Serializable + sealed interface Failure : MessageFlowStatus { + val errorText: UIText + + @Serializable + sealed interface Send : Failure { + @Serializable + data class Locally(val isEdited: Boolean) : Send { + override val errorText: UIText = when { + isEdited -> UIText.StringResource(R.string.label_message_edit_sent_failure) + else -> UIText.StringResource(R.string.label_message_sent_failure) } - ) + } - data class Remotely(val isEdited: Boolean, val backendWithFailure: String) : Send( - if (isEdited) { - UIText.StringResource( - R.string.label_message_edit_sent_remotely_failure, - backendWithFailure - ) - } else { - UIText.StringResource( - R.string.label_message_sent_remotely_failure, - backendWithFailure - ) + @Serializable + data class Remotely(val isEdited: Boolean, val backendWithFailure: String) : Send { + override val errorText: UIText = when { + isEdited -> UIText.StringResource(R.string.label_message_edit_sent_remotely_failure, backendWithFailure) + else -> UIText.StringResource(R.string.label_message_sent_remotely_failure, backendWithFailure) } - ) + } } - data class Decryption(val isDecryptionResolved: Boolean, private val errorCode: Int?) : Failure( - errorCode?.let { + @Serializable + data class Decryption(val isDecryptionResolved: Boolean, private val errorCode: Int?) : Failure { + override val errorText: UIText = errorCode?.let { UIText.StringResource(R.string.label_message_decryption_failure_message_with_error_code, it) } ?: UIText.StringResource(R.string.label_message_decryption_failure_message) - ) + } } - data object Delivered : MessageFlowStatus() + @Serializable + data object Delivered : MessageFlowStatus - data class Read(val count: Long) : MessageFlowStatus() + @Serializable + data class Read(val count: Long) : MessageFlowStatus } @Stable +@Serializable data class MessageStatus( val flowStatus: MessageFlowStatus, val expirationStatus: ExpirationStatus, @@ -223,92 +243,111 @@ data class MessageStatus( } @Stable -sealed class UILastMessageContent { - object None : UILastMessageContent() +@Serializable +sealed interface UILastMessageContent { + + @Serializable + data object None : UILastMessageContent - data class TextMessage(val messageBody: MessageBody) : UILastMessageContent() + @Serializable + data class TextMessage(val messageBody: MessageBody) : UILastMessageContent + @Serializable data class SenderWithMessage( val sender: UIText, val message: UIText, val separator: String = MarkdownConstants.NON_BREAKING_SPACE - ) : UILastMessageContent() + ) : UILastMessageContent + @Serializable data class MultipleMessage( val messages: List, val separator: String = MarkdownConstants.NON_BREAKING_SPACE - ) : UILastMessageContent() + ) : UILastMessageContent - data class Connection(val connectionState: ConnectionState, val userId: UserId) : UILastMessageContent() + @Serializable + data class Connection(val connectionState: ConnectionState, val userId: UserId) : UILastMessageContent - data class VerificationChanged(@StringRes val textResId: Int) : UILastMessageContent() + @Serializable + data class VerificationChanged(@StringRes val textResId: Int) : UILastMessageContent } -sealed class UIMessageContent { +@Serializable +sealed interface UIMessageContent { - sealed class Regular : UIMessageContent() + @Serializable + sealed interface Regular : UIMessageContent /** * IncompleteAssetMessage is a displayable asset that's missing the remote data. * Sometimes client receives two events about the same asset, first one with only part of the data ("preview" type from web), * so such asset shouldn't be shown until all the required data is received. */ - object IncompleteAssetMessage : UIMessageContent() + @Serializable + data object IncompleteAssetMessage : UIMessageContent interface PartialDeliverable { val deliveryStatus: DeliveryStatusContent } + @Serializable data class TextMessage( val messageBody: MessageBody, override val deliveryStatus: DeliveryStatusContent = DeliveryStatusContent.CompleteDelivery - ) : Regular(), PartialDeliverable, Copyable { + ) : Regular, PartialDeliverable, Copyable { override fun textToCopy(resources: Resources): String = messageBody.message.asString(resources) } + @Serializable data class Composite( val messageBody: MessageBody?, val buttonList: PersistentList - ) : Regular(), Copyable { + ) : Regular, Copyable { override fun textToCopy(resources: Resources): String? = messageBody?.message?.asString(resources) } - object Deleted : Regular() + @Serializable + data object Deleted : Regular + @Serializable data class RestrictedAsset( val mimeType: String, val assetSizeInBytes: Long, val assetName: String, override val deliveryStatus: DeliveryStatusContent = DeliveryStatusContent.CompleteDelivery - ) : Regular(), PartialDeliverable + ) : Regular, PartialDeliverable @Stable + @Serializable data class AssetMessage( val assetName: String, val assetExtension: String, val assetId: AssetId, val assetSizeInBytes: Long, override val deliveryStatus: DeliveryStatusContent = DeliveryStatusContent.CompleteDelivery - ) : Regular(), PartialDeliverable + ) : Regular, PartialDeliverable + @Serializable data class ImageMessage( val assetId: AssetId, val asset: ImageAsset.PrivateAsset?, val width: Int, val height: Int, override val deliveryStatus: DeliveryStatusContent = DeliveryStatusContent.CompleteDelivery - ) : Regular(), PartialDeliverable + ) : Regular, PartialDeliverable @Stable + @Serializable data class AudioAssetMessage( val assetName: String, val assetExtension: String, val assetId: AssetId, val audioMessageDurationInMs: Long, override val deliveryStatus: DeliveryStatusContent = DeliveryStatusContent.CompleteDelivery - ) : Regular(), PartialDeliverable + ) : Regular, PartialDeliverable @Stable + @Serializable data class Location( val latitude: Float, val longitude: Float, @@ -316,310 +355,365 @@ sealed class UIMessageContent { val zoom: Int = DEFAULT_LOCATION_ZOOM, @StringRes val urlCoordinates: Int = R.string.url_maps_location_coordinates_fallback, override val deliveryStatus: DeliveryStatusContent = DeliveryStatusContent.CompleteDelivery - ) : Regular(), PartialDeliverable - - sealed class SystemMessage( - @DrawableRes val iconResId: Int?, - open val stringResId: LocalizedStringResource, - @StringRes val learnMoreResId: Int? = null, - val isSmallIcon: Boolean = true, - ) : UIMessageContent() { - - constructor( - @DrawableRes iconResId: Int?, - @StringRes stringResId: Int, - isSmallIcon: Boolean = true, - @StringRes learnMoreResId: Int? = null - ) : this(iconResId, LocalizedStringResource.StringResource(stringResId), learnMoreResId, isSmallIcon) - - constructor( - @DrawableRes iconResId: Int?, - @PluralsRes stringResId: Int, - quantity: Int, - formatArgs: List, - isSmallIcon: Boolean = true, - @StringRes learnMoreResId: Int? = null - ) : this( - iconResId, - LocalizedStringResource.PluralResource(stringResId, quantity, formatArgs.toTypedArray()), - learnMoreResId, - isSmallIcon - ) + ) : Regular, PartialDeliverable - data class Knock(val author: UIText, val isSelfTriggered: Boolean) : SystemMessage( - R.drawable.ic_ping, - if (isSelfTriggered) R.string.label_system_message_self_user_knock else R.string.label_system_message_other_user_knock - ) + @Serializable + sealed interface SystemMessage : UIMessageContent { + val iconResId: Int? + val stringRes: LocalizedStringResource + val learnMoreResId: Int? get() = null + val isSmallIcon: Boolean get() = true + @Serializable + data class Knock( + val author: UIText, + val isSelfTriggered: Boolean + ) : SystemMessage { + override val iconResId = R.drawable.ic_ping + override val stringRes = when { + isSelfTriggered -> R.string.label_system_message_self_user_knock + else -> R.string.label_system_message_other_user_knock + }.toLocalizedStringResource() + } + + @Serializable data class MemberAdded( val author: UIText, val memberNames: List, val isSelfTriggered: Boolean = false - ) : SystemMessage( - R.drawable.ic_add, - if (isSelfTriggered) R.string.label_system_message_added_by_self else R.string.label_system_message_added_by_other - ) + ) : SystemMessage { + override val iconResId = R.drawable.ic_add + override val stringRes = when { + isSelfTriggered -> R.string.label_system_message_added_by_self + else -> R.string.label_system_message_added_by_other + }.toLocalizedStringResource() + } + @Serializable data class MemberJoined( val author: UIText, val isSelfTriggered: Boolean = false - ) : SystemMessage( - R.drawable.ic_add, - if (isSelfTriggered) { - R.string.label_system_message_joined_the_conversation_by_self - } else { - R.string.label_system_message_joined_the_conversation_by_other - } - ) + ) : SystemMessage { + override val iconResId = R.drawable.ic_add + override val stringRes = when { + isSelfTriggered -> R.string.label_system_message_joined_the_conversation_by_self + else -> R.string.label_system_message_joined_the_conversation_by_other + }.toLocalizedStringResource() + } + @Serializable data class MemberRemoved( val author: UIText, val memberNames: List, val isSelfTriggered: Boolean = false - ) : SystemMessage( - R.drawable.ic_minus, - if (isSelfTriggered) R.string.label_system_message_removed_by_self else R.string.label_system_message_removed_by_other - ) + ) : SystemMessage { + override val iconResId = R.drawable.ic_minus + override val stringRes = when { + isSelfTriggered -> R.string.label_system_message_removed_by_self + else -> R.string.label_system_message_removed_by_other + }.toLocalizedStringResource() + } + @Serializable data class TeamMemberRemoved( val author: UIText, val memberNames: List, - ) : SystemMessage( - R.drawable.ic_minus, - R.plurals.label_system_message_team_member_left, - quantity = memberNames.size, - formatArgs = memberNames - ) + ) : SystemMessage { + override val iconResId = R.drawable.ic_minus + override val stringRes = R.plurals.label_system_message_team_member_left.toLocalizedPluralResource(memberNames.size) + } + @Serializable data class MemberLeft( val author: UIText, val isSelfTriggered: Boolean = false - ) : SystemMessage( - R.drawable.ic_minus, - if (isSelfTriggered) { - R.string.label_system_message_left_the_conversation_by_self - } else { - R.string.label_system_message_left_the_conversation_by_other - } - ) + ) : SystemMessage { + override val iconResId = R.drawable.ic_minus + override val stringRes = when { + isSelfTriggered -> R.string.label_system_message_left_the_conversation_by_self + else -> R.string.label_system_message_left_the_conversation_by_other + }.toLocalizedStringResource() + } + @Serializable data class FederationMemberRemoved( val memberNames: List - ) : SystemMessage( - R.drawable.ic_minus, - if (memberNames.size > 1) { - R.string.label_system_message_federation_many_member_removed - } else { - R.string.label_system_message_federation_one_member_removed - } - ) + ) : SystemMessage { + override val iconResId = R.drawable.ic_minus + override val stringRes = when { + memberNames.size > 1 -> R.string.label_system_message_federation_many_member_removed + else -> R.string.label_system_message_federation_one_member_removed + }.toLocalizedStringResource() + } + @Serializable data class FederationStopped( val domainList: List - ) : SystemMessage( - R.drawable.ic_info, - if (domainList.size > 1) { - R.string.label_system_message_federation_conection_removed - } else { - R.string.label_system_message_federation_removed - }, - learnMoreResId = R.string.url_federation_support - ) + ) : SystemMessage { + override val iconResId = R.drawable.ic_info + override val stringRes = when { + domainList.size > 1 -> R.string.label_system_message_federation_conection_removed + else -> R.string.label_system_message_federation_removed + }.toLocalizedStringResource() + override val learnMoreResId = R.string.url_federation_support + } + + @Serializable + sealed interface MissedCall : SystemMessage { + val author: UIText + override val iconResId get() = R.drawable.ic_call_end + override val isSmallIcon get() = false - sealed class MissedCall( - open val author: UIText, - @StringRes stringResId: Int, - ) : SystemMessage(R.drawable.ic_call_end, stringResId, isSmallIcon = false) { + @Serializable + data class YouCalled(override val author: UIText) : MissedCall { + override val stringRes = R.string.label_system_message_you_called.toLocalizedStringResource() + } - data class YouCalled(override val author: UIText) : MissedCall(author, R.string.label_system_message_you_called) - data class OtherCalled(override val author: UIText) : MissedCall(author, R.string.label_system_message_other_called) + @Serializable + data class OtherCalled(override val author: UIText) : MissedCall { + override val stringRes = R.string.label_system_message_other_called.toLocalizedStringResource() + } } - data class RenamedConversation(val author: UIText, val content: MessageContent.ConversationRenamed) : - SystemMessage(R.drawable.ic_edit, R.string.label_system_message_renamed_the_conversation) + @Serializable + data class RenamedConversation( + val author: UIText, + val conversationName: String + ) : SystemMessage { + override val iconResId = R.drawable.ic_edit + override val stringRes = R.string.label_system_message_renamed_the_conversation.toLocalizedStringResource() + } @Deprecated("Use TeamMemberRemoved") @Suppress("ClassNaming") - data class TeamMemberRemoved_Legacy(val content: MessageContent.TeamMemberRemoved) : - SystemMessage( - R.drawable.ic_minus, - R.plurals.label_system_message_team_member_left, - quantity = 0, - formatArgs = emptyList(), - true - ) + @Serializable + data class TeamMemberRemoved_Legacy( + val userName: String + ) : SystemMessage { + override val iconResId = R.drawable.ic_minus + override val stringRes = R.plurals.label_system_message_team_member_left.toLocalizedPluralResource(0) + } - data class CryptoSessionReset(val author: UIText) : - SystemMessage(R.drawable.ic_info, R.string.label_system_message_session_reset) + @Serializable + data class CryptoSessionReset( + val author: UIText + ) : SystemMessage { + override val iconResId = R.drawable.ic_info + override val stringRes = R.string.label_system_message_session_reset.toLocalizedStringResource() + } + @Serializable data class NewConversationReceiptMode( val receiptMode: UIText - ) : SystemMessage(R.drawable.ic_view, R.string.label_system_message_new_conversation_receipt_mode) + ) : SystemMessage { + override val iconResId = R.drawable.ic_view + override val stringRes = R.string.label_system_message_new_conversation_receipt_mode.toLocalizedStringResource() + } + @Serializable data class ConversationReceiptModeChanged( val author: UIText, val receiptMode: UIText, val isAuthorSelfUser: Boolean = false - ) : SystemMessage( - R.drawable.ic_view, - if (isAuthorSelfUser) { - R.string.label_system_message_read_receipt_changed_by_self - } else { - R.string.label_system_message_read_receipt_changed_by_other - } - ) + ) : SystemMessage { + override val iconResId = R.drawable.ic_view + override val stringRes = when { + isAuthorSelfUser -> R.string.label_system_message_read_receipt_changed_by_self + else -> R.string.label_system_message_read_receipt_changed_by_other + }.toLocalizedStringResource() + } + @Serializable data class ConversationMessageTimerActivated( val author: UIText, val isAuthorSelfUser: Boolean = false, val selfDeletionDuration: SelfDeletionDuration - ) : SystemMessage( - R.drawable.ic_timer, - if (isAuthorSelfUser) { - R.string.label_system_message_conversation_message_timer_activated_by_self - } else { - R.string.label_system_message_conversation_message_timer_activated_by_other - } - ) + ) : SystemMessage { + override val iconResId = R.drawable.ic_timer + override val stringRes = when { + isAuthorSelfUser -> R.string.label_system_message_conversation_message_timer_activated_by_self + else -> R.string.label_system_message_conversation_message_timer_activated_by_other + }.toLocalizedStringResource() + } + @Serializable data class ConversationMessageTimerDeactivated( val author: UIText, val isAuthorSelfUser: Boolean = false - ) : SystemMessage( - R.drawable.ic_timer, - if (isAuthorSelfUser) { - R.string.label_system_message_conversation_message_timer_deactivated_by_self - } else { - R.string.label_system_message_conversation_message_timer_deactivated_by_other - } - ) + ) : SystemMessage { + override val iconResId = R.drawable.ic_timer + override val stringRes = when { + isAuthorSelfUser -> R.string.label_system_message_conversation_message_timer_deactivated_by_self + else -> R.string.label_system_message_conversation_message_timer_deactivated_by_other + }.toLocalizedStringResource() + } - class MLSWrongEpochWarning : SystemMessage( - iconResId = R.drawable.ic_info, - stringResId = R.string.label_system_message_conversation_mls_wrong_epoch_error_handled, - learnMoreResId = R.string.url_system_message_learn_more_about_mls - ) + @Serializable + data object MLSWrongEpochWarning : SystemMessage { + override val iconResId = R.drawable.ic_info + override val stringRes = R.string.label_system_message_conversation_mls_wrong_epoch_error_handled.toLocalizedStringResource() + override val learnMoreResId = R.string.url_system_message_learn_more_about_mls + } + @Serializable data class ConversationProtocolChanged( val protocol: Conversation.Protocol - ) : SystemMessage( - iconResId = R.drawable.ic_info, - stringResId = when (protocol) { + ) : SystemMessage { + override val iconResId = R.drawable.ic_info + override val stringRes = when (protocol) { Conversation.Protocol.PROTEUS -> R.string.label_system_message_conversation_protocol_changed_proteus Conversation.Protocol.MIXED -> R.string.label_system_message_conversation_protocol_changed_mixed Conversation.Protocol.MLS -> R.string.label_system_message_conversation_protocol_changed_mls - }, - learnMoreResId = when (protocol) { + }.toLocalizedStringResource() + override val learnMoreResId = when (protocol) { Conversation.Protocol.PROTEUS -> null Conversation.Protocol.MIXED -> null Conversation.Protocol.MLS -> R.string.url_system_message_learn_more_about_mls } - ) + } - data object ConversationProtocolChangedWithCallOngoing : SystemMessage( - R.drawable.ic_info, - R.string.label_system_message_conversation_protocol_changed_during_a_call - ) + @Serializable + data object ConversationProtocolChangedWithCallOngoing : SystemMessage { + override val iconResId = R.drawable.ic_info + override val stringRes = R.string.label_system_message_conversation_protocol_changed_during_a_call.toLocalizedStringResource() + } - object HistoryLost : SystemMessage( - R.drawable.ic_info, - R.string.label_system_message_conversation_history_lost - ) + @Serializable + data object HistoryLost : SystemMessage { + override val iconResId = R.drawable.ic_info + override val stringRes = R.string.label_system_message_conversation_history_lost.toLocalizedStringResource() + } - object HistoryLostProtocolChanged : SystemMessage( - R.drawable.ic_info, - R.string.label_system_message_conversation_history_lost_protocol_changed - ) + @Serializable + data object HistoryLostProtocolChanged : SystemMessage { + override val iconResId = R.drawable.ic_info + override val stringRes = R.string.label_system_message_conversation_history_lost_protocol_changed.toLocalizedStringResource() + } + @Serializable data class ConversationMessageCreated( val author: UIText, val isAuthorSelfUser: Boolean = false, val date: String - ) : SystemMessage( - R.drawable.ic_conversation, - if (isAuthorSelfUser) { - R.string.label_system_message_conversation_started_by_self - } else { - R.string.label_system_message_conversation_started_by_other - } - ) + ) : SystemMessage { + override val iconResId = R.drawable.ic_conversation + override val stringRes = when { + isAuthorSelfUser -> R.string.label_system_message_conversation_started_by_self + else -> R.string.label_system_message_conversation_started_by_other + }.toLocalizedStringResource() + } + @Serializable data class ConversationStartedWithMembers( val memberNames: List - ) : SystemMessage( - R.drawable.ic_contact, - R.string.label_system_message_conversation_started_with_members - ) + ) : SystemMessage { + override val iconResId = R.drawable.ic_contact + override val stringRes = R.string.label_system_message_conversation_started_with_members.toLocalizedStringResource() + } + @Serializable data class MemberFailedToAdd( val memberNames: List, val type: Type, - ) : SystemMessage( - R.drawable.ic_info, - if (memberNames.size > 1) { - R.string.label_system_message_conversation_failed_add_many_members_details - } else { - R.string.label_system_message_conversation_failed_add_one_member_details - }, - learnMoreResId = when (type) { + ) : SystemMessage { + override val iconResId = R.drawable.ic_info + override val stringRes = when { + memberNames.size > 1 -> R.string.label_system_message_conversation_failed_add_many_members_details + else -> R.string.label_system_message_conversation_failed_add_one_member_details + }.toLocalizedStringResource() + override val learnMoreResId = when (type) { Type.Federation -> R.string.url_message_details_offline_backends_learn_more Type.LegalHold -> R.string.url_legal_hold_learn_more Type.Unknown -> null } - - ) { val usersCount = memberNames.size enum class Type { Federation, LegalHold, Unknown; } } - data class ConversationDegraded(val protocol: Conversation.Protocol) : SystemMessage( - iconResId = if (protocol == Conversation.Protocol.MLS) R.drawable.ic_conversation_degraded_mls - else R.drawable.ic_shield_holo, - stringResId = if (protocol == Conversation.Protocol.MLS) R.string.label_system_message_conversation_degraded_mls - else R.string.label_system_message_conversation_degraded_proteus - ) + @Serializable + data class ConversationDegraded( + val protocol: Conversation.Protocol + ) : SystemMessage { + override val iconResId = + if (protocol == Conversation.Protocol.MLS) R.drawable.ic_conversation_degraded_mls + else R.drawable.ic_shield_holo + override val stringRes = LocalizedStringResource.String( + if (protocol == Conversation.Protocol.MLS) R.string.label_system_message_conversation_degraded_mls + else R.string.label_system_message_conversation_degraded_proteus + ) + } - data class ConversationVerified(val protocol: Conversation.Protocol) : SystemMessage( - iconResId = if (protocol == Conversation.Protocol.MLS) R.drawable.ic_certificate_valid_mls - else R.drawable.ic_certificate_valid_proteus, - stringResId = if (protocol == Conversation.Protocol.MLS) R.string.label_system_message_conversation_verified_mls - else R.string.label_system_message_conversation_verified_proteus - ) + @Serializable + data class ConversationVerified( + val protocol: Conversation.Protocol + ) : SystemMessage { + override val iconResId = + if (protocol == Conversation.Protocol.MLS) R.drawable.ic_certificate_valid_mls + else R.drawable.ic_certificate_valid_proteus + override val stringRes = LocalizedStringResource.String( + if (protocol == Conversation.Protocol.MLS) R.string.label_system_message_conversation_verified_mls + else R.string.label_system_message_conversation_verified_proteus + ) + } - data object ConversationMessageCreatedUnverifiedWarning : SystemMessage( - R.drawable.ic_info, - R.string.label_system_message_conversation_started_sensitive_information - ) + @Serializable + data object ConversationMessageCreatedUnverifiedWarning : SystemMessage { + override val iconResId = R.drawable.ic_info + override val stringRes = LocalizedStringResource.String( + R.string.label_system_message_conversation_started_sensitive_information + ) + } + + @Serializable + sealed interface LegalHold : SystemMessage { + val memberNames: List? get() = null + override val iconResId get() = R.drawable.ic_legal_hold - sealed class LegalHold( - stringResId: LocalizedStringResource.StringResource, - @StringRes learnMoreResId: Int? = null, - open val memberNames: List? = null, - ) : SystemMessage(R.drawable.ic_legal_hold, stringResId, learnMoreResId) { + @Serializable + sealed interface Enabled : LegalHold { + override val learnMoreResId get() = R.string.url_legal_hold_learn_more - sealed class Enabled(override val stringResId: LocalizedStringResource.StringResource) : - LegalHold(stringResId, R.string.url_legal_hold_learn_more) { + @Serializable + data object Self : Enabled { + override val stringRes = LocalizedStringResource.String(R.string.legal_hold_system_message_enabled_self) + } - constructor(@StringRes stringResId: Int) : this(LocalizedStringResource.StringResource(stringResId)) + @Serializable + data class Others(override val memberNames: List) : Enabled { + override val stringRes = LocalizedStringResource.String(R.string.legal_hold_system_message_enabled_others) + } - data object Self : Enabled(R.string.legal_hold_system_message_enabled_self) - data class Others(override val memberNames: List) : Enabled(R.string.legal_hold_system_message_enabled_others) - data object Conversation : Enabled(R.string.legal_hold_system_message_enabled_conversation) + @Serializable + data object Conversation : Enabled { + override val stringRes = LocalizedStringResource.String(R.string.legal_hold_system_message_enabled_conversation) + } } - sealed class Disabled(override val stringResId: LocalizedStringResource.StringResource) : LegalHold(stringResId, null) { + @Serializable + sealed interface Disabled : LegalHold { - constructor(@StringRes stringResId: Int) : this(LocalizedStringResource.StringResource(stringResId)) + @Serializable + data object Self : Disabled { + override val stringRes = LocalizedStringResource.String(R.string.legal_hold_system_message_disabled_self) + } - data object Self : Disabled(R.string.legal_hold_system_message_disabled_self) - data class Others(override val memberNames: List) : Disabled(R.string.legal_hold_system_message_disabled_others) - data object Conversation : Disabled(R.string.legal_hold_system_message_disabled_conversation) + @Serializable + data class Others(override val memberNames: List) : Disabled { + override val stringRes = LocalizedStringResource.String(R.string.legal_hold_system_message_disabled_others) + } + + @Serializable + data object Conversation : Disabled { + override val stringRes = + LocalizedStringResource.String(R.string.legal_hold_system_message_disabled_conversation) + } } } } } +@Serializable data class MessageBody( val message: UIText, val quotedMessage: UIQuotedMessage? = null @@ -629,6 +723,7 @@ enum class MessageSource { Self, OtherUser } +@Serializable data class MessageTime(val instant: Instant) { val utcISO: String = instant.toIsoDateTimeString() val formattedDate: String = utcISO.uiMessageDateTime() ?: "" @@ -638,7 +733,10 @@ data class MessageTime(val instant: Instant) { } @Stable +@Serializable sealed interface DeliveryStatusContent { + + @Serializable class PartialDelivery( val failedRecipients: ImmutableList = persistentListOf(), val noClients: ImmutableMap> = persistentMapOf(), @@ -651,14 +749,19 @@ sealed interface DeliveryStatusContent { val totalUsersWithFailures by lazy { (failedRecipients.size + noClients.values.distinct().sumOf { it.size }) } } + @Serializable data object CompleteDelivery : DeliveryStatusContent } @Stable +@Serializable data class MessageButton( val id: String, val text: String, val isSelected: Boolean, ) +private fun @receiver:StringRes Int.toLocalizedStringResource() = LocalizedStringResource.String(this) +private fun @receiver:PluralsRes Int.toLocalizedPluralResource(quantity: Int) = LocalizedStringResource.Plural(this, quantity) + const val DEFAULT_LOCATION_ZOOM = 20 diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/model/UIQuotedMessage.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/model/UIQuotedMessage.kt index a8225875d6..04b90d310a 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/model/UIQuotedMessage.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/model/UIQuotedMessage.kt @@ -21,11 +21,15 @@ import com.wire.android.appLogger import com.wire.android.model.ImageAsset import com.wire.android.util.ui.UIText import com.wire.kalium.logic.data.user.UserId +import kotlinx.serialization.Serializable +@Serializable sealed class UIQuotedMessage { - object UnavailableData : UIQuotedMessage() + @Serializable + data object UnavailableData : UIQuotedMessage() + @Serializable data class UIQuotedData( val messageId: String, val senderId: UserId, diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/usecase/GetConversationsFromSearchUseCase.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/usecase/GetConversationsFromSearchUseCase.kt index d2a85417d4..a7d9584055 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/usecase/GetConversationsFromSearchUseCase.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/usecase/GetConversationsFromSearchUseCase.kt @@ -27,7 +27,6 @@ import com.wire.android.mapper.UserTypeMapper import com.wire.android.mapper.toConversationItem import com.wire.android.ui.home.conversationslist.model.ConversationItem import com.wire.android.util.dispatchers.DispatcherProvider -import com.wire.android.util.ui.WireSessionImageLoader import com.wire.kalium.logic.data.conversation.ConversationFilter import com.wire.kalium.logic.data.conversation.ConversationQueryConfig import com.wire.kalium.logic.feature.conversation.GetPaginatedFlowOfConversationDetailsWithEventsBySearchQueryUseCase @@ -45,7 +44,6 @@ class GetConversationsFromSearchUseCase @Inject constructor( private val useCase: GetPaginatedFlowOfConversationDetailsWithEventsBySearchQueryUseCase, private val getFavoriteFolderUseCase: GetFavoriteFolderUseCase, private val observeConversationsFromFromFolder: ObserveConversationsFromFolderUseCase, - private val wireSessionImageLoader: WireSessionImageLoader, private val userTypeMapper: UserTypeMapper, private val dispatchers: DispatcherProvider, private val observeSelfUser: GetSelfUserUseCase @@ -99,7 +97,6 @@ class GetConversationsFromSearchUseCase @Inject constructor( .map { pagingData -> pagingData.map { it.toConversationItem( - wireSessionImageLoader = wireSessionImageLoader, userTypeMapper = userTypeMapper, searchQuery = searchQuery, selfUserTeamId = observeSelfUser().firstOrNull()?.teamId diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversationslist/ConversationListViewModel.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversationslist/ConversationListViewModel.kt index c17ba56bb9..3ec4d460b9 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversationslist/ConversationListViewModel.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversationslist/ConversationListViewModel.kt @@ -46,7 +46,6 @@ import com.wire.android.ui.home.conversationslist.model.ConversationsSource import com.wire.android.ui.home.conversationslist.model.DialogState import com.wire.android.ui.home.conversationslist.model.GroupDialogState import com.wire.android.util.dispatchers.DispatcherProvider -import com.wire.android.util.ui.WireSessionImageLoader import com.wire.kalium.logic.data.conversation.Conversation import com.wire.kalium.logic.data.conversation.ConversationFilter import com.wire.kalium.logic.data.conversation.MutedConversationStatus @@ -141,7 +140,6 @@ class ConversationListViewModelImpl @AssistedInject constructor( private val updateConversationArchivedStatus: UpdateConversationArchivedStatusUseCase, private val observeLegalHoldStateForSelfUser: ObserveLegalHoldStateForSelfUserUseCase, @CurrentAccount val currentAccount: UserId, - private val wireSessionImageLoader: WireSessionImageLoader, private val userTypeMapper: UserTypeMapper, private val observeSelfUser: GetSelfUserUseCase ) : ConversationListViewModel, ViewModel() { @@ -238,7 +236,6 @@ class ConversationListViewModelImpl @AssistedInject constructor( ).combine(observeLegalHoldStateForSelfUser()) { conversations, selfUserLegalHoldStatus -> conversations.map { conversationDetails -> conversationDetails.toConversationItem( - wireSessionImageLoader = wireSessionImageLoader, userTypeMapper = userTypeMapper, searchQuery = searchQuery, selfUserTeamId = observeSelfUser().firstOrNull()?.teamId diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversationslist/model/BadgeEventType.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversationslist/model/BadgeEventType.kt index 722266a63a..a1bbc3bad0 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversationslist/model/BadgeEventType.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversationslist/model/BadgeEventType.kt @@ -18,15 +18,38 @@ package com.wire.android.ui.home.conversationslist.model +import kotlinx.serialization.Serializable + +@Serializable sealed class BadgeEventType { + + @Serializable data class UnreadMessage(val unreadMessageCount: Int) : BadgeEventType() - object UnreadMention : BadgeEventType() - object UnreadReply : BadgeEventType() - object MissedCall : BadgeEventType() - object Knock : BadgeEventType() - object ReceivedConnectionRequest : BadgeEventType() - object SentConnectRequest : BadgeEventType() - object Blocked : BadgeEventType() - object Deleted : BadgeEventType() - object None : BadgeEventType() + + @Serializable + data object UnreadMention : BadgeEventType() + + @Serializable + data object UnreadReply : BadgeEventType() + + @Serializable + data object MissedCall : BadgeEventType() + + @Serializable + data object Knock : BadgeEventType() + + @Serializable + data object ReceivedConnectionRequest : BadgeEventType() + + @Serializable + data object SentConnectRequest : BadgeEventType() + + @Serializable + data object Blocked : BadgeEventType() + + @Serializable + data object Deleted : BadgeEventType() + + @Serializable + data object None : BadgeEventType() } diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversationslist/model/ConversationItem.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversationslist/model/ConversationItem.kt index aefae1efbe..d5ae38f762 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversationslist/model/ConversationItem.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversationslist/model/ConversationItem.kt @@ -29,7 +29,9 @@ import com.wire.kalium.logic.data.user.ConnectionState import com.wire.kalium.logic.data.user.OtherUser import com.wire.kalium.logic.data.user.UserId import com.wire.kalium.logic.data.user.type.isTeammate +import kotlinx.serialization.Serializable +@Serializable sealed class ConversationItem : ConversationFolderItem { abstract val conversationId: ConversationId abstract val mutedStatus: MutedConversationStatus @@ -46,6 +48,7 @@ sealed class ConversationItem : ConversationFolderItem { val isTeamConversation get() = teamId != null + @Serializable data class GroupConversation( val groupName: String, val hasOnGoingCall: Boolean = false, @@ -66,6 +69,7 @@ sealed class ConversationItem : ConversationFolderItem { override val searchQuery: String = "", ) : ConversationItem() + @Serializable data class PrivateConversation( val userAvatarData: UserAvatarData, val conversationInfo: ConversationInfo, @@ -85,6 +89,7 @@ sealed class ConversationItem : ConversationFolderItem { override val searchQuery: String = "", ) : ConversationItem() + @Serializable data class ConnectionConversation( val userAvatarData: UserAvatarData, val conversationInfo: ConversationInfo, @@ -104,6 +109,7 @@ sealed class ConversationItem : ConversationFolderItem { } } +@Serializable data class ConversationInfo( val name: String, val membership: Membership = Membership.None, diff --git a/app/src/main/kotlin/com/wire/android/ui/home/gallery/MediaGalleryViewModel.kt b/app/src/main/kotlin/com/wire/android/ui/home/gallery/MediaGalleryViewModel.kt index 9d10a6888d..8198a2a420 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/gallery/MediaGalleryViewModel.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/gallery/MediaGalleryViewModel.kt @@ -34,7 +34,6 @@ import com.wire.android.ui.navArgs import com.wire.android.util.FileManager import com.wire.android.util.dispatchers.DispatcherProvider import com.wire.android.util.startFileShareIntent -import com.wire.android.util.ui.WireSessionImageLoader import com.wire.kalium.logic.data.conversation.ConversationDetails import com.wire.kalium.logic.data.id.QualifiedID import com.wire.kalium.logic.feature.asset.GetMessageAssetUseCase @@ -57,7 +56,6 @@ import javax.inject.Inject @HiltViewModel class MediaGalleryViewModel @Inject constructor( savedStateHandle: SavedStateHandle, - wireSessionImageLoader: WireSessionImageLoader, private val getConversationDetails: ObserveConversationDetailsUseCase, private val dispatchers: DispatcherProvider, private val getImageData: GetMessageAssetUseCase, @@ -67,7 +65,6 @@ class MediaGalleryViewModel @Inject constructor( private val mediaGalleryNavArgs: MediaGalleryNavArgs = savedStateHandle.navArgs() val imageAsset: ImageAsset.PrivateAsset = ImageAsset.PrivateAsset( - wireSessionImageLoader, mediaGalleryNavArgs.conversationId, mediaGalleryNavArgs.messageId, mediaGalleryNavArgs.isSelfAsset, diff --git a/app/src/main/kotlin/com/wire/android/ui/sharing/ImportMediaAuthenticatedViewModel.kt b/app/src/main/kotlin/com/wire/android/ui/sharing/ImportMediaAuthenticatedViewModel.kt index efda17b8c0..de0cfd71bf 100644 --- a/app/src/main/kotlin/com/wire/android/ui/sharing/ImportMediaAuthenticatedViewModel.kt +++ b/app/src/main/kotlin/com/wire/android/ui/sharing/ImportMediaAuthenticatedViewModel.kt @@ -45,7 +45,6 @@ import com.wire.android.ui.home.messagecomposer.SelfDeletionDuration import com.wire.android.util.EMPTY import com.wire.android.util.dispatchers.DispatcherProvider import com.wire.android.util.parcelableArrayList -import com.wire.android.util.ui.WireSessionImageLoader import com.wire.kalium.logic.data.id.ConversationId import com.wire.kalium.logic.data.message.SelfDeletionTimer import com.wire.kalium.logic.data.message.SelfDeletionTimer.Companion.SELF_DELETION_LOG_TAG @@ -80,7 +79,6 @@ class ImportMediaAuthenticatedViewModel @Inject constructor( private val handleUriAsset: HandleUriAssetUseCase, private val persistNewSelfDeletionTimerUseCase: PersistNewSelfDeletionTimerUseCase, private val observeSelfDeletionSettingsForConversation: ObserveSelfDeletionTimerSettingsForConversationUseCase, - private val wireSessionImageLoader: WireSessionImageLoader, val dispatchers: DispatcherProvider, ) : ViewModel() { val searchQueryTextState: TextFieldState = TextFieldState() @@ -124,7 +122,7 @@ class ImportMediaAuthenticatedViewModel @Inject constructor( getSelf().collect { selfUser -> withContext(dispatchers.main()) { avatarAsset = selfUser.previewPicture?.let { - ImageAsset.UserAvatarAsset(wireSessionImageLoader, it) + ImageAsset.UserAvatarAsset(it) } } } diff --git a/app/src/main/kotlin/com/wire/android/ui/userprofile/other/OtherUserProfileScreenViewModel.kt b/app/src/main/kotlin/com/wire/android/ui/userprofile/other/OtherUserProfileScreenViewModel.kt index c57fa29f6d..9f809c80af 100644 --- a/app/src/main/kotlin/com/wire/android/ui/userprofile/other/OtherUserProfileScreenViewModel.kt +++ b/app/src/main/kotlin/com/wire/android/ui/userprofile/other/OtherUserProfileScreenViewModel.kt @@ -47,7 +47,6 @@ import com.wire.android.ui.userprofile.other.OtherUserProfileInfoMessageType.Rem import com.wire.android.ui.userprofile.other.OtherUserProfileInfoMessageType.UnblockingUserOperationError import com.wire.android.util.dispatchers.DispatcherProvider import com.wire.android.util.ui.UIText -import com.wire.android.util.ui.WireSessionImageLoader import com.wire.kalium.logic.data.conversation.Conversation import com.wire.kalium.logic.data.conversation.MutedConversationStatus import com.wire.kalium.logic.data.id.ConversationId @@ -96,7 +95,6 @@ class OtherUserProfileScreenViewModel @Inject constructor( private val observeOneToOneConversation: GetOneToOneConversationUseCase, private val observeUserInfo: ObserveUserInfoUseCase, private val userTypeMapper: UserTypeMapper, - private val wireSessionImageLoader: WireSessionImageLoader, private val observeConversationRoleForUser: ObserveConversationRoleForUserUseCase, private val removeMemberFromConversation: RemoveMemberFromConversationUseCase, private val updateMemberRole: UpdateConversationMemberRoleUseCase, @@ -380,7 +378,7 @@ class OtherUserProfileScreenViewModel @Inject constructor( ) { val otherUser = userResult.otherUser val userAvatarAsset = otherUser.completePicture - ?.let { pic -> ImageAsset.UserAvatarAsset(wireSessionImageLoader, pic) } + ?.let { pic -> ImageAsset.UserAvatarAsset(pic) } state = state.copy( isDataLoading = false, diff --git a/app/src/main/kotlin/com/wire/android/ui/userprofile/self/SelfUserProfileViewModel.kt b/app/src/main/kotlin/com/wire/android/ui/userprofile/self/SelfUserProfileViewModel.kt index 2ae4c272fd..36439af8e3 100644 --- a/app/src/main/kotlin/com/wire/android/ui/userprofile/self/SelfUserProfileViewModel.kt +++ b/app/src/main/kotlin/com/wire/android/ui/userprofile/self/SelfUserProfileViewModel.kt @@ -39,7 +39,6 @@ import com.wire.android.notification.WireNotificationManager import com.wire.android.ui.legalhold.banner.LegalHoldUIState import com.wire.android.ui.userprofile.self.dialog.StatusDialogData import com.wire.android.util.dispatchers.DispatcherProvider -import com.wire.android.util.ui.WireSessionImageLoader import com.wire.kalium.logic.data.call.Call import com.wire.kalium.logic.data.id.QualifiedIdMapper import com.wire.kalium.logic.data.id.toQualifiedID @@ -91,7 +90,6 @@ class SelfUserProfileViewModel @Inject constructor( private val logout: LogoutUseCase, private val observeLegalHoldStatusForSelfUser: ObserveLegalHoldStateForSelfUserUseCase, private val dispatchers: DispatcherProvider, - private val wireSessionImageLoader: WireSessionImageLoader, private val otherAccountMapper: OtherAccountMapper, private val observeEstablishedCalls: ObserveEstablishedCallsUseCase, private val accountSwitch: AccountSwitchUseCase, @@ -220,7 +218,7 @@ class SelfUserProfileViewModel @Inject constructor( showLoadingAvatar(true) try { userProfileState = userProfileState.copy( - avatarAsset = UserAvatarAsset(wireSessionImageLoader, avatarAssetId) + avatarAsset = UserAvatarAsset(avatarAssetId) ) // Update avatar asset id on user data store // TODO: obtain the asset id through a useCase once we also store assets ids diff --git a/app/src/main/kotlin/com/wire/android/ui/userprofile/service/ServiceDetailsViewModel.kt b/app/src/main/kotlin/com/wire/android/ui/userprofile/service/ServiceDetailsViewModel.kt index 3fd3f12916..53a5f51813 100644 --- a/app/src/main/kotlin/com/wire/android/ui/userprofile/service/ServiceDetailsViewModel.kt +++ b/app/src/main/kotlin/com/wire/android/ui/userprofile/service/ServiceDetailsViewModel.kt @@ -28,7 +28,6 @@ import com.wire.android.ui.home.conversations.details.participants.usecase.Obser import com.wire.android.ui.navArgs import com.wire.android.util.dispatchers.DispatcherProvider import com.wire.android.util.ui.UIText -import com.wire.android.util.ui.WireSessionImageLoader import com.wire.kalium.logic.StorageFailure import com.wire.kalium.logic.data.conversation.Conversation import com.wire.kalium.logic.data.id.QualifiedID @@ -62,7 +61,6 @@ class ServiceDetailsViewModel @Inject constructor( private val getServiceById: GetServiceByIdUseCase, private val observeIsServiceMember: ObserveIsServiceMemberUseCase, private val observeConversationRoleForUser: ObserveConversationRoleForUserUseCase, - private val wireSessionImageLoader: WireSessionImageLoader, private val removeMemberFromConversation: RemoveMemberFromConversationUseCase, private val addServiceToConversation: AddServiceToConversationUseCase, private val serviceDetailsMapper: ServiceDetailsMapper, @@ -137,7 +135,7 @@ class ServiceDetailsViewModel @Inject constructor( getServiceById(serviceId = serviceId).also { service -> if (service != null) { val serviceAvatarAsset = service.completeAssetId?.let { asset -> - ImageAsset.UserAvatarAsset(wireSessionImageLoader, asset) + ImageAsset.UserAvatarAsset(asset) } serviceDetailsState = serviceDetailsState.copy( diff --git a/app/src/main/kotlin/com/wire/android/util/ui/LocalizedStringResource.kt b/app/src/main/kotlin/com/wire/android/util/ui/LocalizedStringResource.kt index b9a326ce9e..7f8f3ebf9d 100644 --- a/app/src/main/kotlin/com/wire/android/util/ui/LocalizedStringResource.kt +++ b/app/src/main/kotlin/com/wire/android/util/ui/LocalizedStringResource.kt @@ -17,37 +17,16 @@ */ package com.wire.android.util.ui -import android.content.Context import androidx.annotation.PluralsRes import androidx.annotation.StringRes +import kotlinx.serialization.Serializable +@Serializable sealed interface LocalizedStringResource { - fun getString(context: Context): String - data class StringResource(@StringRes val id: Int) : LocalizedStringResource { - override fun getString(context: Context): String = context.getString(id) - } + @Serializable + data class String(@StringRes val id: Int) : LocalizedStringResource - data class PluralResource(@PluralsRes val id: Int, val quantity: Int, val formatArgs: Array) : LocalizedStringResource { - override fun getString(context: Context): String = context.resources.getQuantityString(id, quantity, formatArgs) - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as PluralResource - - if (id != other.id) return false - if (quantity != other.quantity) return false - if (!formatArgs.contentEquals(other.formatArgs)) return false - - return true - } - - override fun hashCode(): Int { - var result = id - result = 31 * result + quantity - result = 31 * result + formatArgs.contentHashCode() - return result - } - } + @Serializable + data class Plural(@PluralsRes val id: Int, val quantity: Int) : LocalizedStringResource } diff --git a/app/src/main/kotlin/com/wire/android/util/ui/UIText.kt b/app/src/main/kotlin/com/wire/android/util/ui/UIText.kt index af3fd64d2b..14bdb4faea 100644 --- a/app/src/main/kotlin/com/wire/android/util/ui/UIText.kt +++ b/app/src/main/kotlin/com/wire/android/util/ui/UIText.kt @@ -26,22 +26,29 @@ import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.res.stringResource import com.wire.android.appLogger import com.wire.kalium.logic.data.message.mention.MessageMention +import com.wire.kalium.util.serialization.AnyPrimitiveValueSerializer +import kotlinx.serialization.Serializable +@Serializable sealed class UIText { + + @Serializable data class DynamicString( val value: String, val mentions: List = listOf() ) : UIText() + @Serializable class StringResource( @StringRes val resId: Int, - vararg val formatArgs: Any + vararg val formatArgs: @Serializable(with = AnyPrimitiveValueSerializer::class) Any ) : UIText() + @Serializable class PluralResource( @PluralsRes val resId: Int, val count: Int, - vararg val formatArgs: Any + vararg val formatArgs: @Serializable(with = AnyPrimitiveValueSerializer::class) Any ) : UIText() @Suppress("SpreadOperator") diff --git a/app/src/test/kotlin/com/wire/android/mapper/MessageMapperTest.kt b/app/src/test/kotlin/com/wire/android/mapper/MessageMapperTest.kt index d10a96f29d..3cde050371 100644 --- a/app/src/test/kotlin/com/wire/android/mapper/MessageMapperTest.kt +++ b/app/src/test/kotlin/com/wire/android/mapper/MessageMapperTest.kt @@ -32,7 +32,6 @@ import com.wire.android.ui.home.conversations.model.UIMessageContent.TextMessage import com.wire.android.ui.home.conversationslist.model.Membership import com.wire.android.util.time.ISOFormatter import com.wire.android.util.ui.UIText -import com.wire.android.util.ui.WireSessionImageLoader import com.wire.android.util.uiMessageDateTime import com.wire.kalium.logic.data.message.Message import com.wire.kalium.logic.data.message.MessageContent @@ -220,11 +219,8 @@ class MessageMapperTest { @MockK lateinit var isoFormatter: ISOFormatter - @MockK - private lateinit var wireSessionImageLoader: WireSessionImageLoader - private val messageMapper by lazy { - MessageMapper(userTypeMapper, messageContentMapper, isoFormatter, wireSessionImageLoader) + MessageMapper(userTypeMapper, messageContentMapper, isoFormatter) } init { diff --git a/app/src/test/kotlin/com/wire/android/mapper/OtherAccountMapperTest.kt b/app/src/test/kotlin/com/wire/android/mapper/OtherAccountMapperTest.kt index f192a52900..2f619df0c9 100644 --- a/app/src/test/kotlin/com/wire/android/mapper/OtherAccountMapperTest.kt +++ b/app/src/test/kotlin/com/wire/android/mapper/OtherAccountMapperTest.kt @@ -20,11 +20,9 @@ package com.wire.android.mapper import com.wire.android.ui.home.conversations.avatar import com.wire.android.ui.userprofile.self.model.OtherAccount -import com.wire.android.util.ui.WireSessionImageLoader import com.wire.kalium.logic.data.team.Team import com.wire.kalium.logic.data.user.SelfUser import io.mockk.MockKAnnotations -import io.mockk.impl.annotations.MockK import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Test @@ -45,27 +43,23 @@ class OtherAccountMapperTest { // Then results.forEachIndexed { index, result -> val (selfUser, team) = data[index] - assert(compareResult(arrangement.wireSessionImageLoader, selfUser, team, result)) + assert(compareResult(selfUser, team, result)) } } private fun compareResult( - wireSessionImageLoader: WireSessionImageLoader, selfUser: SelfUser, team: Team?, otherAccount: OtherAccount ): Boolean = selfUser.id == otherAccount.id && selfUser.name == otherAccount.fullName - && selfUser.avatar(wireSessionImageLoader, selfUser.connectionStatus) == otherAccount.avatarData + && selfUser.avatar(selfUser.connectionStatus) == otherAccount.avatarData && team?.name == otherAccount.teamName private class Arrangement { - @MockK - lateinit var wireSessionImageLoader: WireSessionImageLoader - - private val mapper: OtherAccountMapper by lazy { OtherAccountMapper(wireSessionImageLoader) } + private val mapper: OtherAccountMapper by lazy { OtherAccountMapper() } init { MockKAnnotations.init(this, relaxUnitFun = true) diff --git a/app/src/test/kotlin/com/wire/android/mapper/RegularMessageContentMapperTest.kt b/app/src/test/kotlin/com/wire/android/mapper/RegularMessageContentMapperTest.kt index 3158c9fc66..8d4f499623 100644 --- a/app/src/test/kotlin/com/wire/android/mapper/RegularMessageContentMapperTest.kt +++ b/app/src/test/kotlin/com/wire/android/mapper/RegularMessageContentMapperTest.kt @@ -28,7 +28,6 @@ import com.wire.android.ui.home.conversations.model.UIMessageContent import com.wire.android.ui.home.conversations.model.UIMessageContent.AssetMessage import com.wire.android.util.time.ISOFormatter import com.wire.android.util.ui.UIText -import com.wire.android.util.ui.WireSessionImageLoader import com.wire.kalium.logic.data.id.QualifiedID import com.wire.kalium.logic.data.message.AssetContent import com.wire.kalium.logic.data.message.AssetContent.AssetMetadata @@ -220,11 +219,8 @@ class RegularMessageContentMapperTest { @MockK lateinit var messageResourceProvider: MessageResourceProvider - @MockK - private lateinit var wireSessionImageLoader: WireSessionImageLoader - private val messageContentMapper by lazy { - RegularMessageMapper(messageResourceProvider, wireSessionImageLoader, ISOFormatter()) + RegularMessageMapper(messageResourceProvider, ISOFormatter()) } init { diff --git a/app/src/test/kotlin/com/wire/android/mapper/UICallParticipantMapperTest.kt b/app/src/test/kotlin/com/wire/android/mapper/UICallParticipantMapperTest.kt index 0c34b0124e..d3c23da2a6 100644 --- a/app/src/test/kotlin/com/wire/android/mapper/UICallParticipantMapperTest.kt +++ b/app/src/test/kotlin/com/wire/android/mapper/UICallParticipantMapperTest.kt @@ -21,7 +21,6 @@ package com.wire.android.mapper import com.wire.kalium.logic.data.call.Participant import com.wire.kalium.logic.data.id.QualifiedID import io.mockk.MockKAnnotations -import io.mockk.mockk import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Test @@ -54,7 +53,7 @@ class UICallParticipantMapperTest { private class Arrangement { - private val mapper: UICallParticipantMapper = UICallParticipantMapper(mockk(), UserTypeMapper()) + private val mapper: UICallParticipantMapper = UICallParticipantMapper(UserTypeMapper()) init { MockKAnnotations.init(this, relaxUnitFun = true) diff --git a/app/src/test/kotlin/com/wire/android/mapper/UIParticipantMapperTest.kt b/app/src/test/kotlin/com/wire/android/mapper/UIParticipantMapperTest.kt index a6f600441f..de63bc438b 100644 --- a/app/src/test/kotlin/com/wire/android/mapper/UIParticipantMapperTest.kt +++ b/app/src/test/kotlin/com/wire/android/mapper/UIParticipantMapperTest.kt @@ -23,7 +23,6 @@ import com.wire.android.ui.home.conversations.details.participants.model.UIParti import com.wire.android.ui.home.conversations.handle import com.wire.android.ui.home.conversations.name import com.wire.android.ui.home.conversations.userId -import com.wire.android.util.ui.WireSessionImageLoader import com.wire.kalium.logic.data.conversation.Conversation.Member import com.wire.kalium.logic.data.conversation.MemberDetails import com.wire.kalium.logic.data.id.TeamId @@ -36,7 +35,6 @@ import com.wire.kalium.logic.data.user.UserAvailabilityStatus import com.wire.kalium.logic.data.user.UserId import com.wire.kalium.logic.data.user.type.UserType import io.mockk.MockKAnnotations -import io.mockk.impl.annotations.MockK import kotlinx.coroutines.test.runTest import org.amshove.kluent.internal.assertEquals import org.junit.jupiter.api.Test @@ -58,7 +56,7 @@ class UIParticipantMapperTest { val results = data.map { mapper.toUIParticipant(it.user) } // Then results.forEachIndexed { index, result -> - assert(compareResult(arrangement.wireSessionImageLoader, data[index], result, arrangement.userTypeMapper)) + assert(compareResult(data[index], result, arrangement.userTypeMapper)) } } @@ -96,7 +94,6 @@ class UIParticipantMapperTest { } private fun compareResult( - wireSessionImageLoader: WireSessionImageLoader, memberDetails: MemberDetails, uiParticipant: UIParticipant, userTypeMapper: UserTypeMapper @@ -105,20 +102,17 @@ class UIParticipantMapperTest { return (memberDetails.userId == uiParticipant.id && memberDetails.name == uiParticipant.name && memberDetails.handle == uiParticipant.handle - && memberDetails.user.avatar(wireSessionImageLoader, connectionState) == uiParticipant.avatarData + && memberDetails.user.avatar(connectionState) == uiParticipant.avatarData && userTypeMapper.toMembership(memberDetails.user.userType) == uiParticipant.membership && memberDetails.user is SelfUser == uiParticipant.isSelf) } private class Arrangement { - @MockK - lateinit var wireSessionImageLoader: WireSessionImageLoader - val userTypeMapper: UserTypeMapper = UserTypeMapper() private val mapper: UIParticipantMapper by lazy { - UIParticipantMapper(userTypeMapper, wireSessionImageLoader) + UIParticipantMapper(userTypeMapper) } init { diff --git a/app/src/test/kotlin/com/wire/android/model/ImageAssetTest.kt b/app/src/test/kotlin/com/wire/android/model/ImageAssetTest.kt index 61bac0831a..eb8d3110c6 100644 --- a/app/src/test/kotlin/com/wire/android/model/ImageAssetTest.kt +++ b/app/src/test/kotlin/com/wire/android/model/ImageAssetTest.kt @@ -19,12 +19,10 @@ package com.wire.android.model import android.net.Uri -import com.wire.android.util.ui.WireSessionImageLoader import com.wire.kalium.logic.data.id.ConversationId import com.wire.kalium.logic.data.user.UserAssetId import io.mockk.MockKAnnotations import io.mockk.every -import io.mockk.impl.annotations.MockK import io.mockk.mockk import io.mockk.mockkStatic import okio.Path @@ -36,9 +34,6 @@ import org.junit.jupiter.api.Test class ImageAssetTest { - @MockK - private lateinit var imageLoader: WireSessionImageLoader - @BeforeEach fun setup() { MockKAnnotations.init(this, relaxUnitFun = true) @@ -47,15 +42,13 @@ class ImageAssetTest { every { Uri.parse(any()) } returns mockUri } - private fun createUserAvatarAsset(userAssetId: UserAssetId) = ImageAsset.UserAvatarAsset( - imageLoader, userAssetId - ) + private fun createUserAvatarAsset(userAssetId: UserAssetId) = ImageAsset.UserAvatarAsset(userAssetId) private fun createPrivateAsset( conversationId: ConversationId, messageId: String, isSelfAsset: Boolean - ) = ImageAsset.PrivateAsset(imageLoader, conversationId, messageId, isSelfAsset) + ) = ImageAsset.PrivateAsset(conversationId, messageId, isSelfAsset) private fun createLocalAsset( dataPath: Path, diff --git a/app/src/test/kotlin/com/wire/android/navigation/NavigationUtilsTest.kt b/app/src/test/kotlin/com/wire/android/navigation/NavigationUtilsTest.kt index ae5667d5c4..97f32e2b11 100644 --- a/app/src/test/kotlin/com/wire/android/navigation/NavigationUtilsTest.kt +++ b/app/src/test/kotlin/com/wire/android/navigation/NavigationUtilsTest.kt @@ -22,7 +22,6 @@ import com.wire.android.model.ImageAsset import com.wire.android.model.parseIntoPrivateImageAsset import com.wire.kalium.logic.data.id.QualifiedID import com.wire.kalium.logic.data.id.QualifiedIdMapperImpl -import io.mockk.mockk import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows @@ -44,7 +43,7 @@ internal class NavigationUtilsTest { "$mockConversationIdValue@$mockConversationIdDomain:$mockMessageId:$mockIsSelfAsset:$mockIsEphemeral" // When - val privateImgAsset = correctImagePrivateAssetString.parseIntoPrivateImageAsset(mockk(), qualifiedIdMapper) + val privateImgAsset = correctImagePrivateAssetString.parseIntoPrivateImageAsset(qualifiedIdMapper) // Then assertEquals(privateImgAsset.conversationId.value, mockConversationIdValue) @@ -62,7 +61,7 @@ internal class NavigationUtilsTest { val mockWrongImagePrivateAssetString = "wrong-private-asset@image" // When, Then - assertThrows { mockWrongImagePrivateAssetString.parseIntoPrivateImageAsset(mockk(), qualifiedIdMapper) } + assertThrows { mockWrongImagePrivateAssetString.parseIntoPrivateImageAsset(qualifiedIdMapper) } } @Test @@ -87,11 +86,13 @@ internal class NavigationUtilsTest { val mockQualifiedIdDomain = "mocked.domain" val mockMessageId = "mocked-message-id" val actualPrivateAssetImage = ImageAsset.PrivateAsset( - mockk(), - QualifiedID( + conversationId = QualifiedID( value = mockQualifiedIdValue, domain = mockQualifiedIdDomain - ), mockMessageId, true, true + ), + messageId = mockMessageId, + isSelfAsset = true, + isEphemeral = true ) val expectedPrivateAssetImage = "$mockQualifiedIdValue@$mockQualifiedIdDomain:$mockMessageId:true:true" diff --git a/app/src/test/kotlin/com/wire/android/ui/calling/SharedCallingViewModelTest.kt b/app/src/test/kotlin/com/wire/android/ui/calling/SharedCallingViewModelTest.kt index 6eaceb9050..18e62a8187 100644 --- a/app/src/test/kotlin/com/wire/android/ui/calling/SharedCallingViewModelTest.kt +++ b/app/src/test/kotlin/com/wire/android/ui/calling/SharedCallingViewModelTest.kt @@ -25,7 +25,6 @@ import com.wire.android.config.TestDispatcherProvider import com.wire.android.mapper.UICallParticipantMapper import com.wire.android.mapper.UserTypeMapper import com.wire.android.media.CallRinger -import com.wire.android.util.ui.WireSessionImageLoader import com.wire.kalium.logic.data.call.VideoState import com.wire.kalium.logic.data.id.ConversationId import com.wire.kalium.logic.feature.call.usecase.EndCallUseCase @@ -100,9 +99,6 @@ class SharedCallingViewModelTest { @MockK private lateinit var view: View - @MockK - private lateinit var wireSessionImageLoader: WireSessionImageLoader - @MockK private lateinit var userTypeMapper: UserTypeMapper @@ -110,10 +106,7 @@ class SharedCallingViewModelTest { private lateinit var onCompleted: () -> Unit private val uiCallParticipantMapper: UICallParticipantMapper by lazy { - UICallParticipantMapper( - wireSessionImageLoader, - userTypeMapper - ) + UICallParticipantMapper(userTypeMapper) } private lateinit var sharedCallingViewModel: SharedCallingViewModel @@ -141,7 +134,6 @@ class SharedCallingViewModelTest { observeSpeaker = observeSpeaker, callRinger = callRinger, uiCallParticipantMapper = uiCallParticipantMapper, - wireSessionImageLoader = wireSessionImageLoader, userTypeMapper = userTypeMapper, dispatchers = TestDispatcherProvider() ) diff --git a/app/src/test/kotlin/com/wire/android/ui/connection/ConnectionActionButtonViewModelTest.kt b/app/src/test/kotlin/com/wire/android/ui/connection/ConnectionActionButtonViewModelTest.kt index 58f1e3be4c..034026ff59 100644 --- a/app/src/test/kotlin/com/wire/android/ui/connection/ConnectionActionButtonViewModelTest.kt +++ b/app/src/test/kotlin/com/wire/android/ui/connection/ConnectionActionButtonViewModelTest.kt @@ -30,7 +30,6 @@ import com.wire.android.framework.TestConversation import com.wire.android.framework.TestUser import com.wire.android.ui.userprofile.other.OtherUserProfileScreenViewModelTest import com.wire.android.util.ui.UIText -import com.wire.android.util.ui.WireSessionImageLoader import com.wire.kalium.logic.CoreFailure import com.wire.kalium.logic.data.id.ConversationId import com.wire.kalium.logic.feature.connection.AcceptConnectionRequestUseCase @@ -343,9 +342,6 @@ internal class ConnectionActionButtonHiltArrangement { @MockK lateinit var ignoreConnectionRequest: IgnoreConnectionRequestUseCase - @MockK - lateinit var wireSessionImageLoader: WireSessionImageLoader - @MockK lateinit var unblockUser: UnblockUserUseCase diff --git a/app/src/test/kotlin/com/wire/android/ui/home/HomeViewModelTest.kt b/app/src/test/kotlin/com/wire/android/ui/home/HomeViewModelTest.kt index 28198da601..931a8ddad8 100644 --- a/app/src/test/kotlin/com/wire/android/ui/home/HomeViewModelTest.kt +++ b/app/src/test/kotlin/com/wire/android/ui/home/HomeViewModelTest.kt @@ -25,7 +25,6 @@ import com.wire.android.feature.analytics.AnonymousAnalyticsManager import com.wire.android.feature.analytics.model.AnalyticsEvent import com.wire.android.framework.TestUser import com.wire.android.migration.userDatabase.ShouldTriggerMigrationForUserUserCase -import com.wire.android.util.ui.WireSessionImageLoader import com.wire.kalium.logic.data.user.SelfUser import com.wire.kalium.logic.data.user.UserAvailabilityStatus import com.wire.kalium.logic.feature.client.NeedsToRegisterClientUseCase @@ -135,9 +134,6 @@ class HomeViewModelTest { @MockK lateinit var observeLegalHoldStatusForSelfUser: ObserveLegalHoldStateForSelfUserUseCase - @MockK - lateinit var wireSessionImageLoader: WireSessionImageLoader - @MockK lateinit var shouldTriggerMigrationForUser: ShouldTriggerMigrationForUserUserCase @@ -155,7 +151,6 @@ class HomeViewModelTest { getSelf = getSelf, needsToRegisterClient = needsToRegisterClient, observeLegalHoldStatusForSelfUser = observeLegalHoldStatusForSelfUser, - wireSessionImageLoader = wireSessionImageLoader, shouldTriggerMigrationForUser = shouldTriggerMigrationForUser, analyticsManager = analyticsManager, canMigrateFromPersonalToTeam = canMigrateFromPersonalToTeam diff --git a/app/src/test/kotlin/com/wire/android/ui/home/conversations/details/participants/usecase/ObserveParticipantsForConversationUseCaseTest.kt b/app/src/test/kotlin/com/wire/android/ui/home/conversations/details/participants/usecase/ObserveParticipantsForConversationUseCaseTest.kt index dc0e39f3f6..4f4aba479e 100644 --- a/app/src/test/kotlin/com/wire/android/ui/home/conversations/details/participants/usecase/ObserveParticipantsForConversationUseCaseTest.kt +++ b/app/src/test/kotlin/com/wire/android/ui/home/conversations/details/participants/usecase/ObserveParticipantsForConversationUseCaseTest.kt @@ -23,7 +23,6 @@ import com.wire.android.config.TestDispatcherProvider import com.wire.android.mapper.UIParticipantMapper import com.wire.android.mapper.UserTypeMapper import com.wire.android.mapper.testOtherUser -import com.wire.android.util.ui.WireSessionImageLoader import com.wire.kalium.logic.data.conversation.Conversation.Member import com.wire.kalium.logic.data.conversation.MemberDetails import com.wire.kalium.logic.data.id.ConversationId @@ -220,9 +219,7 @@ internal class ObserveParticipantsForConversationUseCaseArrangement { @MockK lateinit var getMembersE2EICertificateStatuses: GetMembersE2EICertificateStatusesUseCase - @MockK - private lateinit var wireSessionImageLoader: WireSessionImageLoader - private val uIParticipantMapper by lazy { UIParticipantMapper(UserTypeMapper(), wireSessionImageLoader) } + private val uIParticipantMapper by lazy { UIParticipantMapper(UserTypeMapper()) } private val conversationMembersChannel = Channel>(capacity = Channel.UNLIMITED) private val useCase by lazy { ObserveParticipantsForConversationUseCase( diff --git a/app/src/test/kotlin/com/wire/android/ui/home/conversations/info/ConversationInfoViewModelArrangement.kt b/app/src/test/kotlin/com/wire/android/ui/home/conversations/info/ConversationInfoViewModelArrangement.kt index 931b5f6280..778549c42a 100644 --- a/app/src/test/kotlin/com/wire/android/ui/home/conversations/info/ConversationInfoViewModelArrangement.kt +++ b/app/src/test/kotlin/com/wire/android/ui/home/conversations/info/ConversationInfoViewModelArrangement.kt @@ -23,7 +23,6 @@ import com.wire.android.config.mockUri import com.wire.android.framework.TestUser import com.wire.android.ui.home.conversations.ConversationNavArgs import com.wire.android.ui.navArgs -import com.wire.android.util.ui.WireSessionImageLoader import com.wire.kalium.logic.StorageFailure import com.wire.kalium.logic.data.conversation.ConversationDetails import com.wire.kalium.logic.data.id.ConversationId @@ -59,9 +58,6 @@ class ConversationInfoViewModelArrangement { @MockK lateinit var fetchConversationMLSVerificationStatus: FetchConversationMLSVerificationStatusUseCase - @MockK - private lateinit var wireSessionImageLoader: WireSessionImageLoader - @MockK(relaxed = true) lateinit var onNotFound: () -> Unit @@ -71,7 +67,6 @@ class ConversationInfoViewModelArrangement { savedStateHandle, observeConversationDetails, fetchConversationMLSVerificationStatus, - wireSessionImageLoader, selfUserId = TestUser.SELF_USER_ID, ) } diff --git a/app/src/test/kotlin/com/wire/android/ui/home/conversations/usecase/GetConversationsFromSearchUseCaseTest.kt b/app/src/test/kotlin/com/wire/android/ui/home/conversations/usecase/GetConversationsFromSearchUseCaseTest.kt index 16dc6cf6c4..d60659aa43 100644 --- a/app/src/test/kotlin/com/wire/android/ui/home/conversations/usecase/GetConversationsFromSearchUseCaseTest.kt +++ b/app/src/test/kotlin/com/wire/android/ui/home/conversations/usecase/GetConversationsFromSearchUseCaseTest.kt @@ -26,7 +26,6 @@ import com.wire.android.framework.TestUser import com.wire.android.mapper.UserTypeMapper import com.wire.android.ui.home.conversationslist.model.ConversationItem import com.wire.android.ui.home.conversationslist.model.Membership -import com.wire.android.util.ui.WireSessionImageLoader import com.wire.kalium.logic.data.conversation.ConversationDetailsWithEvents import com.wire.kalium.logic.data.conversation.ConversationFilter import com.wire.kalium.logic.data.conversation.ConversationFolder @@ -179,9 +178,6 @@ class GetConversationsFromSearchUseCaseTest { @MockK lateinit var observeConversationsFromFolderUseCase: ObserveConversationsFromFolderUseCase - @MockK - lateinit var wireSessionImageLoader: WireSessionImageLoader - @MockK lateinit var userTypeMapper: UserTypeMapper @@ -225,7 +221,6 @@ class GetConversationsFromSearchUseCaseTest { useCase, getFavoriteFolderUseCase, observeConversationsFromFolderUseCase, - wireSessionImageLoader, userTypeMapper, dispatcherProvider, observeSelfUser diff --git a/app/src/test/kotlin/com/wire/android/ui/home/conversationslist/ConversationListViewModelTest.kt b/app/src/test/kotlin/com/wire/android/ui/home/conversationslist/ConversationListViewModelTest.kt index 265393c65f..a1a0fc548f 100644 --- a/app/src/test/kotlin/com/wire/android/ui/home/conversationslist/ConversationListViewModelTest.kt +++ b/app/src/test/kotlin/com/wire/android/ui/home/conversationslist/ConversationListViewModelTest.kt @@ -35,7 +35,6 @@ import com.wire.android.ui.common.dialogs.BlockUserDialogState import com.wire.android.ui.home.conversations.usecase.GetConversationsFromSearchUseCase import com.wire.android.ui.home.conversationslist.model.ConversationItem import com.wire.android.ui.home.conversationslist.model.ConversationsSource -import com.wire.android.util.ui.WireSessionImageLoader import com.wire.kalium.logic.data.conversation.ConversationDetailsWithEvents import com.wire.kalium.logic.data.conversation.ConversationFilter import com.wire.kalium.logic.data.conversation.MutedConversationStatus @@ -272,9 +271,6 @@ class ConversationListViewModelTest { @MockK private lateinit var observeLegalHoldStateForSelfUserUseCase: ObserveLegalHoldStateForSelfUserUseCase - @MockK - private lateinit var wireSessionImageLoader: WireSessionImageLoader - @MockK private lateinit var observeSelfUser: GetSelfUserUseCase @@ -346,7 +342,6 @@ class ConversationListViewModelTest { observeConversationListDetailsWithEvents = observeConversationListDetailsWithEventsUseCase, observeLegalHoldStateForSelfUser = observeLegalHoldStateForSelfUserUseCase, userTypeMapper = UserTypeMapper(), - wireSessionImageLoader = wireSessionImageLoader, observeSelfUser = observeSelfUser, usePagination = true ) diff --git a/app/src/test/kotlin/com/wire/android/ui/home/gallery/MediaGalleryViewModelTest.kt b/app/src/test/kotlin/com/wire/android/ui/home/gallery/MediaGalleryViewModelTest.kt index d41787b15c..80d101cae1 100644 --- a/app/src/test/kotlin/com/wire/android/ui/home/gallery/MediaGalleryViewModelTest.kt +++ b/app/src/test/kotlin/com/wire/android/ui/home/gallery/MediaGalleryViewModelTest.kt @@ -29,7 +29,6 @@ import com.wire.android.ui.home.conversations.delete.DeleteMessageDialogActiveSt import com.wire.android.ui.home.conversations.delete.DeleteMessageDialogsState import com.wire.android.ui.navArgs import com.wire.android.util.FileManager -import com.wire.android.util.ui.WireSessionImageLoader import com.wire.kalium.logic.CoreFailure import com.wire.kalium.logic.data.conversation.Conversation import com.wire.kalium.logic.data.conversation.ConversationDetails @@ -192,9 +191,6 @@ class MediaGalleryViewModelTest { @MockK private lateinit var savedStateHandle: SavedStateHandle - @MockK - private lateinit var wireSessionImageLoader: WireSessionImageLoader - @MockK lateinit var getConversationDetails: ObserveConversationDetailsUseCase @@ -271,7 +267,6 @@ class MediaGalleryViewModelTest { fun arrange() = this to MediaGalleryViewModel( savedStateHandle, - wireSessionImageLoader, getConversationDetails, TestDispatcherProvider(), getImageData, diff --git a/app/src/test/kotlin/com/wire/android/ui/sharing/ImportMediaAuthenticatedViewModelTest.kt b/app/src/test/kotlin/com/wire/android/ui/sharing/ImportMediaAuthenticatedViewModelTest.kt index f2623f18c9..e305a258bc 100644 --- a/app/src/test/kotlin/com/wire/android/ui/sharing/ImportMediaAuthenticatedViewModelTest.kt +++ b/app/src/test/kotlin/com/wire/android/ui/sharing/ImportMediaAuthenticatedViewModelTest.kt @@ -28,7 +28,6 @@ import com.wire.android.framework.TestConversationItem import com.wire.android.framework.TestUser import com.wire.android.ui.home.conversations.usecase.GetConversationsFromSearchUseCase import com.wire.android.ui.home.conversations.usecase.HandleUriAssetUseCase -import com.wire.android.util.ui.WireSessionImageLoader import com.wire.kalium.logic.feature.selfDeletingMessages.ObserveSelfDeletionTimerSettingsForConversationUseCase import com.wire.kalium.logic.feature.selfDeletingMessages.PersistNewSelfDeletionTimerUseCase import com.wire.kalium.logic.feature.user.GetSelfUserUseCase @@ -87,9 +86,6 @@ class ImportMediaAuthenticatedViewModelTest { @MockK lateinit var observeSelfDeletionSettingsForConversation: ObserveSelfDeletionTimerSettingsForConversationUseCase - @MockK - lateinit var wireSessionImageLoader: WireSessionImageLoader - init { MockKAnnotations.init(this, relaxUnitFun = true) coEvery { @@ -109,7 +105,6 @@ class ImportMediaAuthenticatedViewModelTest { handleUriAsset = handleUriAssetUseCase, persistNewSelfDeletionTimerUseCase = persistNewSelfDeletionTimerUseCase, observeSelfDeletionSettingsForConversation = observeSelfDeletionSettingsForConversation, - wireSessionImageLoader = wireSessionImageLoader, dispatchers = dispatcherProvider, ) } diff --git a/app/src/test/kotlin/com/wire/android/ui/userprofile/other/OtherUserProfileViewModelArrangement.kt b/app/src/test/kotlin/com/wire/android/ui/userprofile/other/OtherUserProfileViewModelArrangement.kt index e550f0188d..f0a5443b53 100644 --- a/app/src/test/kotlin/com/wire/android/ui/userprofile/other/OtherUserProfileViewModelArrangement.kt +++ b/app/src/test/kotlin/com/wire/android/ui/userprofile/other/OtherUserProfileViewModelArrangement.kt @@ -28,7 +28,6 @@ import com.wire.android.ui.home.conversationslist.model.Membership import com.wire.android.ui.navArgs import com.wire.android.ui.userprofile.other.OtherUserProfileScreenViewModelTest.Companion.CONVERSATION_ID import com.wire.android.ui.userprofile.other.OtherUserProfileScreenViewModelTest.Companion.USER_ID -import com.wire.android.util.ui.WireSessionImageLoader import com.wire.kalium.logic.data.id.ConversationId import com.wire.kalium.logic.feature.client.FetchUsersClientsFromRemoteUseCase import com.wire.kalium.logic.feature.client.ObserveClientsByUserIdUseCase @@ -44,8 +43,8 @@ import com.wire.kalium.logic.feature.conversation.UpdateConversationArchivedStat import com.wire.kalium.logic.feature.conversation.UpdateConversationMemberRoleResult import com.wire.kalium.logic.feature.conversation.UpdateConversationMemberRoleUseCase import com.wire.kalium.logic.feature.conversation.UpdateConversationMutedStatusUseCase -import com.wire.kalium.logic.feature.e2ei.usecase.IsOtherUserE2EIVerifiedUseCase import com.wire.kalium.logic.feature.e2ei.usecase.GetUserE2eiCertificatesUseCase +import com.wire.kalium.logic.feature.e2ei.usecase.IsOtherUserE2EIVerifiedUseCase import com.wire.kalium.logic.feature.user.GetSelfUserUseCase import com.wire.kalium.logic.feature.user.GetUserInfoResult import com.wire.kalium.logic.feature.user.ObserveUserInfoUseCase @@ -66,9 +65,6 @@ internal class OtherUserProfileViewModelArrangement { @MockK lateinit var observeUserInfo: ObserveUserInfoUseCase - @MockK - lateinit var wireSessionImageLoader: WireSessionImageLoader - @MockK lateinit var observeConversationRoleForUserUseCase: ObserveConversationRoleForUserUseCase @@ -123,7 +119,6 @@ internal class OtherUserProfileViewModelArrangement { getOneToOneConversation, observeUserInfo, userTypeMapper, - wireSessionImageLoader, observeConversationRoleForUserUseCase, removeMemberFromConversationUseCase, updateConversationMemberRoleUseCase, diff --git a/app/src/test/kotlin/com/wire/android/ui/userprofile/self/SelfUserProfileViewModelArrangement.kt b/app/src/test/kotlin/com/wire/android/ui/userprofile/self/SelfUserProfileViewModelArrangement.kt index c9ef3d6604..47b10e19c1 100644 --- a/app/src/test/kotlin/com/wire/android/ui/userprofile/self/SelfUserProfileViewModelArrangement.kt +++ b/app/src/test/kotlin/com/wire/android/ui/userprofile/self/SelfUserProfileViewModelArrangement.kt @@ -28,7 +28,6 @@ import com.wire.android.framework.TestUser import com.wire.android.mapper.OtherAccountMapper import com.wire.android.notification.WireNotificationManager import com.wire.android.util.dispatchers.DispatcherProvider -import com.wire.android.util.ui.WireSessionImageLoader import com.wire.kalium.logic.data.id.QualifiedIdMapper import com.wire.kalium.logic.feature.auth.LogoutUseCase import com.wire.kalium.logic.feature.call.usecase.EndCallUseCase @@ -72,9 +71,6 @@ class SelfUserProfileViewModelArrangement { @MockK lateinit var dispatchers: DispatcherProvider - @MockK - lateinit var wireSessionImageLoader: WireSessionImageLoader - @MockK lateinit var otherAccountMapper: OtherAccountMapper @@ -116,7 +112,6 @@ class SelfUserProfileViewModelArrangement { logout = logout, observeLegalHoldStatusForSelfUser = observeLegalHoldStatusForSelfUser, dispatchers = TestDispatcherProvider(), - wireSessionImageLoader = wireSessionImageLoader, otherAccountMapper = otherAccountMapper, observeEstablishedCalls = observeEstablishedCalls, accountSwitch = accountSwitch, diff --git a/app/src/test/kotlin/com/wire/android/ui/userprofile/service/ServiceDetailsViewModelTest.kt b/app/src/test/kotlin/com/wire/android/ui/userprofile/service/ServiceDetailsViewModelTest.kt index d5a5026c0d..44165d9727 100644 --- a/app/src/test/kotlin/com/wire/android/ui/userprofile/service/ServiceDetailsViewModelTest.kt +++ b/app/src/test/kotlin/com/wire/android/ui/userprofile/service/ServiceDetailsViewModelTest.kt @@ -28,7 +28,6 @@ import com.wire.android.ui.home.conversations.details.participants.usecase.Conve import com.wire.android.ui.home.conversations.details.participants.usecase.ObserveConversationRoleForUserUseCase import com.wire.android.ui.navArgs import com.wire.android.ui.userprofile.other.OtherUserProfileScreenViewModelTest -import com.wire.android.util.ui.WireSessionImageLoader import com.wire.kalium.logic.CoreFailure import com.wire.kalium.logic.StorageFailure import com.wire.kalium.logic.data.conversation.Conversation @@ -295,9 +294,6 @@ class ServiceDetailsViewModelTest { @MockK lateinit var observeConversationRoleForUser: ObserveConversationRoleForUserUseCase - @MockK - lateinit var wireSessionImageLoader: WireSessionImageLoader - @MockK lateinit var removeMemberFromConversation: RemoveMemberFromConversationUseCase @@ -316,7 +312,6 @@ class ServiceDetailsViewModelTest { getServiceById, observeIsServiceMember, observeConversationRoleForUser, - wireSessionImageLoader, removeMemberFromConversation, addServiceToConversation, serviceDetailsMapper, diff --git a/app/src/test/kotlin/com/wire/android/util/ui/AssetImageFetcherTest.kt b/app/src/test/kotlin/com/wire/android/util/ui/AssetImageFetcherTest.kt index bc9b49db4c..c30e6346bc 100644 --- a/app/src/test/kotlin/com/wire/android/util/ui/AssetImageFetcherTest.kt +++ b/app/src/test/kotlin/com/wire/android/util/ui/AssetImageFetcherTest.kt @@ -53,7 +53,7 @@ internal class AssetImageFetcherTest { val someUserAssetId = AssetId("value", "domain") val someDummyData = "some-dummy-data".toByteArray() val someDummyName = "some-dummy-name" - val data = ImageAsset.UserAvatarAsset(mockk(), someUserAssetId) + val data = ImageAsset.UserAvatarAsset(someUserAssetId) val avatarPath = fakeKaliumFileSystem.selfUserAvatarPath() val (arrangement, assetImageFetcher) = Arrangement() .withSuccessfulImageData(data, avatarPath, someDummyData.size.toLong(), someDummyName) @@ -74,7 +74,7 @@ internal class AssetImageFetcherTest { val someUserAssetId = AssetId("value", "domain") val someDummyData = "some-dummy-data".toByteArray() val someDummyName = "some-dummy-name" - val data = ImageAsset.UserAvatarAsset(mockk(), someUserAssetId) + val data = ImageAsset.UserAvatarAsset(someUserAssetId) val avatarPath = fakeKaliumFileSystem.selfUserAvatarPath() val (arrangement, assetImageFetcher) = Arrangement() .withSuccessfulImageData(data, avatarPath, someDummyData.size.toLong(), someDummyName, 1) @@ -97,7 +97,7 @@ internal class AssetImageFetcherTest { val someMessageId = "some-message-id" val someDummyData = "some-dummy-data".toByteArray() val someDummyName = "some-dummy-name" - val data = ImageAsset.PrivateAsset(mockk(), someConversationId, someMessageId, true) + val data = ImageAsset.PrivateAsset(someConversationId, someMessageId, true) val avatarPath = fakeKaliumFileSystem.selfUserAvatarPath() val (arrangement, assetImageFetcher) = Arrangement() .withSuccessfulImageData(data, avatarPath, 1, someDummyName) @@ -117,7 +117,7 @@ internal class AssetImageFetcherTest { fun givenAUserAvatarAssetData_WhenCallingFetchUnsuccessfully_ThenFetchResultIsNotReturned() = runTest { // Given val someUserAssetId = AssetId("value", "domain") - val data = ImageAsset.UserAvatarAsset(mockk(), someUserAssetId) + val data = ImageAsset.UserAvatarAsset(someUserAssetId) val (arrangement, assetImageFetcher) = Arrangement().withErrorResponse(data).arrange() // When @@ -132,7 +132,7 @@ internal class AssetImageFetcherTest { // Given val someConversationId = ConversationId("some-value", "some-domain") val someMessageId = "some-message-id" - val data = ImageAsset.PrivateAsset(mockk(), someConversationId, someMessageId, true) + val data = ImageAsset.PrivateAsset(someConversationId, someMessageId, true) val (arrangement, assetImageFetcher) = Arrangement().withErrorResponse(data).arrange() // When @@ -147,7 +147,7 @@ internal class AssetImageFetcherTest { runTest { // Given val someUserAssetId = AssetId("value", "domain") - val data = ImageAsset.UserAvatarAsset(mockk(), someUserAssetId) + val data = ImageAsset.UserAvatarAsset(someUserAssetId) val (arrangement, assetImageFetcher) = Arrangement() .withErrorResponse( data = data, @@ -169,7 +169,7 @@ internal class AssetImageFetcherTest { runTest { // Given val someUserAssetId = AssetId("value", "domain") - val data = ImageAsset.UserAvatarAsset(mockk(), someUserAssetId) + val data = ImageAsset.UserAvatarAsset(someUserAssetId) val (arrangement, assetImageFetcher) = Arrangement() .withErrorResponse( data = data, @@ -191,7 +191,7 @@ internal class AssetImageFetcherTest { runTest { // Given val someUserAssetId = AssetId("value", "domain") - val data = ImageAsset.UserAvatarAsset(mockk(), someUserAssetId) + val data = ImageAsset.UserAvatarAsset(someUserAssetId) val (arrangement, assetImageFetcher) = Arrangement() .withErrorResponse( data = data, @@ -214,7 +214,7 @@ internal class AssetImageFetcherTest { // Given val someConversationId = ConversationId("some-value", "some-domain") val someMessageId = "some-message-id" - val data = ImageAsset.PrivateAsset(mockk(), someConversationId, someMessageId, true) + val data = ImageAsset.PrivateAsset(someConversationId, someMessageId, true) val (arrangement, assetImageFetcher) = Arrangement() .withErrorResponse( data = data, @@ -237,7 +237,7 @@ internal class AssetImageFetcherTest { // Given val someConversationId = ConversationId("some-value", "some-domain") val someMessageId = "some-message-id" - val data = ImageAsset.PrivateAsset(mockk(), someConversationId, someMessageId, true) + val data = ImageAsset.PrivateAsset(someConversationId, someMessageId, true) val (arrangement, assetImageFetcher) = Arrangement() .withErrorResponse( data = data, @@ -260,7 +260,7 @@ internal class AssetImageFetcherTest { // Given val someConversationId = ConversationId("some-value", "some-domain") val someMessageId = "some-message-id" - val data = ImageAsset.PrivateAsset(mockk(), someConversationId, someMessageId, true) + val data = ImageAsset.PrivateAsset(someConversationId, someMessageId, true) val (arrangement, assetImageFetcher) = Arrangement() .withErrorResponse( data = data, diff --git a/core/ui-common/build.gradle.kts b/core/ui-common/build.gradle.kts index ac41ac5e46..5f6cb899b8 100644 --- a/core/ui-common/build.gradle.kts +++ b/core/ui-common/build.gradle.kts @@ -1,6 +1,7 @@ plugins { id(libs.plugins.wire.android.library.get().pluginId) id(libs.plugins.wire.kover.get().pluginId) + alias(libs.plugins.kotlin.serialization) } android { @@ -12,6 +13,8 @@ dependencies { implementation(libs.androidx.core) implementation(libs.androidx.appcompat) implementation(libs.material) + implementation(libs.ktx.serialization) + implementation(libs.bundlizer.core) val composeBom = platform(libs.compose.bom) implementation(composeBom) diff --git a/core/ui-common/src/main/kotlin/com/wire/android/ui/common/bottomsheet/WireModalSheetState.kt b/core/ui-common/src/main/kotlin/com/wire/android/ui/common/bottomsheet/WireModalSheetState.kt index a43ddb1432..ffcdacf1cc 100644 --- a/core/ui-common/src/main/kotlin/com/wire/android/ui/common/bottomsheet/WireModalSheetState.kt +++ b/core/ui-common/src/main/kotlin/com/wire/android/ui/common/bottomsheet/WireModalSheetState.kt @@ -17,6 +17,7 @@ */ package com.wire.android.ui.common.bottomsheet +import android.os.Bundle import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.SheetState import androidx.compose.material3.SheetValue @@ -31,12 +32,16 @@ import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.platform.SoftwareKeyboardController import androidx.compose.ui.unit.Density +import dev.ahmedmourad.bundlizer.Bundlizer import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.launch +import kotlinx.serialization.InternalSerializationApi +import kotlinx.serialization.serializer +import kotlinx.serialization.serializerOrNull @OptIn(ExperimentalMaterial3Api::class) -open class WireModalSheetState internal constructor( +open class WireModalSheetState( density: Density, private val scope: CoroutineScope, private val keyboardController: SoftwareKeyboardController? = null, @@ -84,33 +89,43 @@ open class WireModalSheetState internal constructor( companion object { const val DELAY_TO_SHOW_BOTTOM_SHEET_WHEN_KEYBOARD_IS_OPEN = 300L - @Suppress("UNCHECKED_CAST") - fun saver( + @OptIn(InternalSerializationApi::class) + inline fun saver( density: Density, softwareKeyboardController: SoftwareKeyboardController?, - onDismissAction: () -> Unit, + noinline onDismissAction: () -> Unit, scope: CoroutineScope ): Saver, *> = Saver( save = { - val isExpanded = it.currentValue is WireSheetValue.Expanded - val (isValueOfTypeUnit, value) = (it.currentValue as? WireSheetValue.Expanded)?.let { - val isValueOfTypeUnit = it.value is Unit // Unit cannot be saved into Bundle, need to handle it separately - val value = if (isValueOfTypeUnit) null else it.value - isValueOfTypeUnit to value - } ?: (false to null) - listOf(isExpanded, isValueOfTypeUnit, value) + when (it.currentValue) { + is WireSheetValue.Hidden -> listOf(false) // hidden + is WireSheetValue.Expanded -> { + val value = (it.currentValue as WireSheetValue.Expanded).value + when { + value is Unit -> // expanded and with Unit value + listOf(true, SavedType.Unit) + + canBeSaved(value) -> // expanded and non-Unit value that can be saved normally + listOf(true, SavedType.Regular, value) + + T::class.serializerOrNull() != null -> // expanded and with non-Unit value that can be serialized + listOf(true, SavedType.SerializedBundle, Bundlizer.bundle(T::class.serializer(), value)) + + else -> listOf(false) // hidden because value cannot be saved + } + } + } }, restore = { savedValue -> val isExpanded = savedValue[0] as Boolean val sheetValue = when (isExpanded) { - true -> { - val isValueOfTypeUnit = savedValue[1] as Boolean - if (isValueOfTypeUnit) { - WireSheetValue.Expanded(Unit as T) - } else { - val value = savedValue[2] as T - WireSheetValue.Expanded(value) - } + true -> when (savedValue[1] as SavedType) { + SavedType.Unit -> WireSheetValue.Expanded(Unit as T) + + SavedType.Regular -> WireSheetValue.Expanded(savedValue[2] as T) + + SavedType.SerializedBundle -> + WireSheetValue.Expanded(Bundlizer.unbundle(T::class.serializer(), savedValue[2] as Bundle)) } false -> WireSheetValue.Hidden @@ -121,6 +136,8 @@ open class WireModalSheetState internal constructor( } } +enum class SavedType { Unit, Regular, SerializedBundle } + @OptIn(ExperimentalMaterial3Api::class) sealed class WireSheetValue(val originalValue: SheetValue) { data object Hidden : WireSheetValue(SheetValue.Hidden) @@ -135,9 +152,9 @@ sealed class WireSheetValue(val originalValue: SheetValue) { * @param onDismissAction The action to be executed when the sheet is dismissed. */ @Composable -fun rememberWireModalSheetState( +inline fun rememberWireModalSheetState( initialValue: WireSheetValue = WireSheetValue.Hidden, - onDismissAction: () -> Unit = {} + noinline onDismissAction: () -> Unit = {} ): WireModalSheetState { val softwareKeyboardController = LocalSoftwareKeyboardController.current val density = LocalDensity.current