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:
parent
28bc323b73
commit
9ded869c33
59 changed files with 950 additions and 69 deletions
|
@ -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')
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -110,6 +110,9 @@ class RemoteFilesManageFragment : BaseBottomSheetComposeFragment() {
|
|||
is CollectionViewModel.Command.OpenShareScreen -> {
|
||||
// Do nothing
|
||||
}
|
||||
is CollectionViewModel.Command.OpenParticipant -> {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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 = {}
|
||||
)
|
||||
}
|
|
@ -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 = {}
|
||||
)
|
||||
}
|
|
@ -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()
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
package com.anytypeio.anytype.presentation.profile
|
||||
|
||||
sealed class ParticipantEvent{
|
||||
data object OnDismiss: ParticipantEvent()
|
||||
data object OnButtonClick: ParticipantEvent()
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
)
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -59,7 +59,7 @@ include ':app',
|
|||
':libs',
|
||||
':feature-date'
|
||||
|
||||
include ':ui-settings'
|
||||
include ':feature-ui-settings'
|
||||
include ':crash-reporting'
|
||||
include ':localization'
|
||||
include ':payments'
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue