1
0
Fork 0
mirror of https://github.com/anyproto/anytype-kotlin.git synced 2025-06-08 05:47:05 +09:00

DROID-3239 Participant card | UI + logic (#1992)

This commit is contained in:
Konstantin Ivanov 2025-01-17 14:05:17 +01:00 committed by GitHub
parent 28bc323b73
commit 9ded869c33
Signed by: github
GPG key ID: B5690EEEBB952194
59 changed files with 950 additions and 69 deletions

View file

@ -169,7 +169,7 @@ dependencies {
implementation project(':library-page-icon-picker-widget')
implementation project(':library-emojifier')
implementation project(':analytics')
implementation project(':ui-settings')
implementation project(':feature-ui-settings')
implementation project(':crash-reporting')
implementation project(':payments')
implementation project(':feature-chats')

View file

@ -66,6 +66,7 @@ import com.anytypeio.anytype.di.feature.onboarding.DaggerOnboardingStartComponen
import com.anytypeio.anytype.di.feature.onboarding.login.DaggerOnboardingMnemonicLoginComponent
import com.anytypeio.anytype.di.feature.onboarding.signup.DaggerOnboardingMnemonicComponent
import com.anytypeio.anytype.di.feature.onboarding.signup.DaggerOnboardingSoulCreationComponent
import com.anytypeio.anytype.di.feature.participant.DaggerParticipantComponent
import com.anytypeio.anytype.di.feature.relations.DaggerRelationCreateFromLibraryComponent
import com.anytypeio.anytype.di.feature.relations.DaggerRelationEditComponent
import com.anytypeio.anytype.di.feature.relations.LimitObjectTypeModule
@ -115,6 +116,7 @@ import com.anytypeio.anytype.presentation.multiplayer.RequestJoinSpaceViewModel
import com.anytypeio.anytype.presentation.multiplayer.ShareSpaceViewModel
import com.anytypeio.anytype.presentation.multiplayer.SpaceJoinRequestViewModel
import com.anytypeio.anytype.presentation.objects.SelectObjectTypeViewModel
import com.anytypeio.anytype.presentation.profile.ParticipantViewModel
import com.anytypeio.anytype.presentation.relations.RelationAddViewModelBase
import com.anytypeio.anytype.presentation.relations.RelationListViewModel
import com.anytypeio.anytype.presentation.relations.option.CreateOrEditOptionViewModel
@ -343,6 +345,12 @@ class ComponentManager(
.create(params, findComponentDependencies())
}
val participantScreenComponent = ComponentWithParams { params: ParticipantViewModel.VmParams ->
DaggerParticipantComponent
.factory()
.create(params, findComponentDependencies())
}
val dateObjectComponent = ComponentWithParams { params: DateObjectVmParams ->
DaggerDateObjectComponent
.factory()

View file

@ -0,0 +1,112 @@
package com.anytypeio.anytype.di.feature.participant
import androidx.lifecycle.ViewModelProvider
import com.anytypeio.anytype.analytics.base.Analytics
import com.anytypeio.anytype.core_utils.di.scope.PerScreen
import com.anytypeio.anytype.di.common.ComponentDependencies
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
import com.anytypeio.anytype.domain.block.repo.BlockRepository
import com.anytypeio.anytype.domain.config.ConfigStorage
import com.anytypeio.anytype.domain.config.UserSettingsRepository
import com.anytypeio.anytype.domain.debugging.Logger
import com.anytypeio.anytype.domain.icon.SetDocumentImageIcon
import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
import com.anytypeio.anytype.domain.misc.LocaleProvider
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.multiplayer.UserPermissionProvider
import com.anytypeio.anytype.domain.`object`.SetObjectDetails
import com.anytypeio.anytype.domain.primitives.FieldParser
import com.anytypeio.anytype.domain.search.SubscriptionEventChannel
import com.anytypeio.anytype.presentation.analytics.AnalyticSpaceHelperDelegate
import com.anytypeio.anytype.presentation.membership.provider.MembershipProvider
import com.anytypeio.anytype.presentation.profile.ParticipantViewModel
import com.anytypeio.anytype.ui.profile.ParticipantFragment
import dagger.Binds
import dagger.BindsInstance
import dagger.Component
import dagger.Module
import dagger.Provides
@Component(
dependencies = [ParticipantComponentDependencies::class],
modules = [
ParticipantModule::class,
ParticipantModule.Declarations::class
]
)
@PerScreen
interface ParticipantComponent {
@Component.Factory
interface Factory {
fun create(
@BindsInstance vmParams: ParticipantViewModel.VmParams,
dependencies: ParticipantComponentDependencies
): ParticipantComponent
}
fun inject(fragment: ParticipantFragment)
}
@Module
object ParticipantModule {
@JvmStatic
@Provides
@PerScreen
fun provideSetObjectDetails(
repo: BlockRepository,
dispatchers: AppCoroutineDispatchers
): SetObjectDetails = SetObjectDetails(
repo,
dispatchers
)
@JvmStatic
@Provides
@PerScreen
fun provideSetDocumentImageIconUseCase(
repo: BlockRepository
): SetDocumentImageIcon = SetDocumentImageIcon(repo)
@JvmStatic
@Provides
@PerScreen
fun provideStoreLessSubscriptionContainer(
repo: BlockRepository,
channel: SubscriptionEventChannel,
dispatchers: AppCoroutineDispatchers,
logger: Logger
): StorelessSubscriptionContainer = StorelessSubscriptionContainer.Impl(
repo = repo,
channel = channel,
dispatchers = dispatchers,
logger = logger
)
@Module
interface Declarations {
@PerScreen
@Binds
fun bindViewModelFactory(
factory: ParticipantViewModel.Factory
): ViewModelProvider.Factory
}
}
interface ParticipantComponentDependencies : ComponentDependencies {
fun blockRepository(): BlockRepository
fun analytics(): Analytics
fun urlBuilder(): UrlBuilder
fun dispatchers(): AppCoroutineDispatchers
fun analyticsHelper(): AnalyticSpaceHelperDelegate
fun userSettingsRepository(): UserSettingsRepository
fun logger(): Logger
fun localeProvider(): LocaleProvider
fun userPermissionProvider(): UserPermissionProvider
fun fieldsProvider(): FieldParser
fun provideMembershipProvider(): MembershipProvider
fun subEventChannel(): SubscriptionEventChannel
fun provideConfigStorage(): ConfigStorage
}

View file

@ -37,6 +37,7 @@ import com.anytypeio.anytype.di.feature.onboarding.OnboardingStartDependencies
import com.anytypeio.anytype.di.feature.onboarding.login.OnboardingMnemonicLoginDependencies
import com.anytypeio.anytype.di.feature.onboarding.signup.OnboardingMnemonicDependencies
import com.anytypeio.anytype.di.feature.onboarding.signup.OnboardingSoulCreationDependencies
import com.anytypeio.anytype.di.feature.participant.ParticipantComponentDependencies
import com.anytypeio.anytype.di.feature.relations.RelationCreateFromLibraryDependencies
import com.anytypeio.anytype.di.feature.relations.RelationEditDependencies
import com.anytypeio.anytype.di.feature.search.GlobalSearchDependencies
@ -139,7 +140,8 @@ interface MainComponent :
MoveToDependencies,
DateObjectDependencies,
SelectChatReactionDependencies,
ChatReactionDependencies
ChatReactionDependencies,
ParticipantComponentDependencies
{
fun inject(app: AndroidApplication)
@ -400,4 +402,9 @@ abstract class ComponentDependenciesModule {
@IntoMap
@ComponentDependenciesKey(ChatReactionDependencies::class)
abstract fun provideChatReactionDependencies(component: MainComponent): ComponentDependencies
@Binds
@IntoMap
@ComponentDependenciesKey(ParticipantComponentDependencies::class)
abstract fun provideParticipantComponentDependencies(component: MainComponent): ComponentDependencies
}

View file

@ -16,6 +16,7 @@ import com.anytypeio.anytype.ui.date.DateObjectFragment
import com.anytypeio.anytype.ui.editor.EditorFragment
import com.anytypeio.anytype.ui.editor.EditorModalFragment
import com.anytypeio.anytype.ui.multiplayer.ShareSpaceFragment
import com.anytypeio.anytype.ui.profile.ParticipantFragment
import com.anytypeio.anytype.ui.relations.RelationCreateFromScratchForObjectFragment
import com.anytypeio.anytype.ui.relations.RelationEditFragment
import com.anytypeio.anytype.ui.search.GlobalSearchFragment
@ -342,4 +343,17 @@ class Navigator : AppNavigation {
space = space)
)
}
override fun openParticipantObject(
objectId: Id,
space: Id
) {
navController?.navigate(
resId = R.id.participantScreen,
args = ParticipantFragment.args(
objectId = objectId,
space = space
)
)
}
}

View file

@ -38,6 +38,7 @@ import com.anytypeio.anytype.ui.base.navigation
import com.anytypeio.anytype.ui.multiplayer.ShareSpaceFragment
import com.anytypeio.anytype.ui.objects.creation.ObjectTypeSelectionFragment
import com.anytypeio.anytype.ui.objects.types.pickers.ObjectTypeSelectionListener
import com.anytypeio.anytype.ui.profile.ParticipantFragment
import com.anytypeio.anytype.ui.relations.REQUEST_KEY_MODIFY_RELATION
import com.anytypeio.anytype.ui.relations.REQUEST_KEY_UNINSTALL_RELATION
import com.anytypeio.anytype.ui.relations.REQUEST_UNINSTALL_RELATION_ARG_ID
@ -278,6 +279,20 @@ class AllContentFragment : BaseComposeFragment(), ObjectTypeSelectionListener {
Timber.e(e, "Error while opening date object from All Objects screen")
}
}
is AllContentViewModel.Command.NavigateToParticipant -> {
runCatching {
findNavController().navigate(
R.id.participantScreen,
ParticipantFragment.args(
objectId = command.objectId,
space = command.space
)
)
}.onFailure {
Timber.w("Error while opening participant screen")
}
}
}
}
}

View file

@ -62,6 +62,10 @@ class NavigationRouter(
objectId = command.objectId,
space = command.space
)
is AppNavigation.Command.OpenParticipant -> navigation.openParticipantObject(
objectId = command.objectId,
space = command.space
)
else -> Timber.d("Nav command ignored: $command")
}
} catch (e: Exception) {

View file

@ -39,6 +39,7 @@ import com.anytypeio.anytype.feature_date.viewmodel.DateObjectVmParams
import com.anytypeio.anytype.ui.base.navigation
import com.anytypeio.anytype.ui.objects.creation.ObjectTypeSelectionFragment
import com.anytypeio.anytype.ui.objects.types.pickers.ObjectTypeSelectionListener
import com.anytypeio.anytype.ui.profile.ParticipantFragment
import com.anytypeio.anytype.ui.search.GlobalSearchFragment
import com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi
import com.google.accompanist.navigation.material.rememberBottomSheetNavigator
@ -163,6 +164,19 @@ class DateObjectFragment : BaseComposeFragment(), ObjectTypeSelectionListener {
val dialog = ObjectTypeSelectionFragment.new(space = space)
dialog.show(childFragmentManager, null)
}
is DateObjectCommand.NavigateToParticipant -> {
runCatching {
findNavController().navigate(
R.id.participantScreen,
ParticipantFragment.args(
objectId = effect.objectId,
space = effect.space.id
)
)
}.onFailure {
Timber.w("Error while opening participant screen")
}
}
}
}
}

View file

@ -57,6 +57,9 @@ import com.anytypeio.anytype.ui.objects.types.pickers.ObjectTypeSelectionListene
import com.anytypeio.anytype.ui.objects.types.pickers.WidgetObjectTypeListener
import com.anytypeio.anytype.ui.objects.types.pickers.WidgetSourceTypeListener
import com.anytypeio.anytype.ui.payments.MembershipFragment
import com.anytypeio.anytype.ui.profile.ParticipantFragment
import com.anytypeio.anytype.ui.search.GlobalSearchScreen
import com.anytypeio.anytype.ui.sets.ObjectSetFragment
import com.anytypeio.anytype.ui.settings.space.SpaceSettingsFragment
import com.anytypeio.anytype.ui.settings.typography
import com.anytypeio.anytype.ui.widgets.SelectWidgetSourceFragment
@ -446,6 +449,16 @@ class HomeScreenFragment : BaseComposeFragment(),
Timber.e(e, "Error while opening date object from widgets")
}
}
is Navigation.OpenParticipant -> {
runCatching {
navigation().openParticipantObject(
objectId = destination.objectId,
space = destination.space
)
}.onFailure { e ->
Timber.e(e, "Error while opening participant from widgets")
}
}
}
}

View file

@ -60,6 +60,7 @@ import com.anytypeio.anytype.ui.multiplayer.ShareSpaceFragment
import com.anytypeio.anytype.ui.multiplayer.SpaceJoinRequestFragment
import com.anytypeio.anytype.ui.notifications.NotificationsFragment
import com.anytypeio.anytype.ui.payments.MembershipFragment
import com.anytypeio.anytype.ui.profile.ParticipantFragment
import com.anytypeio.anytype.ui.sets.ObjectSetFragment
import com.anytypeio.anytype.ui.sharing.SharingFragment
import com.anytypeio.anytype.ui_settings.appearance.ThemeApplicator
@ -209,6 +210,19 @@ class MainActivity : AppCompatActivity(R.layout.activity_main), AppNavigation.Pr
Timber.e(it, "Error while data view navigation")
}
}
is OpenObjectNavigation.OpenParticipant -> {
runCatching {
findNavController(R.id.fragment).navigate(
R.id.participantScreen,
ParticipantFragment.args(
objectId = dest.target,
space = dest.space
)
)
}.onFailure {
Timber.w("Error while opening participant screen")
}
}
is OpenObjectNavigation.OpenEditor -> {
runCatching {
findNavController(R.id.fragment).navigate(
@ -234,7 +248,7 @@ class MainActivity : AppCompatActivity(R.layout.activity_main), AppNavigation.Pr
OpenObjectNavigation.NonValidObject -> {
toast(getString(R.string.error_non_valid_object))
}
is OpenObjectNavigation.OpenDataObject -> {
is OpenObjectNavigation.OpenDateObject -> {
runCatching {
findNavController(R.id.fragment).navigate(
R.id.dateObjectScreen,

View file

@ -26,6 +26,7 @@ import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetComposeFragment
import com.anytypeio.anytype.di.common.componentManager
import com.anytypeio.anytype.presentation.multiplayer.ShareSpaceViewModel
import com.anytypeio.anytype.presentation.multiplayer.ShareSpaceViewModel.Command
import com.anytypeio.anytype.ui.profile.ParticipantFragment
import com.anytypeio.anytype.ui.settings.typography
import javax.inject.Inject
import timber.log.Timber
@ -69,7 +70,8 @@ class ShareSpaceFragment : BaseBottomSheetComposeFragment() {
onDeleteLinkClicked = vm::onDeleteLinkClicked,
incentiveState = vm.showIncentive.collectAsStateWithLifecycle().value,
onIncentiveClicked = vm::onIncentiveClicked,
isLoadingInProgress = vm.isLoadingInProgress.collectAsStateWithLifecycle().value
isLoadingInProgress = vm.isLoadingInProgress.collectAsStateWithLifecycle().value,
onMemberClicked = vm::onMemberClicked
)
}
LaunchedEffect(Unit) {
@ -215,6 +217,19 @@ class ShareSpaceFragment : BaseBottomSheetComposeFragment() {
}
}
}
is Command.OpenParticipantObject -> {
runCatching {
findNavController().navigate(
R.id.participantScreen,
ParticipantFragment.args(
space = command.space.id,
objectId = command.objectId
)
)
}.onFailure {
Timber.e(it, "Error while navigation: $command")
}
}
}
}

View file

@ -0,0 +1,102 @@
package com.anytypeio.anytype.ui.profile
import android.os.Bundle
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.LaunchedEffect
import androidx.core.os.bundleOf
import androidx.fragment.app.viewModels
import androidx.fragment.compose.content
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.fragment.findNavController
import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.primitives.SpaceId
import com.anytypeio.anytype.core_ui.features.profile.ParticipantScreen
import com.anytypeio.anytype.core_utils.ext.argString
import com.anytypeio.anytype.core_utils.ext.toast
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetComposeFragment
import com.anytypeio.anytype.di.common.componentManager
import com.anytypeio.anytype.presentation.profile.ParticipantViewModel
import javax.inject.Inject
import timber.log.Timber
class ParticipantFragment: BaseBottomSheetComposeFragment() {
@Inject
lateinit var factory: ParticipantViewModel.Factory
private val vm by viewModels<ParticipantViewModel> { factory }
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
) = content {
MaterialTheme {
ParticipantScreen(
uiState = vm.uiState.collectAsStateWithLifecycle().value,
onEvent = vm::onEvent
)
LaunchedEffect(Unit) {
vm.commands.collect { command ->
proceedWithCommand(command)
}
}
}
}
private fun proceedWithCommand(command: ParticipantViewModel.Command) {
when (command) {
ParticipantViewModel.Command.Dismiss -> {
findNavController().popBackStack()
}
is ParticipantViewModel.Command.Toast.Error -> {
toast(command.msg)
}
ParticipantViewModel.Command.OpenSettingsProfile -> {
runCatching {
findNavController().navigate(
R.id.profileSettingsScreen,
null
)
}.onFailure {
Timber.w("Error while opening participant screen")
}
}
}
}
override fun onStart() {
super.onStart()
vm.onStart()
}
override fun onStop() {
super.onStop()
vm.onStop()
}
override fun injectDependencies() {
val vmParams = ParticipantViewModel.VmParams(
objectId = argString(ARG_OBJECT_ID),
space = SpaceId(argString(ARG_SPACE))
)
componentManager().participantScreenComponent.get(vmParams).inject(this)
}
override fun releaseDependencies() {
componentManager().participantScreenComponent.release()
}
companion object ProfileScreenNavigation {
const val ARG_SPACE = "arg.participant.screen.space"
const val ARG_OBJECT_ID = "arg.participant.screen.object_id"
fun args(space: Id, objectId: Id) = bundleOf(
ARG_SPACE to space,
ARG_OBJECT_ID to objectId
)
}
}

View file

@ -27,6 +27,7 @@ import com.anytypeio.anytype.presentation.home.OpenObjectNavigation
import com.anytypeio.anytype.presentation.search.GlobalSearchViewModel
import com.anytypeio.anytype.ui.date.DateObjectFragment
import com.anytypeio.anytype.ui.editor.EditorFragment
import com.anytypeio.anytype.ui.profile.ParticipantFragment
import com.anytypeio.anytype.ui.sets.ObjectSetFragment
import com.anytypeio.anytype.ui.settings.typography
import javax.inject.Inject
@ -92,6 +93,19 @@ class GlobalSearchFragment : BaseBottomSheetComposeFragment() {
)
)
}
is OpenObjectNavigation.OpenParticipant -> {
runCatching {
findNavController().navigate(
R.id.participantScreen,
ParticipantFragment.args(
objectId = nav.target,
space = nav.space
)
)
}.onFailure {
Timber.w("Error while opening participant screen")
}
}
is OpenObjectNavigation.OpenChat -> {
findNavController().navigate(
R.id.chatScreen,
@ -104,7 +118,7 @@ class GlobalSearchFragment : BaseBottomSheetComposeFragment() {
OpenObjectNavigation.NonValidObject -> {
toast(getString(R.string.error_non_valid_object))
}
is OpenObjectNavigation.OpenDataObject -> {
is OpenObjectNavigation.OpenDateObject -> {
runCatching {
findNavController().navigate(
R.id.dateObjectScreen,

View file

@ -110,6 +110,9 @@ class RemoteFilesManageFragment : BaseBottomSheetComposeFragment() {
is CollectionViewModel.Command.OpenShareScreen -> {
// Do nothing
}
is CollectionViewModel.Command.OpenParticipant -> {
// Do nothing
}
}
}

View file

@ -109,7 +109,7 @@ class VaultFragment : BaseComposeFragment() {
is Command.OpenProfileSettings -> {
runCatching {
findNavController().navigate(
R.id.profileScreen,
R.id.profileSettingsScreen,
null
)
}.onFailure {
@ -192,6 +192,18 @@ class VaultFragment : BaseComposeFragment() {
Timber.e(e, "Error while opening date object from widgets")
}
}
is Navigation.OpenParticipant -> {
runCatching {
findNavController().navigate(R.id.actionOpenSpaceFromVault)
navigation().openParticipantObject(
objectId = destination.ctx,
space = destination.space
)
}.onFailure { e ->
Timber.e(e, "Error while opening participant object from widgets")
}
}
}
}

View file

@ -142,6 +142,17 @@ class CollectionFragment : BaseComposeFragment(), ObjectTypeSelectionListener {
Timber.e(it, "Error while opening share screen")
}
}
is Command.OpenParticipant -> {
runCatching {
navigation().openParticipantObject(
objectId = command.target,
space = command.space
)
}.onFailure { e ->
Timber.e(e, "Error while opening participant object from Collection screen")
}
}
}
}

View file

@ -176,6 +176,11 @@
android:name="com.anytypeio.anytype.ui.chats.ChatReactionFragment"
android:label="Chat reaction" />
<dialog
android:id="@+id/participantScreen"
android:name="com.anytypeio.anytype.ui.profile.ParticipantFragment"
android:label="Profile screen" />
<fragment
android:id="@+id/homeScreen"
android:name="com.anytypeio.anytype.ui.home.HomeScreenFragment"
@ -306,7 +311,7 @@
android:label="Object-Set-Icon-Picker-Screen" />
<dialog
android:id="@+id/profileScreen"
android:id="@+id/profileSettingsScreen"
android:name="com.anytypeio.anytype.ui.settings.ProfileSettingsFragment">
</dialog>

View file

@ -76,6 +76,7 @@ import com.anytypeio.anytype.core_ui.foundation.Dragger
import com.anytypeio.anytype.core_ui.foundation.Section
import com.anytypeio.anytype.core_ui.foundation.Toolbar
import com.anytypeio.anytype.core_ui.foundation.noRippleClickable
import com.anytypeio.anytype.core_ui.foundation.noRippleThrottledClickable
import com.anytypeio.anytype.core_ui.views.BodyRegular
import com.anytypeio.anytype.core_ui.views.ButtonSecondary
import com.anytypeio.anytype.core_ui.views.ButtonSize
@ -111,7 +112,8 @@ fun ShareSpaceScreen(
onMoreInfoClicked: () -> Unit,
onShareQrCodeClicked: () -> Unit,
onDeleteLinkClicked: () -> Unit,
onIncentiveClicked: () -> Unit
onIncentiveClicked: () -> Unit,
onMemberClicked: (ObjectWrapper.SpaceMember) -> Unit
) {
val nestedScrollInteropConnection = rememberNestedScrollInteropConnection()
Box(
@ -237,7 +239,8 @@ fun ShareSpaceScreen(
icon = member.icon,
canEditEnabled = member.canEditEnabled,
canReadEnabled = member.canReadEnabled,
isUser = member.isUser
isUser = member.isUser,
onMemberClicked = onMemberClicked
)
}
@ -403,6 +406,7 @@ private fun SpaceMember(
config: ShareSpaceMemberView.Config.Member,
onCanEditClicked: () -> Unit,
onCanViewClicked: () -> Unit,
onMemberClicked: (ObjectWrapper.SpaceMember) -> Unit,
onRemoveMemberClicked: () -> Unit,
canEditEnabled: Boolean,
canReadEnabled: Boolean
@ -412,6 +416,7 @@ private fun SpaceMember(
modifier = Modifier
.height(72.dp)
.fillMaxWidth()
.noRippleThrottledClickable{ onMemberClicked(member) }
) {
Spacer(modifier = Modifier.width(16.dp))
SpaceMemberIcon(
@ -838,7 +843,8 @@ fun ShareSpaceScreenPreview() {
spaceAccessType = null,
incentiveState = ShareSpaceViewModel.ShareSpaceIncentiveState.VisibleSpaceReaders,
onIncentiveClicked = {},
isLoadingInProgress = false
isLoadingInProgress = false,
onMemberClicked = {}
)
}
@ -860,7 +866,8 @@ private fun SpaceOwnerMemberPreview() {
isCurrentUserOwner = true,
canEditEnabled = true,
canReadEnabled = true,
isUser = true
isUser = true,
onMemberClicked = {}
)
}
@ -882,7 +889,8 @@ private fun SpaceEditorMemberPreview() {
isCurrentUserOwner = true,
canReadEnabled = true,
canEditEnabled = true,
isUser = true
isUser = true,
onMemberClicked = {}
)
}
@ -904,6 +912,7 @@ private fun SpaceMemberLongNamePreview() {
isCurrentUserOwner = true,
canReadEnabled = true,
canEditEnabled = true,
isUser = true
isUser = true,
onMemberClicked = {}
)
}

View file

@ -0,0 +1,230 @@
package com.anytypeio.anytype.core_ui.features.profile
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Text
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.anytypeio.anytype.core_ui.R
import com.anytypeio.anytype.core_ui.common.DefaultPreviews
import com.anytypeio.anytype.core_ui.foundation.Dragger
import com.anytypeio.anytype.core_ui.foundation.noRippleClickable
import com.anytypeio.anytype.core_ui.views.AvatarTitle
import com.anytypeio.anytype.core_ui.views.ButtonSecondary
import com.anytypeio.anytype.core_ui.views.ButtonSize
import com.anytypeio.anytype.core_ui.views.Caption1Regular
import com.anytypeio.anytype.core_ui.views.HeadlineHeading
import com.anytypeio.anytype.core_ui.views.PreviewTitle2Regular
import com.anytypeio.anytype.presentation.profile.ParticipantEvent
import com.anytypeio.anytype.presentation.profile.ParticipantViewModel.UiParticipantScreenState
import com.anytypeio.anytype.presentation.profile.ProfileIconView
import com.bumptech.glide.integration.compose.ExperimentalGlideComposeApi
import com.bumptech.glide.integration.compose.GlideImage
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ParticipantScreen(
uiState: UiParticipantScreenState,
onEvent: (ParticipantEvent) -> Unit
) {
val bottomSheetState = rememberModalBottomSheetState(
skipPartiallyExpanded = true
)
ModalBottomSheet(
modifier = Modifier
.fillMaxWidth()
.padding(start = 12.dp, end = 12.dp, bottom = 32.dp),
dragHandle = {
Column {
Spacer(modifier = Modifier.height(6.dp))
Dragger()
Spacer(modifier = Modifier.height(6.dp))
}
},
scrimColor = colorResource(id = R.color.modal_screen_outside_background),
containerColor = colorResource(id = R.color.background_secondary),
shape = RoundedCornerShape(20.dp),
sheetState = bottomSheetState,
onDismissRequest = {
onEvent(ParticipantEvent.OnDismiss)
},
content = {
if (uiState is UiParticipantScreenState.Data) {
val (spacer, iconSize) = if (uiState.description.isNullOrBlank()) {
68.dp to 184.dp
} else {
48.dp to 128.dp
}
Spacer(modifier = Modifier.height(spacer))
ImageBlock(
modifier = Modifier
.size(iconSize)
.align(Alignment.CenterHorizontally),
name = uiState.name,
icon = uiState.icon,
) { }
Spacer(modifier = Modifier.height(12.dp))
Title(
modifier = Modifier.align(Alignment.CenterHorizontally),
name = uiState.name
)
Spacer(modifier = Modifier.height(4.dp))
if (uiState.identity != null) {
AnyIdentity(
modifier = Modifier
.padding(horizontal = 32.dp)
.align(Alignment.CenterHorizontally),
identity = uiState.identity!!
)
Spacer(modifier = Modifier.height(4.dp))
}
if (!uiState.description.isNullOrBlank()) {
Description(
modifier = Modifier
.padding(horizontal = 32.dp)
.align(Alignment.CenterHorizontally),
description = uiState.description!!
)
}
if (uiState.isOwner) {
ButtonSecondary(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 32.dp)
.padding(top = 16.dp)
.align(Alignment.CenterHorizontally),
text = stringResource(R.string.profile_view_edit_button),
size = ButtonSize.LargeSecondary,
onClick = { onEvent(ParticipantEvent.OnButtonClick) }
)
} else {
Spacer(
modifier = Modifier
.height(64.dp)
.background(color = colorResource(R.color.palette_dark_teal))
)
}
}
},
)
}
@OptIn(ExperimentalGlideComposeApi::class)
@Composable
private fun ImageBlock(
modifier: Modifier,
name: String,
icon: ProfileIconView,
fontSize: TextUnit = 24.sp,
onProfileIconClick: () -> Unit
) {
when (icon) {
is ProfileIconView.Image -> {
GlideImage(
model = icon.url,
contentDescription = "Custom image profile",
contentScale = ContentScale.Crop,
modifier = modifier
.clip(shape = CircleShape)
.noRippleClickable {
onProfileIconClick.invoke()
}
)
}
else -> {
val nameFirstChar = if (name.isEmpty()) {
stringResource(id = R.string.account_default_name)
} else {
name.first().uppercaseChar().toString()
}
Box(
modifier = modifier
.clip(CircleShape)
.background(colorResource(id = R.color.shape_tertiary))
.noRippleClickable {
onProfileIconClick.invoke()
}
) {
Text(
text = nameFirstChar,
style = AvatarTitle.copy(
color = colorResource(id = R.color.glyph_active),
fontSize = fontSize
),
modifier = Modifier.align(Alignment.Center)
)
}
}
}
}
@Composable
private fun Title(modifier: Modifier, name: String) {
Text(
modifier = modifier,
text = name,
style = HeadlineHeading,
color = colorResource(id = R.color.text_primary),
maxLines = 3
)
}
@Composable
private fun AnyIdentity(modifier: Modifier, identity: String) {
Text(
modifier = modifier,
text = identity,
style = Caption1Regular,
color = colorResource(id = R.color.text_secondary),
maxLines = 2,
textAlign = TextAlign.Center
)
}
@Composable
private fun Description(modifier: Modifier, description: String) {
Text(
modifier = modifier,
text = description,
style = PreviewTitle2Regular,
color = colorResource(id = R.color.text_primary),
textAlign = TextAlign.Center
)
}
@DefaultPreviews
@Composable
fun ParticipantScreenPreview() {
ParticipantScreen(
uiState = UiParticipantScreenState.Data(
name = "Ivanov Konstantin",
icon = ProfileIconView.Emoji(""),
description = "some desc",
isOwner = true
),
onEvent = {}
)
}

View file

@ -697,6 +697,15 @@ class AllContentViewModel(
)
}
is OpenObjectNavigation.OpenParticipant -> {
commands.emit(
NavigateToParticipant(
objectId = navigation.target,
space = navigation.space
)
)
}
is OpenObjectNavigation.OpenEditor -> {
commands.emit(
Command.NavigateToEditor(
@ -721,7 +730,7 @@ class AllContentViewModel(
OpenObjectNavigation.NonValidObject -> {
Timber.e("Object id is missing")
}
is OpenObjectNavigation.OpenDataObject -> {
is OpenObjectNavigation.OpenDateObject -> {
commands.emit(
NavigateToEditor(
id = navigation.target,
@ -1007,6 +1016,7 @@ class AllContentViewModel(
data class NavigateToEditor(val id: Id, val space: Id) : Command()
data class NavigateToSetOrCollection(val id: Id, val space: Id) : Command()
data class NavigateToBin(val space: Id) : Command()
data class NavigateToParticipant(val objectId: Id, val space: Id) : Command()
data class NavigateToDateObject(val objectId: Id, val space: Id) : Command()
sealed class SendToast: Command() {
data class Error(val message: String) : SendToast()

View file

@ -8,6 +8,7 @@ sealed class DateObjectCommand {
data class NavigateToEditor(val id: Id, val space: SpaceId) : DateObjectCommand()
data class NavigateToSetOrCollection(val id: Id, val space: SpaceId) : DateObjectCommand()
data class NavigateToDateObject(val objectId: Id, val space: SpaceId) : DateObjectCommand()
data class NavigateToParticipant(val objectId: Id, val space: SpaceId) : DateObjectCommand()
data object TypeSelectionScreen : DateObjectCommand()
data object ExitToSpaceWidgets : DateObjectCommand()
sealed class SendToast : DateObjectCommand() {

View file

@ -631,6 +631,15 @@ class DateObjectViewModel(
)
}
is OpenObjectNavigation.OpenParticipant -> {
effects.emit(
DateObjectCommand.NavigateToParticipant(
objectId = navigation.target,
space = SpaceId(navigation.space)
)
)
}
is OpenObjectNavigation.OpenEditor -> {
effects.emit(
DateObjectCommand.NavigateToEditor(
@ -658,7 +667,7 @@ class DateObjectViewModel(
Timber.e("Object id is missing")
}
is OpenObjectNavigation.OpenDataObject -> {
is OpenObjectNavigation.OpenDateObject -> {
effects.emit(
DateObjectCommand.NavigateToEditor(
id = navigation.target,

View file

@ -1421,6 +1421,7 @@
<string name="multiplayer_collaborate_step_1">1. Tap the Space widget to access settings</string>
<string name="multiplayer_collaborate_step_2">2. Open Share section</string>
<string name="multiplayer_collaborate_step_3">3. Generate an invite link and share it</string>
<string name="profile_view_edit_button">Edit Profile</string>
<plurals name="multiplayer_number_of_space_members">
<item quantity="one">%1$d member</item>

View file

@ -3226,8 +3226,7 @@ class EditorViewModel(
ObjectType.Layout.BASIC,
ObjectType.Layout.NOTE,
ObjectType.Layout.TODO,
ObjectType.Layout.BOOKMARK,
ObjectType.Layout.PARTICIPANT -> {
ObjectType.Layout.BOOKMARK -> {
proceedWithOpeningObject(target = target)
}
in SupportedLayouts.fileLayouts -> {
@ -3260,6 +3259,16 @@ class EditorViewModel(
)
)
}
ObjectType.Layout.PARTICIPANT -> {
navigate(
EventWrapper(
OpenParticipant(
objectId = target,
space = vmParams.space.id
)
)
)
}
else -> {
sendToast("Cannot open object with layout: ${wrapper.layout}")
}
@ -4438,6 +4447,16 @@ class EditorViewModel(
)
)
}
is OpenObjectNavigation.OpenParticipant -> {
navigate(
EventWrapper(
AppNavigation.Command.OpenParticipant(
objectId = navigation.target,
space = navigation.space
)
)
)
}
is OpenObjectNavigation.OpenEditor -> {
navigate(
EventWrapper(
@ -4457,7 +4476,7 @@ class EditorViewModel(
OpenObjectNavigation.NonValidObject -> {
sendToast("Object id is missing")
}
is OpenObjectNavigation.OpenDataObject -> {
is OpenObjectNavigation.OpenDateObject -> {
navigate(
EventWrapper(
OpenDateObject(

View file

@ -1436,7 +1436,7 @@ class HomeScreenViewModel(
OpenObjectNavigation.NonValidObject -> {
sendToast("Object id is missing")
}
is OpenObjectNavigation.OpenDataObject -> {
is OpenObjectNavigation.OpenDateObject -> {
navigate(
destination = Navigation.OpenDateObject(
ctx = navigation.target,
@ -1444,6 +1444,14 @@ class HomeScreenViewModel(
)
)
}
is OpenObjectNavigation.OpenParticipant -> {
navigate(
Navigation.OpenParticipant(
objectId = navigation.target,
space = navigation.space
)
)
}
}
}
@ -2169,6 +2177,7 @@ class HomeScreenViewModel(
data object OpenSpaceSwitcher: Navigation()
data class OpenAllContent(val space: Id) : Navigation()
data class OpenDateObject(val ctx: Id, val space: Id) : Navigation()
data class OpenParticipant(val objectId: Id, val space: Id) : Navigation()
}
class Factory @Inject constructor(
@ -2402,7 +2411,8 @@ sealed class OpenObjectNavigation {
data class UnexpectedLayoutError(val layout: ObjectType.Layout?): OpenObjectNavigation()
data object NonValidObject: OpenObjectNavigation()
data class OpenChat(val target: Id, val space: Id): OpenObjectNavigation()
data class OpenDataObject(val target: Id, val space: Id): OpenObjectNavigation()
data class OpenDateObject(val target: Id, val space: Id): OpenObjectNavigation()
data class OpenParticipant(val target: Id, val space: Id): OpenObjectNavigation()
}
fun ObjectWrapper.Basic.navigation() : OpenObjectNavigation {
@ -2411,8 +2421,7 @@ fun ObjectWrapper.Basic.navigation() : OpenObjectNavigation {
ObjectType.Layout.BASIC,
ObjectType.Layout.NOTE,
ObjectType.Layout.TODO,
ObjectType.Layout.BOOKMARK,
ObjectType.Layout.PARTICIPANT -> {
ObjectType.Layout.BOOKMARK -> {
OpenObjectNavigation.OpenEditor(
target = id,
space = requireNotNull(spaceId)
@ -2452,7 +2461,13 @@ fun ObjectWrapper.Basic.navigation() : OpenObjectNavigation {
)
}
ObjectType.Layout.DATE -> {
OpenObjectNavigation.OpenDataObject(
OpenObjectNavigation.OpenDateObject(
target = id,
space = requireNotNull(spaceId)
)
}
ObjectType.Layout.PARTICIPANT -> {
OpenObjectNavigation.OpenParticipant(
target = id,
space = requireNotNull(spaceId)
)
@ -2471,8 +2486,7 @@ fun ObjectType.Layout.navigation(
ObjectType.Layout.BASIC,
ObjectType.Layout.NOTE,
ObjectType.Layout.TODO,
ObjectType.Layout.BOOKMARK,
ObjectType.Layout.PARTICIPANT -> {
ObjectType.Layout.BOOKMARK -> {
OpenObjectNavigation.OpenEditor(
target = target,
space = space
@ -2504,7 +2518,13 @@ fun ObjectType.Layout.navigation(
)
}
ObjectType.Layout.DATE -> {
OpenObjectNavigation.OpenDataObject(
OpenObjectNavigation.OpenDateObject(
target = target,
space = space
)
}
ObjectType.Layout.PARTICIPANT -> {
OpenObjectNavigation.OpenParticipant(
target = target,
space = space
)

View file

@ -465,6 +465,17 @@ class ShareSpaceViewModel(
}
}
fun onMemberClicked(member: ObjectWrapper.SpaceMember) {
viewModelScope.launch {
commands.emit(
Command.OpenParticipantObject(
objectId = member.id,
space = vmParams.space
)
)
}
}
fun onIncentiveClicked() {
val activeTier = (_activeTier.value as? ActiveTierState.Success) ?: return
val isPossibleToUpgrade = activeTier.tierId.isPossibleToUpgradeNumberOfSpaceMembers()
@ -599,6 +610,7 @@ class ShareSpaceViewModel(
data object Dismiss : Command()
data object ShowMembershipScreen : Command()
data object ShowMembershipUpgradeScreen : Command()
data class OpenParticipantObject(val objectId: Id, val space: SpaceId) : Command()
}
sealed class ShareSpaceIncentiveState {

View file

@ -37,6 +37,11 @@ interface AppNavigation {
space: Id
)
fun openParticipantObject(
objectId: Id,
space: Id
)
fun launchDocument(target: String, space: Id)
fun launchCollections(subscription: Subscription, space: Id)
fun launchObjectSet(target: Id, space: Id)
@ -103,6 +108,11 @@ interface AppNavigation {
val space: Id
) : Command()
data class OpenParticipant(
val objectId: Id,
val space: Id
): Command()
data class LaunchObjectSet(val target: Id, val space: Id) : Command()
object OpenUpdateAppScreen : Command()

View file

@ -0,0 +1,6 @@
package com.anytypeio.anytype.presentation.profile
sealed class ParticipantEvent{
data object OnDismiss: ParticipantEvent()
data object OnButtonClick: ParticipantEvent()
}

View file

@ -0,0 +1,172 @@
package com.anytypeio.anytype.presentation.profile
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.anytypeio.anytype.analytics.base.Analytics
import com.anytypeio.anytype.analytics.base.EventsDictionary
import com.anytypeio.anytype.analytics.base.sendEvent
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.Relations
import com.anytypeio.anytype.core_models.membership.MembershipStatus
import com.anytypeio.anytype.core_models.primitives.SpaceId
import com.anytypeio.anytype.domain.config.ConfigStorage
import com.anytypeio.anytype.domain.library.StoreSearchByIdsParams
import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.multiplayer.UserPermissionProvider
import com.anytypeio.anytype.domain.primitives.FieldParser
import com.anytypeio.anytype.presentation.membership.provider.MembershipProvider
import com.anytypeio.anytype.presentation.search.ObjectSearchConstants
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
class ParticipantViewModel(
private val vmParams: VmParams,
private val analytics: Analytics,
private val urlBuilder: UrlBuilder,
private val membershipProvider: MembershipProvider,
private val subscriptionContainer: StorelessSubscriptionContainer,
private val fieldsParser: FieldParser,
private val userPermissionProvider: UserPermissionProvider,
private val configStorage: ConfigStorage
) : ViewModel() {
val uiState = MutableStateFlow<UiParticipantScreenState>(UiParticipantScreenState.Idle)
val membershipStatusState = MutableStateFlow<MembershipStatus?>(null)
val commands = MutableSharedFlow<Command>(0)
private val permission = MutableStateFlow(userPermissionProvider.get(vmParams.space))
init {
proceedWithObservingPermissions()
viewModelScope.launch {
analytics.sendEvent(
eventName = EventsDictionary.screenSettingsAccount
)
}
viewModelScope.launch {
membershipProvider.status().collect { status ->
membershipStatusState.value = status
}
}
}
fun onStart() {
viewModelScope.launch {
val params = StoreSearchByIdsParams(
space = vmParams.space,
subscription = "$SUB_ID-${vmParams.objectId}",
targets = listOf(vmParams.objectId),
keys = ObjectSearchConstants.spaceMemberKeys
)
subscriptionContainer.subscribe(params)
.collect { participant ->
if (participant.isNotEmpty()) {
val obj = participant.first()
val identityProfileLink = obj.getSingleValue<String>(Relations.IDENTITY_PROFILE_LINK)
uiState.value = UiParticipantScreenState.Data(
name = fieldsParser.getObjectName(obj),
icon = obj.profileIcon(urlBuilder),
isOwner = configStorage.getOrNull()?.profile == identityProfileLink,
identity = obj.getSingleValue<String>(Relations.IDENTITY),
description = if (obj.description?.isBlank() == true) {
null
} else {
obj.description
}
)
}
}
}
}
private fun proceedWithObservingPermissions() {
viewModelScope.launch {
userPermissionProvider
.observe(space = vmParams.space)
.collect { result ->
permission.value = result
}
}
}
fun onStop() {
viewModelScope.launch{
subscriptionContainer.unsubscribe(listOf("$SUB_ID-${vmParams.objectId}"))
}
}
fun onEvent(event: ParticipantEvent) {
when (event) {
ParticipantEvent.OnDismiss -> {
viewModelScope.launch {
commands.emit(Command.Dismiss)
}
}
ParticipantEvent.OnButtonClick -> {
viewModelScope.launch {
commands.emit(Command.OpenSettingsProfile)
}
}
}
}
sealed class UiParticipantScreenState {
data object Idle : UiParticipantScreenState()
class Data(
val name: String,
val icon: ProfileIconView,
val description: String? = null,
val identity: String? = null,
val isOwner: Boolean
) : UiParticipantScreenState()
}
data class VmParams(
val objectId: Id,
val space: SpaceId
)
sealed class Command {
sealed class Toast : Command() {
data class Error(val msg: String) : Toast()
}
data object Dismiss : Command()
data object OpenSettingsProfile : Command()
}
companion object {
const val SUB_ID = "Participant-subscription"
}
class Factory @Inject constructor(
private val vmParams: VmParams,
private val analytics: Analytics,
private val urlBuilder: UrlBuilder,
private val membershipProvider: MembershipProvider,
private val subscriptionContainer: StorelessSubscriptionContainer,
private val fieldsParser: FieldParser,
private val userPermissionProvider: UserPermissionProvider,
private val configStorage: ConfigStorage
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return ParticipantViewModel(
vmParams = vmParams,
analytics = analytics,
urlBuilder = urlBuilder,
membershipProvider = membershipProvider,
subscriptionContainer = subscriptionContainer,
fieldsParser = fieldsParser,
userPermissionProvider = userPermissionProvider,
configStorage = configStorage
) as T
}
}
}

View file

@ -1,8 +0,0 @@
package com.anytypeio.anytype.presentation.profile
import com.anytypeio.anytype.core_models.Url
data class ProfileView(
val name: String,
val avatar: Url? = null,
)

View file

@ -1276,7 +1276,9 @@ object ObjectSearchConstants {
Relations.SPACE_ID,
Relations.TARGET_SPACE_ID,
Relations.IDENTITY,
Relations.IDENTITY_PROFILE_LINK,
Relations.NAME,
Relations.DESCRIPTION,
Relations.ICON_IMAGE,
Relations.PARTICIPANT_STATUS,
Relations.PARTICIPANT_PERMISSIONS,
@ -1301,29 +1303,6 @@ object ObjectSearchConstants {
)
//region SPACE VIEW
fun getSpaceViewSearchParams(
techSpaceId: Id,
subscription: String,
targetSpaceId: Id
): StoreSearchParams {
return StoreSearchParams(
space = SpaceId(techSpaceId),
subscription = subscription,
keys = spaceViewKeys,
limit = 1,
filters = buildList {
add(
DVFilter(
relation = Relations.TARGET_SPACE_ID,
value = targetSpaceId,
condition = DVFilterCondition.EQUAL
)
)
}
)
}
fun getSpaceMembersSearchParams(
subscription: String,
space: SpaceId,

View file

@ -169,8 +169,7 @@ open class ObjectSearchViewModel(
ObjectType.Layout.NOTE,
ObjectType.Layout.FILE,
ObjectType.Layout.IMAGE,
ObjectType.Layout.BOOKMARK,
ObjectType.Layout.PARTICIPANT -> {
ObjectType.Layout.BOOKMARK -> {
val obj = objects
.value
.getOrNull()
@ -184,6 +183,20 @@ open class ObjectSearchViewModel(
)
)
}
ObjectType.Layout.PARTICIPANT -> {
val obj = objects
.value
.getOrNull()
?.find { obj -> obj.id == view.id }
navigate(
EventWrapper(
AppNavigation.Command.OpenParticipant(
objectId = target,
space = requireNotNull(obj?.spaceId)
)
)
)
}
ObjectType.Layout.PROFILE -> {
val obj = objects
.value

View file

@ -1468,11 +1468,20 @@ class ObjectSetViewModel(
ObjectType.Layout.VIDEO,
ObjectType.Layout.AUDIO,
ObjectType.Layout.PDF,
ObjectType.Layout.BOOKMARK,
ObjectType.Layout.PARTICIPANT -> proceedWithOpeningObject(
ObjectType.Layout.BOOKMARK -> proceedWithOpeningObject(
target = target,
space = space
)
ObjectType.Layout.PARTICIPANT -> {
navigate(
EventWrapper(
AppNavigation.Command.OpenParticipant(
objectId = target,
space = space
)
)
)
}
ObjectType.Layout.PROFILE -> proceedWithOpeningObject(
target = identityProfileLink ?: target,
space = space

View file

@ -37,6 +37,7 @@ import com.anytypeio.anytype.presentation.navigation.NavigationViewModel
import com.anytypeio.anytype.presentation.spaces.SpaceGradientProvider
import com.anytypeio.anytype.presentation.spaces.SpaceIconView
import com.anytypeio.anytype.presentation.spaces.spaceIcon
import com.anytypeio.anytype.presentation.vault.VaultViewModel.Navigation.*
import javax.inject.Inject
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow
@ -281,7 +282,7 @@ class VaultViewModel(
when(navigation) {
is OpenObjectNavigation.OpenDataView -> {
navigate(
Navigation.OpenSet(
OpenSet(
ctx = navigation.target,
space = navigation.space,
view = null
@ -290,7 +291,7 @@ class VaultViewModel(
}
is OpenObjectNavigation.OpenEditor -> {
navigate(
Navigation.OpenObject(
OpenObject(
ctx = navigation.target,
space = navigation.space
)
@ -298,7 +299,7 @@ class VaultViewModel(
}
is OpenObjectNavigation.OpenChat -> {
navigate(
Navigation.OpenChat(
OpenChat(
ctx = navigation.target,
space = navigation.space
)
@ -310,9 +311,17 @@ class VaultViewModel(
OpenObjectNavigation.NonValidObject -> {
sendToast("Object id is missing")
}
is OpenObjectNavigation.OpenDataObject -> {
is OpenObjectNavigation.OpenDateObject -> {
navigate(
Navigation.OpenDateObject(
OpenDateObject(
ctx = navigation.target,
space = navigation.space
)
)
}
is OpenObjectNavigation.OpenParticipant -> {
navigate(
OpenParticipant(
ctx = navigation.target,
space = navigation.space
)
@ -385,6 +394,7 @@ class VaultViewModel(
data class OpenObject(val ctx: Id, val space: Id) : Navigation()
data class OpenSet(val ctx: Id, val space: Id, val view: Id?) : Navigation()
data class OpenDateObject(val ctx: Id, val space: Id) : Navigation()
data class OpenParticipant(val ctx: Id, val space: Id) : Navigation()
}
companion object {

View file

@ -435,7 +435,6 @@ class CollectionViewModel(
val target = view.id
when (view.layout) {
ObjectType.Layout.PROFILE,
ObjectType.Layout.PARTICIPANT,
ObjectType.Layout.BASIC,
ObjectType.Layout.TODO,
ObjectType.Layout.NOTE,
@ -457,6 +456,14 @@ class CollectionViewModel(
)
)
}
ObjectType.Layout.PARTICIPANT -> {
commands.emit(
Command.OpenParticipant(
target = target,
space = view.space
)
)
}
else -> {
Timber.e("Unexpected layout: ${view.layout}")
}
@ -934,7 +941,7 @@ class CollectionViewModel(
OpenObjectNavigation.NonValidObject -> {
toasts.emit("Object id is missing")
}
is OpenObjectNavigation.OpenDataObject -> {
is OpenObjectNavigation.OpenDateObject -> {
commands.emit(
OpenDateObject(
target = navigation.target,
@ -942,6 +949,14 @@ class CollectionViewModel(
)
)
}
is OpenObjectNavigation.OpenParticipant -> {
commands.emit(
OpenParticipant(
target = navigation.target,
space = navigation.space
)
)
}
}
}
@ -1046,6 +1061,7 @@ class CollectionViewModel(
data class LaunchObjectSet(val target: Id, val space: Id) : Command()
data class OpenChat(val target: Id, val space: Id) : Command()
data class OpenDateObject(val target: Id, val space: Id) : Command()
data class OpenParticipant(val target: Id, val space: Id) : Command()
data class OpenShareScreen(val space: SpaceId) : Command()
data object ToDesktop : Command()

View file

@ -59,7 +59,7 @@ include ':app',
':libs',
':feature-date'
include ':ui-settings'
include ':feature-ui-settings'
include ':crash-reporting'
include ':localization'
include ':payments'