Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import com.anytypeio.anytype.domain.misc.AppActionManager
import com.anytypeio.anytype.domain.misc.DateProvider
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.multiplayer.ActiveSpaceMemberSubscriptionContainer
import com.anytypeio.anytype.domain.multiplayer.ParticipantSubscriptionContainer
import com.anytypeio.anytype.domain.multiplayer.SpaceInviteResolver
import com.anytypeio.anytype.domain.multiplayer.SpaceViewSubscriptionContainer
import com.anytypeio.anytype.domain.multiplayer.UserPermissionProvider
Expand Down Expand Up @@ -307,6 +308,7 @@ interface HomeScreenDependencies : ComponentDependencies {
fun analyticSpaceHelperDelegate(): AnalyticSpaceHelperDelegate
fun storeOfRelations(): StoreOfRelations
fun spaceViewSubscriptionContainer(): SpaceViewSubscriptionContainer
fun participantSubscriptionContainer(): ParticipantSubscriptionContainer
fun featureToggles(): FeatureToggles
fun payloadDelegator(): PayloadDelegator
fun fieldParser(): FieldParser
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.anytypeio.anytype.presentation.extension

import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.ObjectWrapper

/**
* Resolves a participant's display name by identity from the participant map.
* Uses the standard fallback chain: name β†’ globalName β†’ fallback.
*
* @param identity The identity ID to look up
* @param fallback The fallback string if participant not found, has no name, or identity is null/empty
* @return The resolved name or fallback - always returns a non-null value
*/
fun Map<Id, ObjectWrapper.SpaceMember>.resolveParticipantName(
identity: Id?,
fallback: String
): String {
if (identity.isNullOrEmpty()) return fallback
val participant = this[identity]
return participant?.name
?: participant?.globalName
?: fallback
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.multiplayer.ActiveSpaceMemberSubscriptionContainer
import com.anytypeio.anytype.domain.multiplayer.CopyInviteLinkToClipboard
import com.anytypeio.anytype.domain.multiplayer.GetSpaceInviteLink
import com.anytypeio.anytype.domain.multiplayer.ParticipantSubscriptionContainer
import com.anytypeio.anytype.domain.multiplayer.SpaceInviteResolver
import com.anytypeio.anytype.domain.multiplayer.SpaceViewSubscriptionContainer
import com.anytypeio.anytype.domain.multiplayer.UserPermissionProvider
Expand Down Expand Up @@ -242,6 +243,7 @@ class HomeScreenViewModel(
private val spaceMembers: ActiveSpaceMemberSubscriptionContainer,
private val setAsFavourite: SetObjectListIsFavorite,
private val chatPreviews: ChatPreviewContainer,
private val participantContainer: ParticipantSubscriptionContainer,
private val notificationPermissionManager: NotificationPermissionManager,
private val copyInviteLinkToClipboard: CopyInviteLinkToClipboard,
private val userSettingsRepository: UserSettingsRepository,
Expand Down Expand Up @@ -3205,6 +3207,7 @@ class HomeScreenViewModel(
WidgetContainerDelegateImpl(
spaceId = vmParams.spaceId,
chatPreviews = chatPreviews,
participantContainer = participantContainer,
spaceViewSubscriptionContainer = spaceViewSubscriptionContainer,
notificationPermissionManager = notificationPermissionManager,
fieldParser = fieldParser,
Expand Down Expand Up @@ -3283,6 +3286,7 @@ class HomeScreenViewModel(
private val activeSpaceMemberSubscriptionContainer: ActiveSpaceMemberSubscriptionContainer,
private val setObjectListIsFavorite: SetObjectListIsFavorite,
private val chatPreviews: ChatPreviewContainer,
private val participantContainer: ParticipantSubscriptionContainer,
private val notificationPermissionManager: NotificationPermissionManager,
private val copyInviteLinkToClipboard: CopyInviteLinkToClipboard,
private val userRepo: UserSettingsRepository,
Expand Down Expand Up @@ -3345,6 +3349,7 @@ class HomeScreenViewModel(
spaceMembers = activeSpaceMemberSubscriptionContainer,
setAsFavourite = setObjectListIsFavorite,
chatPreviews = chatPreviews,
participantContainer = participantContainer,
notificationPermissionManager = notificationPermissionManager,
copyInviteLinkToClipboard = copyInviteLinkToClipboard,
userSettingsRepository = userRepo,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ import com.anytypeio.anytype.domain.base.fold
import com.anytypeio.anytype.domain.chats.ChatPreviewContainer
import com.anytypeio.anytype.domain.chats.ChatsDetailsSubscriptionContainer
import com.anytypeio.anytype.domain.deeplink.PendingIntentStore
import com.anytypeio.anytype.domain.multiplayer.ParticipantSubscriptionContainer
import com.anytypeio.anytype.domain.misc.AppActionManager
import com.anytypeio.anytype.domain.misc.DateProvider
import com.anytypeio.anytype.domain.misc.DeepLinkResolver
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.multiplayer.ParticipantSubscriptionContainer
import com.anytypeio.anytype.domain.multiplayer.SpaceInviteResolver
import com.anytypeio.anytype.domain.multiplayer.SpaceViewSubscriptionContainer
import com.anytypeio.anytype.domain.multiplayer.UserPermissionProvider
Expand All @@ -46,13 +46,13 @@ import com.anytypeio.anytype.domain.vault.UnpinSpace
import com.anytypeio.anytype.domain.wallpaper.GetSpaceWallpapers
import com.anytypeio.anytype.domain.workspace.SpaceManager
import com.anytypeio.anytype.presentation.BuildConfig
import com.anytypeio.anytype.presentation.extension.resolveParticipantName
import com.anytypeio.anytype.presentation.home.OpenObjectNavigation
import com.anytypeio.anytype.presentation.home.navigation
import com.anytypeio.anytype.presentation.mapper.objectIcon
import com.anytypeio.anytype.presentation.navigation.DeepLinkToObjectDelegate
import com.anytypeio.anytype.presentation.notifications.NotificationPermissionManager
import com.anytypeio.anytype.presentation.notifications.NotificationPermissionManagerImpl
import com.anytypeio.anytype.presentation.notifications.NotificationStateCalculator
import com.anytypeio.anytype.presentation.notifications.NotificationStateCalculator.calculateChatNotificationState
import com.anytypeio.anytype.presentation.objects.ObjectIcon
import com.anytypeio.anytype.presentation.objects.ObjectIcon.FileDefault
Expand Down Expand Up @@ -493,16 +493,13 @@ class VaultViewModel(
chatDetailsMap: Map<Id, ObjectWrapper.Basic>,
participantsByIdentity: Map<Id, ObjectWrapper.SpaceMember>
): VaultSpaceView.ChatSpace {
val creatorId = chatPreview?.message?.creator
val messageText = chatPreview?.message?.content?.text

// Resolve creator name from cross-space participants container
val creatorName = if (!creatorId.isNullOrEmpty()) {
val participant = participantsByIdentity[creatorId]
participant?.name ?: participant?.globalName ?: stringResourceProvider.getUntitledCreatorName()
} else {
null
}
val creatorName = participantsByIdentity.resolveParticipantName(
identity = chatPreview?.message?.creator,
fallback = stringResourceProvider.getUntitledCreatorName()
)

val messageTime = chatPreview?.message?.createdAt?.let { timeInSeconds ->
if (timeInSeconds > 0) {
Expand Down Expand Up @@ -565,16 +562,13 @@ class VaultViewModel(
chatDetailsMap: Map<Id, ObjectWrapper.Basic>,
participantsByIdentity: Map<Id, ObjectWrapper.SpaceMember>
): VaultSpaceView.DataSpaceWithChat {
val creatorId = chatPreview.message?.creator
val messageText = chatPreview.message?.content?.text

// Resolve creator name from cross-space participants container
val creatorName = if (!creatorId.isNullOrEmpty()) {
val participant = participantsByIdentity[creatorId]
participant?.name ?: participant?.globalName ?: stringResourceProvider.getUntitledCreatorName()
} else {
null
}
val creatorName = participantsByIdentity.resolveParticipantName(
identity = chatPreview.message?.creator,
fallback = stringResourceProvider.getUntitledCreatorName()
)

val messageTime = chatPreview.message?.createdAt?.let { timeInSeconds ->
if (timeInSeconds > 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,34 +8,36 @@ import com.anytypeio.anytype.core_models.ObjectWrapper
import com.anytypeio.anytype.core_models.Relations
import com.anytypeio.anytype.core_models.chats.Chat
import com.anytypeio.anytype.core_models.chats.NotificationState
import com.anytypeio.anytype.core_utils.const.MimeTypes
import com.anytypeio.anytype.core_models.ext.content
import com.anytypeio.anytype.core_models.ext.isValidObject
import com.anytypeio.anytype.core_models.getSingleValue
import com.anytypeio.anytype.core_models.primitives.SpaceId
import com.anytypeio.anytype.core_utils.const.MimeTypes
import com.anytypeio.anytype.domain.chats.ChatPreviewContainer
import com.anytypeio.anytype.domain.library.StoreSearchParams
import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
import com.anytypeio.anytype.domain.misc.DateProvider
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.multiplayer.ParticipantSubscriptionContainer
import com.anytypeio.anytype.domain.multiplayer.SpaceViewSubscriptionContainer
import com.anytypeio.anytype.domain.resources.StringResourceProvider
import com.anytypeio.anytype.domain.`object`.GetObject
import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
import com.anytypeio.anytype.domain.objects.StoreOfRelations
import com.anytypeio.anytype.domain.objects.getTypeOfObject
import com.anytypeio.anytype.domain.primitives.FieldParser
import com.anytypeio.anytype.domain.resources.StringResourceProvider
import com.anytypeio.anytype.presentation.editor.cover.CoverImageHashProvider
import com.anytypeio.anytype.presentation.extension.resolveParticipantName
import com.anytypeio.anytype.presentation.mapper.objectIcon
import com.anytypeio.anytype.presentation.notifications.NotificationStateCalculator
import com.anytypeio.anytype.presentation.objects.ObjectIcon
import com.anytypeio.anytype.presentation.relations.cover
import com.anytypeio.anytype.presentation.vault.VaultSpaceView
import com.anytypeio.anytype.domain.objects.StoreOfRelations
import com.anytypeio.anytype.presentation.search.ObjectSearchConstants
import com.anytypeio.anytype.presentation.sets.subscription.updateWithRelationFormat
import com.anytypeio.anytype.presentation.vault.VaultSpaceView
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.emptyFlow
Expand Down Expand Up @@ -69,6 +71,7 @@ class ChatListWidgetContainer(
private val dateProvider: DateProvider,
private val stringResourceProvider: StringResourceProvider,
private val spaceViewSubscriptionContainer: SpaceViewSubscriptionContainer,
private val participantContainer: ParticipantSubscriptionContainer,
isSessionActiveFlow: Flow<Boolean>,
onRequestCache: () -> WidgetView? = { null },
) : WidgetContainer {
Expand Down Expand Up @@ -174,6 +177,14 @@ class ChatListWidgetContainer(
.observe()
.distinctUntilChanged()

// Observe global participants for creator name resolution
val participantsFlow = participantContainer
.observe()
.map { participants ->
participants.associateBy { it.identity }
}
.distinctUntilChanged()

val chats = view.flatMapLatest { view ->
val chats = view.elements.map { it.obj.id }
previews
Expand All @@ -184,16 +195,23 @@ class ChatListWidgetContainer(
}
.distinctUntilChanged()
.flatMapLatest { previewList ->
spaceViews.map { spaces ->
combine(
spaceViews,
participantsFlow
) { spaces, participantsByIdentity ->
view.copy(
elements = view.elements.map { element ->
val preview = previewList.find { p ->
p.chat == element.obj.id
}
val state = preview?.state
if (preview != null && state != null) {
// Extract preview data
val creatorName = extractCreatorName(preview)
// Extract preview data using participant subscription for creator names
val creatorName =
participantsByIdentity.resolveParticipantName(
identity = preview.message?.creator,
fallback = stringResourceProvider.getUntitledCreatorName()
)
val messageText = preview.message?.content?.text
val messageTime = preview.message?.createdAt?.let { timeInSeconds ->
if (timeInSeconds > 0) {
Expand Down Expand Up @@ -449,19 +467,6 @@ class ChatListWidgetContainer(
)
}
}

/**
* Extracts creator name from chat preview dependencies.
*/
private fun extractCreatorName(preview: Chat.Preview): String? {
val creatorId = preview.message?.creator
if (creatorId.isNullOrEmpty()) return null

val creatorObj = preview.dependencies.find {
it.getSingleValue<String>(Relations.IDENTITY) == creatorId
}
return creatorObj?.name ?: stringResourceProvider.getUntitledCreatorName()
}

/**
* Transforms a Chat.Message.Attachment to VaultSpaceView.AttachmentPreview.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import com.anytypeio.anytype.domain.config.UserSettingsRepository
import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
import com.anytypeio.anytype.domain.misc.DateProvider
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.multiplayer.ParticipantSubscriptionContainer
import com.anytypeio.anytype.domain.multiplayer.SpaceViewSubscriptionContainer
import com.anytypeio.anytype.domain.`object`.GetObject
import com.anytypeio.anytype.domain.objects.ObjectWatcher
Expand Down Expand Up @@ -54,6 +55,7 @@ interface WidgetContainerDelegate {
class WidgetContainerDelegateImpl(
private val spaceId: SpaceId,
private val chatPreviews: ChatPreviewContainer,
private val participantContainer: ParticipantSubscriptionContainer,
private val spaceViewSubscriptionContainer: SpaceViewSubscriptionContainer,
private val notificationPermissionManager: NotificationPermissionManager,
private val fieldParser: FieldParser,
Expand Down Expand Up @@ -231,7 +233,8 @@ class WidgetContainerDelegateImpl(
chatPreviewContainer = chatPreviews,
dateProvider = dateProvider,
stringResourceProvider = stringResourceProvider,
spaceViewSubscriptionContainer = spaceViewSubscriptionContainer
spaceViewSubscriptionContainer = spaceViewSubscriptionContainer,
participantContainer = participantContainer
)
} else {
DataViewListWidgetContainer(
Expand Down Expand Up @@ -299,7 +302,8 @@ class WidgetContainerDelegateImpl(
chatPreviewContainer = chatPreviews,
dateProvider = dateProvider,
stringResourceProvider = stringResourceProvider,
spaceViewSubscriptionContainer = spaceViewSubscriptionContainer
spaceViewSubscriptionContainer = spaceViewSubscriptionContainer,
participantContainer = participantContainer
)
} else {
DataViewListWidgetContainer(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.multiplayer.ActiveSpaceMemberSubscriptionContainer
import com.anytypeio.anytype.domain.multiplayer.CopyInviteLinkToClipboard
import com.anytypeio.anytype.domain.multiplayer.GetSpaceInviteLink
import com.anytypeio.anytype.domain.multiplayer.ParticipantSubscriptionContainer
import com.anytypeio.anytype.domain.multiplayer.SpaceInviteResolver
import com.anytypeio.anytype.domain.multiplayer.SpaceViewSubscriptionContainer
import com.anytypeio.anytype.domain.multiplayer.UserPermissionProvider
Expand Down Expand Up @@ -293,6 +294,9 @@ class HomeScreenViewModelTest {
@Mock
lateinit var notificationPermissionManager: NotificationPermissionManager

@Mock
lateinit var participantContainer: ParticipantSubscriptionContainer

lateinit var userPermissionProvider: UserPermissionProvider

private val objectPayloadDispatcher = Dispatcher.Default<Payload>()
Expand Down Expand Up @@ -3037,6 +3041,7 @@ class HomeScreenViewModelTest {
deleteSpace = deleteSpace,
setAsFavourite = setObjectListIsFavorite,
chatPreviews = chacPreviewContainer,
participantContainer = participantContainer,
notificationPermissionManager = notificationPermissionManager,
copyInviteLinkToClipboard = copyInviteLinkToClipboard,
userSettingsRepository = userSettingsRepository,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,13 +85,11 @@ class VaultViewModelTest {
shouldShowCreateSpaceBadge.stub {
onBlocking { async(any()) }.thenReturn(Resultat.Success(false))
}
participantSubscriptionContainer.stub {
onBlocking { observe() }.thenReturn(flowOf(emptyList()))
}
chatsDetailsSubscriptionContainer.stub {
onBlocking { observe() }.thenReturn(flowOf(emptyList()))
}
whenever(participantSubscriptionContainer.observe()).thenReturn(flowOf(emptyList()))
whenever(chatsDetailsSubscriptionContainer.observe()).thenReturn(flowOf(emptyList()))
whenever(notificationPermissionManager.areNotificationsEnabled()).thenReturn(true)
whenever(appInfo.versionName).thenReturn("1.0.0")
whenever(stringResourceProvider.getUntitledCreatorName()).thenReturn("Untitled")
}

@Test
Expand Down