mirror of
https://github.com/anyproto/anytype-kotlin.git
synced 2025-06-07 21:37:02 +09:00
DROID-3013 Tech | Remove old library screen (#1789)
This commit is contained in:
parent
6d37e175d7
commit
029daff72c
41 changed files with 22 additions and 3534 deletions
|
@ -53,7 +53,6 @@ import com.anytypeio.anytype.di.feature.cover.UnsplashModule
|
|||
import com.anytypeio.anytype.di.feature.discussions.DaggerDiscussionComponent
|
||||
import com.anytypeio.anytype.di.feature.gallery.DaggerGalleryInstallationComponent
|
||||
import com.anytypeio.anytype.di.feature.home.DaggerHomeScreenComponent
|
||||
import com.anytypeio.anytype.di.feature.library.DaggerLibraryComponent
|
||||
import com.anytypeio.anytype.di.feature.membership.DaggerMembershipComponent
|
||||
import com.anytypeio.anytype.di.feature.membership.DaggerMembershipUpdateComponent
|
||||
import com.anytypeio.anytype.di.feature.multiplayer.DaggerRequestJoinSpaceComponent
|
||||
|
@ -106,7 +105,6 @@ import com.anytypeio.anytype.gallery_experience.viewmodel.GalleryInstallationVie
|
|||
import com.anytypeio.anytype.presentation.common.BaseViewModel
|
||||
import com.anytypeio.anytype.presentation.editor.EditorViewModel
|
||||
import com.anytypeio.anytype.presentation.history.VersionHistoryViewModel
|
||||
import com.anytypeio.anytype.presentation.library.LibraryViewModel
|
||||
import com.anytypeio.anytype.presentation.multiplayer.RequestJoinSpaceViewModel
|
||||
import com.anytypeio.anytype.presentation.multiplayer.ShareSpaceViewModel
|
||||
import com.anytypeio.anytype.presentation.multiplayer.SpaceJoinRequestViewModel
|
||||
|
@ -738,14 +736,6 @@ class ComponentManager(
|
|||
.create(findComponentDependencies())
|
||||
}
|
||||
|
||||
val libraryComponent = ComponentWithParams { (ctx, params) : Pair<Context, LibraryViewModel.Params> ->
|
||||
DaggerLibraryComponent.builder()
|
||||
.withContext(ctx)
|
||||
.withParams(params)
|
||||
.withDependencies(findComponentDependencies())
|
||||
.build()
|
||||
}
|
||||
|
||||
val backLinkOrAddToObjectComponent = ComponentWithParams { ctx: Id ->
|
||||
DaggerBacklinkOrAddToObjectComponent.builder()
|
||||
.withContext(ctx)
|
||||
|
|
|
@ -1,218 +0,0 @@
|
|||
package com.anytypeio.anytype.di.feature.library
|
||||
|
||||
import android.content.Context
|
||||
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.launch.GetDefaultObjectType
|
||||
import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.domain.`object`.SetObjectDetails
|
||||
import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
|
||||
import com.anytypeio.anytype.domain.page.CreateObject
|
||||
import com.anytypeio.anytype.domain.search.SubscriptionEventChannel
|
||||
import com.anytypeio.anytype.domain.templates.GetTemplates
|
||||
import com.anytypeio.anytype.domain.workspace.AddObjectToWorkspace
|
||||
import com.anytypeio.anytype.domain.workspace.RemoveObjectsFromWorkspace
|
||||
import com.anytypeio.anytype.domain.workspace.SpaceManager
|
||||
import com.anytypeio.anytype.presentation.analytics.AnalyticSpaceHelperDelegate
|
||||
import com.anytypeio.anytype.presentation.library.LibraryListDelegate
|
||||
import com.anytypeio.anytype.presentation.library.LibraryResourceManager
|
||||
import com.anytypeio.anytype.presentation.library.LibraryViewModel
|
||||
import com.anytypeio.anytype.presentation.library.delegates.LibraryRelationsDelegate
|
||||
import com.anytypeio.anytype.presentation.library.delegates.LibraryTypesDelegate
|
||||
import com.anytypeio.anytype.presentation.library.delegates.MyRelationsDelegate
|
||||
import com.anytypeio.anytype.presentation.library.delegates.MyTypesDelegate
|
||||
import com.anytypeio.anytype.ui.library.LibraryFragment
|
||||
import dagger.Binds
|
||||
import dagger.BindsInstance
|
||||
import dagger.Component
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
||||
@Component(
|
||||
dependencies = [LibraryDependencies::class],
|
||||
modules = [
|
||||
LibraryModule::class,
|
||||
LibraryModule.Declarations::class
|
||||
]
|
||||
)
|
||||
@PerScreen
|
||||
interface LibraryComponent {
|
||||
|
||||
@Component.Builder
|
||||
interface Builder {
|
||||
fun withDependencies(dependencies: LibraryDependencies): Builder
|
||||
|
||||
@BindsInstance
|
||||
fun withContext(context: Context): Builder
|
||||
|
||||
@BindsInstance
|
||||
fun withParams(params: LibraryViewModel.Params): Builder
|
||||
|
||||
fun build(): LibraryComponent
|
||||
}
|
||||
|
||||
fun inject(fragment: LibraryFragment)
|
||||
}
|
||||
|
||||
@Module
|
||||
object LibraryModule {
|
||||
|
||||
@PerScreen
|
||||
@Provides
|
||||
@JvmStatic
|
||||
fun provideMyTypesDelegate(
|
||||
container: StorelessSubscriptionContainer,
|
||||
spaceManager: SpaceManager,
|
||||
urlBuilder: UrlBuilder,
|
||||
dispatchers: AppCoroutineDispatchers
|
||||
): LibraryListDelegate {
|
||||
return MyTypesDelegate(
|
||||
container = container,
|
||||
spaceManager = spaceManager,
|
||||
urlBuilder = urlBuilder,
|
||||
dispatchers = dispatchers
|
||||
)
|
||||
}
|
||||
|
||||
@PerScreen
|
||||
@Provides
|
||||
@JvmStatic
|
||||
fun provideLibTypesDelegate(
|
||||
container: StorelessSubscriptionContainer,
|
||||
urlBuilder: UrlBuilder,
|
||||
dispatchers: AppCoroutineDispatchers
|
||||
): LibraryListDelegate {
|
||||
return LibraryTypesDelegate(container, urlBuilder, dispatchers)
|
||||
}
|
||||
|
||||
@PerScreen
|
||||
@Provides
|
||||
@JvmStatic
|
||||
fun provideMyRelationsDelegate(
|
||||
container: StorelessSubscriptionContainer,
|
||||
spaceManager: SpaceManager,
|
||||
urlBuilder: UrlBuilder,
|
||||
dispatchers: AppCoroutineDispatchers
|
||||
): LibraryListDelegate {
|
||||
return MyRelationsDelegate(
|
||||
container = container,
|
||||
spaceManager = spaceManager,
|
||||
urlBuilder = urlBuilder,
|
||||
dispatchers = dispatchers
|
||||
)
|
||||
}
|
||||
|
||||
@PerScreen
|
||||
@Provides
|
||||
@JvmStatic
|
||||
fun provideLibRelationsDelegate(
|
||||
container: StorelessSubscriptionContainer,
|
||||
urlBuilder: UrlBuilder,
|
||||
dispatchers: AppCoroutineDispatchers
|
||||
): LibraryListDelegate {
|
||||
return LibraryRelationsDelegate(container, urlBuilder, dispatchers)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@PerScreen
|
||||
@JvmStatic
|
||||
fun addObjectToWorkspace(
|
||||
repo: BlockRepository,
|
||||
dispatchers: AppCoroutineDispatchers
|
||||
): AddObjectToWorkspace = AddObjectToWorkspace(
|
||||
repo = repo,
|
||||
dispatchers = dispatchers
|
||||
)
|
||||
|
||||
@Provides
|
||||
@PerScreen
|
||||
@JvmStatic
|
||||
fun removeObjectFromWorkspace(
|
||||
repo: BlockRepository,
|
||||
dispatchers: AppCoroutineDispatchers
|
||||
): RemoveObjectsFromWorkspace = RemoveObjectsFromWorkspace(repo, dispatchers)
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@PerScreen
|
||||
fun getCreateObject(
|
||||
repo: BlockRepository,
|
||||
getDefaultObjectType: GetDefaultObjectType,
|
||||
dispatchers: AppCoroutineDispatchers,
|
||||
): CreateObject = CreateObject(
|
||||
repo = repo,
|
||||
getDefaultObjectType = getDefaultObjectType,
|
||||
dispatchers = dispatchers
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@PerScreen
|
||||
fun provideGetDefaultPageType(
|
||||
userSettingsRepository: UserSettingsRepository,
|
||||
blockRepository: BlockRepository,
|
||||
dispatchers: AppCoroutineDispatchers
|
||||
): GetDefaultObjectType = GetDefaultObjectType(
|
||||
userSettingsRepository = userSettingsRepository,
|
||||
blockRepository = blockRepository,
|
||||
dispatchers = dispatchers
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@PerScreen
|
||||
fun provideGetTemplates(
|
||||
repo: BlockRepository,
|
||||
spaceManager: SpaceManager,
|
||||
dispatchers: AppCoroutineDispatchers
|
||||
): GetTemplates = GetTemplates(
|
||||
repo = repo,
|
||||
spaceManager = spaceManager,
|
||||
dispatchers = dispatchers
|
||||
)
|
||||
|
||||
@Provides
|
||||
@PerScreen
|
||||
@JvmStatic
|
||||
fun objectSetDetails(
|
||||
repo: BlockRepository,
|
||||
dispatchers: AppCoroutineDispatchers
|
||||
): SetObjectDetails = SetObjectDetails(repo = repo, dispatchers = dispatchers)
|
||||
|
||||
@Module
|
||||
interface Declarations {
|
||||
|
||||
@PerScreen
|
||||
@Binds
|
||||
fun bindViewModelFactory(factory: LibraryViewModel.Factory): ViewModelProvider.Factory
|
||||
|
||||
@PerScreen
|
||||
@Binds
|
||||
fun bindResourceManager(manager: LibraryResourceManager.Impl): LibraryResourceManager
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface LibraryDependencies : ComponentDependencies {
|
||||
fun blockRepository(): BlockRepository
|
||||
fun urlBuilder(): UrlBuilder
|
||||
fun channel(): SubscriptionEventChannel
|
||||
fun dispatchers(): AppCoroutineDispatchers
|
||||
fun userSettingsRepository(): UserSettingsRepository
|
||||
fun analytics(): Analytics
|
||||
fun spaceManager(): SpaceManager
|
||||
fun config(): ConfigStorage
|
||||
fun logger(): Logger
|
||||
fun container(): StorelessSubscriptionContainer
|
||||
fun storeOfTypes (): StoreOfObjectTypes
|
||||
fun analyticSpaceHelperDelegate(): AnalyticSpaceHelperDelegate
|
||||
}
|
|
@ -23,7 +23,6 @@ import com.anytypeio.anytype.di.feature.auth.DeletedAccountDependencies
|
|||
import com.anytypeio.anytype.di.feature.discussions.DiscussionComponentDependencies
|
||||
import com.anytypeio.anytype.di.feature.gallery.GalleryInstallationComponentDependencies
|
||||
import com.anytypeio.anytype.di.feature.home.HomeScreenDependencies
|
||||
import com.anytypeio.anytype.di.feature.library.LibraryDependencies
|
||||
import com.anytypeio.anytype.di.feature.membership.MembershipComponentDependencies
|
||||
import com.anytypeio.anytype.di.feature.membership.MembershipUpdateComponentDependencies
|
||||
import com.anytypeio.anytype.di.feature.multiplayer.RequestJoinSpaceDependencies
|
||||
|
@ -90,7 +89,6 @@ import javax.inject.Singleton
|
|||
)
|
||||
interface MainComponent :
|
||||
AppearanceDependencies,
|
||||
LibraryDependencies,
|
||||
HomeScreenDependencies,
|
||||
CollectionDependencies,
|
||||
CreateObjectTypeDependencies,
|
||||
|
@ -164,11 +162,6 @@ abstract class ComponentDependenciesModule {
|
|||
@ComponentDependenciesKey(AppearanceDependencies::class)
|
||||
abstract fun provideAppearanceDependencies(component: MainComponent): ComponentDependencies
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ComponentDependenciesKey(LibraryDependencies::class)
|
||||
abstract fun provideLibraryDependencies(component: MainComponent): ComponentDependencies
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ComponentDependenciesKey(HomeScreenDependencies::class)
|
||||
|
|
|
@ -13,7 +13,6 @@ import com.anytypeio.anytype.ui.allcontent.AllContentFragment
|
|||
import com.anytypeio.anytype.ui.auth.account.DeletedAccountFragment
|
||||
import com.anytypeio.anytype.ui.editor.EditorFragment
|
||||
import com.anytypeio.anytype.ui.editor.EditorModalFragment
|
||||
import com.anytypeio.anytype.ui.library.LibraryFragment
|
||||
import com.anytypeio.anytype.ui.relations.RelationCreateFromScratchForObjectFragment
|
||||
import com.anytypeio.anytype.ui.relations.RelationEditFragment
|
||||
import com.anytypeio.anytype.ui.search.GlobalSearchFragment
|
||||
|
@ -245,13 +244,6 @@ class Navigator : AppNavigation {
|
|||
})
|
||||
}
|
||||
|
||||
override fun openLibrary(space: Id) {
|
||||
navController?.navigate(
|
||||
R.id.libraryFragment,
|
||||
LibraryFragment.args(space)
|
||||
)
|
||||
}
|
||||
|
||||
override fun openRemoteFilesManageScreen(subscription: Id, space: Id) {
|
||||
navController?.navigate(
|
||||
resId = R.id.remoteStorageFragment,
|
||||
|
|
|
@ -27,11 +27,11 @@ import com.anytypeio.anytype.core_utils.ext.toast
|
|||
import com.anytypeio.anytype.core_utils.insets.EDGE_TO_EDGE_MIN_SDK
|
||||
import com.anytypeio.anytype.core_utils.ui.BaseComposeFragment
|
||||
import com.anytypeio.anytype.di.common.componentManager
|
||||
import com.anytypeio.anytype.feature_allcontent.models.AllContentTab
|
||||
import com.anytypeio.anytype.feature_allcontent.presentation.AllContentViewModel
|
||||
import com.anytypeio.anytype.feature_allcontent.presentation.AllContentViewModelFactory
|
||||
import com.anytypeio.anytype.feature_allcontent.ui.AllContentNavigation.ALL_CONTENT_MAIN
|
||||
import com.anytypeio.anytype.feature_allcontent.ui.AllContentWrapperScreen
|
||||
import com.anytypeio.anytype.presentation.library.LibraryViewModel
|
||||
import com.anytypeio.anytype.presentation.objects.ObjectIcon
|
||||
import com.anytypeio.anytype.presentation.widgets.collection.Subscription
|
||||
import com.anytypeio.anytype.ui.base.navigation
|
||||
|
@ -65,7 +65,7 @@ class AllContentFragment : BaseComposeFragment(), ObjectTypeSelectionListener {
|
|||
setFragmentResultListener(REQUEST_KEY_UNINSTALL_TYPE) { _, bundle ->
|
||||
val id = requireNotNull(bundle.getString(REQUEST_UNINSTALL_TYPE_ARG_ID))
|
||||
val name = requireNotNull(bundle.getString(REQUEST_UNINSTALL_TYPE_ARG_NAME))
|
||||
vm.uninstallObject(id, LibraryViewModel.LibraryItem.TYPE, name)
|
||||
vm.uninstallObject(id, AllContentTab.TYPES, name)
|
||||
}
|
||||
setFragmentResultListener(REQUEST_KEY_MODIFY_TYPE) { _, bundle ->
|
||||
val id = requireNotNull(bundle.getString(REQUEST_UNINSTALL_TYPE_ARG_ID))
|
||||
|
@ -76,7 +76,7 @@ class AllContentFragment : BaseComposeFragment(), ObjectTypeSelectionListener {
|
|||
setFragmentResultListener(REQUEST_KEY_UNINSTALL_RELATION) { _, bundle ->
|
||||
val id = requireNotNull(bundle.getString(REQUEST_UNINSTALL_RELATION_ARG_ID))
|
||||
val name = requireNotNull(bundle.getString(REQUEST_UNINSTALL_RELATION_ARG_NAME))
|
||||
vm.uninstallObject(id, LibraryViewModel.LibraryItem.RELATION, name)
|
||||
vm.uninstallObject(id, AllContentTab.RELATIONS, name)
|
||||
}
|
||||
setFragmentResultListener(REQUEST_KEY_MODIFY_RELATION) { _, bundle ->
|
||||
val id = requireNotNull(bundle.getString(REQUEST_UNINSTALL_RELATION_ARG_ID))
|
||||
|
|
|
@ -54,7 +54,6 @@ class NavigationRouter(
|
|||
is AppNavigation.Command.OpenTemplates -> navigation.openTemplatesModal(
|
||||
typeId = command.typeId
|
||||
)
|
||||
is AppNavigation.Command.OpenLibrary -> navigation.openLibrary(command.space)
|
||||
is AppNavigation.Command.MigrationErrorScreen -> navigation.migrationErrorScreen()
|
||||
else -> Timber.d("Nav command ignored: $command")
|
||||
}
|
||||
|
|
|
@ -46,7 +46,6 @@ import com.anytypeio.anytype.core_ui.foundation.components.BottomNavigationMenu
|
|||
import com.anytypeio.anytype.core_ui.foundation.noRippleClickable
|
||||
import com.anytypeio.anytype.core_ui.views.UXBody
|
||||
import com.anytypeio.anytype.presentation.home.InteractionMode
|
||||
import com.anytypeio.anytype.presentation.profile.ProfileIconView
|
||||
import com.anytypeio.anytype.presentation.widgets.DropDownMenuAction
|
||||
import com.anytypeio.anytype.presentation.widgets.FromIndex
|
||||
import com.anytypeio.anytype.presentation.widgets.ToIndex
|
||||
|
@ -60,7 +59,6 @@ import com.anytypeio.anytype.ui.widgets.types.AllContentWidgetCard
|
|||
import com.anytypeio.anytype.ui.widgets.types.BinWidgetCard
|
||||
import com.anytypeio.anytype.ui.widgets.types.DataViewListWidgetCard
|
||||
import com.anytypeio.anytype.ui.widgets.types.GalleryWidgetCard
|
||||
import com.anytypeio.anytype.ui.widgets.types.LibraryWidgetCard
|
||||
import com.anytypeio.anytype.ui.widgets.types.LinkWidgetCard
|
||||
import com.anytypeio.anytype.ui.widgets.types.ListWidgetCard
|
||||
import com.anytypeio.anytype.ui.widgets.types.SpaceChatWidgetCard
|
||||
|
@ -87,7 +85,6 @@ fun HomeScreen(
|
|||
onToggleExpandedWidgetState: (WidgetId) -> Unit,
|
||||
onExitEditMode: () -> Unit,
|
||||
onSearchClicked: () -> Unit,
|
||||
onLibraryClicked: () -> Unit,
|
||||
onCreateNewObjectClicked: () -> Unit,
|
||||
onCreateNewObjectLongClicked: () -> Unit,
|
||||
onBackClicked: () -> Unit,
|
||||
|
@ -113,7 +110,6 @@ fun HomeScreen(
|
|||
mode = mode,
|
||||
onChangeWidgetView = onChangeWidgetView,
|
||||
onEditWidgets = onEditWidgets,
|
||||
onLibraryClicked = onLibraryClicked,
|
||||
onSpaceWidgetClicked = onSpaceWidgetClicked,
|
||||
onMove = onMove,
|
||||
onObjectCheckboxClicked = onObjectCheckboxClicked,
|
||||
|
@ -185,7 +181,6 @@ private fun WidgetList(
|
|||
mode: InteractionMode,
|
||||
onChangeWidgetView: (WidgetId, ViewId) -> Unit,
|
||||
onEditWidgets: () -> Unit,
|
||||
onLibraryClicked: () -> Unit,
|
||||
onMove: (List<WidgetView>, FromIndex, ToIndex) -> Unit,
|
||||
onObjectCheckboxClicked: (Id, Boolean) -> Unit,
|
||||
onSpaceWidgetClicked: () -> Unit,
|
||||
|
@ -429,15 +424,6 @@ private fun WidgetList(
|
|||
onWidgetClicked = { onBundledWidgetHeaderClicked(item.id) }
|
||||
)
|
||||
}
|
||||
is WidgetView.Library -> {
|
||||
LibraryWidgetCard(
|
||||
onDropDownMenuAction = { action ->
|
||||
onWidgetMenuAction(item.id, action)
|
||||
},
|
||||
onClick = onLibraryClicked,
|
||||
mode = mode
|
||||
)
|
||||
}
|
||||
is WidgetView.Action.EditWidgets -> {
|
||||
Box(
|
||||
Modifier
|
||||
|
|
|
@ -35,7 +35,6 @@ import com.anytypeio.anytype.presentation.home.HomeScreenViewModel.Navigation
|
|||
import com.anytypeio.anytype.presentation.widgets.DropDownMenuAction
|
||||
import com.anytypeio.anytype.ui.base.navigation
|
||||
import com.anytypeio.anytype.ui.gallery.GalleryInstallationFragment
|
||||
import com.anytypeio.anytype.ui.library.LibraryFragment
|
||||
import com.anytypeio.anytype.ui.multiplayer.RequestJoinSpaceFragment
|
||||
import com.anytypeio.anytype.ui.multiplayer.ShareSpaceFragment
|
||||
import com.anytypeio.anytype.ui.objects.creation.ObjectTypeSelectionFragment
|
||||
|
@ -102,9 +101,6 @@ class HomeScreenFragment : BaseComposeFragment(),
|
|||
onChangeWidgetView = vm::onChangeCurrentWidgetView,
|
||||
onToggleExpandedWidgetState = vm::onToggleCollapsedWidgetState,
|
||||
onSearchClicked = vm::onSearchIconClicked,
|
||||
onLibraryClicked = {
|
||||
vm.onLibraryClicked()
|
||||
},
|
||||
onCreateNewObjectClicked = throttledClick(
|
||||
onClick = { vm.onCreateNewObjectClicked() }
|
||||
),
|
||||
|
@ -343,14 +339,6 @@ class HomeScreenFragment : BaseComposeFragment(),
|
|||
space = destination.space
|
||||
)
|
||||
}
|
||||
is Navigation.OpenLibrary -> runCatching {
|
||||
findNavController().navigate(
|
||||
R.id.libraryFragment,
|
||||
args = LibraryFragment.args(destination.space)
|
||||
)
|
||||
}.onFailure { e ->
|
||||
Timber.e(e, "Error while opening space library from widgets")
|
||||
}
|
||||
is Navigation.OpenAllContent -> {
|
||||
runCatching {
|
||||
navigation().openAllContent(space = destination.space)
|
||||
|
|
|
@ -1,243 +0,0 @@
|
|||
package com.anytypeio.anytype.ui.library
|
||||
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.compose.ui.platform.ViewCompositionStrategy
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.setFragmentResultListener
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.anytypeio.anytype.BuildConfig
|
||||
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.extensions.simpleIcon
|
||||
import com.anytypeio.anytype.core_utils.ext.arg
|
||||
import com.anytypeio.anytype.core_utils.ext.safeNavigate
|
||||
import com.anytypeio.anytype.core_utils.ext.subscribe
|
||||
import com.anytypeio.anytype.core_utils.ext.toast
|
||||
import com.anytypeio.anytype.core_utils.insets.EDGE_TO_EDGE_MIN_SDK
|
||||
import com.anytypeio.anytype.core_utils.ui.BaseComposeFragment
|
||||
import com.anytypeio.anytype.di.common.componentManager
|
||||
import com.anytypeio.anytype.presentation.library.LibraryViewModel
|
||||
import com.anytypeio.anytype.presentation.objects.ObjectIcon
|
||||
import com.anytypeio.anytype.ui.editor.EditorFragment
|
||||
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
|
||||
import com.anytypeio.anytype.ui.relations.REQUEST_UNINSTALL_RELATION_ARG_NAME
|
||||
import com.anytypeio.anytype.ui.relations.RelationCreateFromScratchForObjectFragment
|
||||
import com.anytypeio.anytype.ui.relations.RelationEditFragment
|
||||
import com.anytypeio.anytype.ui.sets.ObjectSetFragment
|
||||
import com.anytypeio.anytype.ui.settings.typography
|
||||
import com.anytypeio.anytype.ui.types.create.CreateObjectTypeFragment
|
||||
import com.anytypeio.anytype.ui.types.create.REQUEST_CREATE_OBJECT
|
||||
import com.anytypeio.anytype.ui.types.edit.REQUEST_KEY_MODIFY_TYPE
|
||||
import com.anytypeio.anytype.ui.types.edit.REQUEST_KEY_UNINSTALL_TYPE
|
||||
import com.anytypeio.anytype.ui.types.edit.REQUEST_UNINSTALL_TYPE_ARG_ICON
|
||||
import com.anytypeio.anytype.ui.types.edit.REQUEST_UNINSTALL_TYPE_ARG_ID
|
||||
import com.anytypeio.anytype.ui.types.edit.REQUEST_UNINSTALL_TYPE_ARG_NAME
|
||||
import com.anytypeio.anytype.ui.types.edit.TypeEditFragment
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import timber.log.Timber
|
||||
|
||||
@Deprecated("legacy")
|
||||
class LibraryFragment : BaseComposeFragment() {
|
||||
|
||||
@Inject
|
||||
lateinit var factory: LibraryViewModel.Factory
|
||||
|
||||
private val vm by viewModels<LibraryViewModel> { factory }
|
||||
|
||||
private val space get() = arg<Id>(ARG_SPACE_ID_KEY)
|
||||
|
||||
@OptIn(ExperimentalAnimationApi::class)
|
||||
@FlowPreview
|
||||
@ExperimentalMaterialApi
|
||||
@ExperimentalComposeUiApi
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
|
||||
): View {
|
||||
return ComposeView(requireContext()).apply {
|
||||
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
|
||||
setContent {
|
||||
MaterialTheme(typography = typography) {
|
||||
LibraryScreen(
|
||||
configuration = LibraryConfiguration(),
|
||||
viewModel = vm,
|
||||
onBackPressed = {
|
||||
findNavController().popBackStack()
|
||||
},
|
||||
onCreateObjectLongClicked = {},
|
||||
onBackLongPressed = {
|
||||
runCatching {
|
||||
findNavController().navigate(R.id.actionExitToSpaceWidgets)
|
||||
}.onFailure {
|
||||
Timber.e(it, "Error while opening space switcher from library")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
subscribe(vm.toasts) { toast(it) }
|
||||
subscribe(vm.navigation) {
|
||||
when (it) {
|
||||
is LibraryViewModel.Navigation.OpenTypeCreation -> {
|
||||
findNavController().safeNavigate(
|
||||
R.id.libraryFragment,
|
||||
R.id.openTypeCreationScreen,
|
||||
CreateObjectTypeFragment.args(it.name)
|
||||
)
|
||||
}
|
||||
is LibraryViewModel.Navigation.OpenTypeEditing -> {
|
||||
findNavController().safeNavigate(
|
||||
R.id.libraryFragment,
|
||||
R.id.openTypeEditingScreen,
|
||||
TypeEditFragment.args(
|
||||
typeName = it.view.name,
|
||||
id = it.view.id,
|
||||
iconUnicode = (it.view.icon as? ObjectIcon.Basic.Emoji)?.unicode ?: "",
|
||||
readOnly = it.view.readOnly
|
||||
)
|
||||
)
|
||||
}
|
||||
is LibraryViewModel.Navigation.OpenRelationCreation -> {
|
||||
findNavController().safeNavigate(
|
||||
R.id.libraryFragment,
|
||||
R.id.openRelationCreationScreen,
|
||||
RelationCreateFromScratchForObjectFragment.args(
|
||||
ctx = "",
|
||||
query = it.name,
|
||||
space = space
|
||||
)
|
||||
)
|
||||
}
|
||||
is LibraryViewModel.Navigation.OpenRelationEditing -> {
|
||||
findNavController().safeNavigate(
|
||||
R.id.libraryFragment,
|
||||
R.id.openRelationEditingScreen,
|
||||
RelationEditFragment.args(
|
||||
typeName = it.view.name,
|
||||
id = it.view.id,
|
||||
iconUnicode = it.view.format.simpleIcon() ?: 0,
|
||||
readOnly = it.view.readOnly
|
||||
)
|
||||
)
|
||||
}
|
||||
is LibraryViewModel.Navigation.Back -> {
|
||||
findNavController().popBackStack()
|
||||
}
|
||||
is LibraryViewModel.Navigation.Search -> {
|
||||
findNavController().safeNavigate(
|
||||
R.id.libraryFragment,
|
||||
R.id.pageSearchFragment
|
||||
)
|
||||
}
|
||||
is LibraryViewModel.Navigation.OpenEditor -> {
|
||||
findNavController().safeNavigate(
|
||||
R.id.libraryFragment,
|
||||
R.id.objectNavigation,
|
||||
EditorFragment.args(
|
||||
ctx = it.id,
|
||||
space = space
|
||||
)
|
||||
)
|
||||
}
|
||||
is LibraryViewModel.Navigation.ExitToVault -> {
|
||||
runCatching {
|
||||
findNavController().navigate(R.id.actionOpenVault)
|
||||
}.onFailure { e ->
|
||||
Timber.e(e, "Error while exiting to vault from space library")
|
||||
}
|
||||
}
|
||||
is LibraryViewModel.Navigation.OpenSetOrCollection -> {
|
||||
findNavController().safeNavigate(
|
||||
R.id.libraryFragment,
|
||||
R.id.dataViewNavigation,
|
||||
bundleOf(
|
||||
ObjectSetFragment.CONTEXT_ID_KEY to it.id,
|
||||
ObjectSetFragment.SPACE_ID_KEY to space
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setFragmentResultListener(REQUEST_KEY_UNINSTALL_TYPE) { _, bundle ->
|
||||
val id = requireNotNull(bundle.getString(REQUEST_UNINSTALL_TYPE_ARG_ID))
|
||||
val name = requireNotNull(bundle.getString(REQUEST_UNINSTALL_TYPE_ARG_NAME))
|
||||
vm.uninstallObject(id, LibraryViewModel.LibraryItem.TYPE, name)
|
||||
}
|
||||
setFragmentResultListener(REQUEST_KEY_MODIFY_TYPE) { _, bundle ->
|
||||
val id = requireNotNull(bundle.getString(REQUEST_UNINSTALL_TYPE_ARG_ID))
|
||||
val name = requireNotNull(bundle.getString(REQUEST_UNINSTALL_TYPE_ARG_NAME))
|
||||
val icon = requireNotNull(bundle.getString(REQUEST_UNINSTALL_TYPE_ARG_ICON))
|
||||
vm.updateObject(id, name, icon)
|
||||
}
|
||||
setFragmentResultListener(REQUEST_KEY_UNINSTALL_RELATION) { _, bundle ->
|
||||
val id = requireNotNull(bundle.getString(REQUEST_UNINSTALL_RELATION_ARG_ID))
|
||||
val name = requireNotNull(bundle.getString(REQUEST_UNINSTALL_RELATION_ARG_NAME))
|
||||
vm.uninstallObject(id, LibraryViewModel.LibraryItem.RELATION, name)
|
||||
}
|
||||
setFragmentResultListener(REQUEST_KEY_MODIFY_RELATION) { _, bundle ->
|
||||
val id = requireNotNull(bundle.getString(REQUEST_UNINSTALL_RELATION_ARG_ID))
|
||||
val name = requireNotNull(bundle.getString(REQUEST_UNINSTALL_RELATION_ARG_NAME))
|
||||
vm.updateObject(
|
||||
id = id,
|
||||
name = name,
|
||||
icon = null
|
||||
)
|
||||
}
|
||||
setFragmentResultListener(REQUEST_CREATE_OBJECT) { _, _ ->
|
||||
vm.onObjectCreated()
|
||||
}
|
||||
}
|
||||
|
||||
override fun injectDependencies() {
|
||||
componentManager()
|
||||
.libraryComponent
|
||||
.get(
|
||||
Pair(
|
||||
requireContext(),
|
||||
LibraryViewModel.Params(
|
||||
space = SpaceId(space)
|
||||
)
|
||||
)
|
||||
)
|
||||
.inject(this)
|
||||
}
|
||||
|
||||
override fun releaseDependencies() {
|
||||
componentManager().libraryComponent.release()
|
||||
}
|
||||
|
||||
override fun onApplyWindowRootInsets(view: View) {
|
||||
if (BuildConfig.USE_EDGE_TO_EDGE && Build.VERSION.SDK_INT >= EDGE_TO_EDGE_MIN_SDK) {
|
||||
// Do nothing.
|
||||
} else {
|
||||
super.onApplyWindowRootInsets(view)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val ARG_SPACE_ID_KEY = "arg.library.space-id"
|
||||
fun args(space: Id) = bundleOf(ARG_SPACE_ID_KEY to space)
|
||||
}
|
||||
}
|
|
@ -1,199 +0,0 @@
|
|||
@file:OptIn(ExperimentalAnimationApi::class)
|
||||
|
||||
package com.anytypeio.anytype.ui.library
|
||||
|
||||
import android.os.Build
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.AnimatedVisibilityScope
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.animation.core.FastOutLinearInEasing
|
||||
import androidx.compose.animation.core.Spring
|
||||
import androidx.compose.animation.core.TweenSpec
|
||||
import androidx.compose.animation.core.VisibilityThreshold
|
||||
import androidx.compose.animation.core.spring
|
||||
import androidx.compose.animation.expandVertically
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.shrinkVertically
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.ime
|
||||
import androidx.compose.foundation.layout.navigationBars
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.systemBars
|
||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.Scaffold
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.unit.IntSize
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.anytypeio.anytype.BuildConfig
|
||||
import com.anytypeio.anytype.R
|
||||
import com.anytypeio.anytype.core_ui.foundation.components.BottomNavigationMenu
|
||||
import com.anytypeio.anytype.core_utils.insets.EDGE_TO_EDGE_MIN_SDK
|
||||
import com.anytypeio.anytype.presentation.library.LibraryEvent
|
||||
import com.anytypeio.anytype.presentation.library.LibraryViewModel
|
||||
import com.anytypeio.anytype.ui.library.views.LibraryTabs
|
||||
import com.anytypeio.anytype.ui.library.views.LibraryTabsContent
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
|
||||
|
||||
@ExperimentalAnimationApi
|
||||
@ExperimentalComposeUiApi
|
||||
@ExperimentalMaterialApi
|
||||
@FlowPreview
|
||||
@Composable
|
||||
fun LibraryScreen(
|
||||
configuration: LibraryConfiguration,
|
||||
viewModel: LibraryViewModel,
|
||||
onBackPressed: () -> Unit,
|
||||
onBackLongPressed: () -> Unit,
|
||||
onCreateObjectLongClicked: () -> Unit
|
||||
) {
|
||||
|
||||
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
||||
val effects by viewModel.effects.collectAsStateWithLifecycle()
|
||||
|
||||
val pagerState = rememberPagerState(
|
||||
initialPage = INITIAL_TAB,
|
||||
pageCount = { 2 }
|
||||
)
|
||||
val modifier = Modifier
|
||||
.background(color = colorResource(id = R.color.background_primary))
|
||||
|
||||
val screenState = remember { mutableStateOf(ScreenState.CONTENT) }
|
||||
|
||||
Scaffold(
|
||||
bottomBar = {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.then(
|
||||
if (BuildConfig.USE_EDGE_TO_EDGE && Build.VERSION.SDK_INT >= EDGE_TO_EDGE_MIN_SDK)
|
||||
Modifier
|
||||
.windowInsetsPadding(WindowInsets.navigationBars)
|
||||
else
|
||||
Modifier
|
||||
)
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
Menu(
|
||||
viewModel,
|
||||
modifier = Modifier.align(Alignment.BottomCenter),
|
||||
screenState = screenState,
|
||||
onCreateObjectLongClicked = onCreateObjectLongClicked,
|
||||
onBackLongClicked = onBackLongPressed
|
||||
)
|
||||
}
|
||||
}
|
||||
) { paddingValues ->
|
||||
Column(
|
||||
modifier = modifier
|
||||
.padding(paddingValues)
|
||||
.then(
|
||||
if (BuildConfig.USE_EDGE_TO_EDGE && Build.VERSION.SDK_INT >= EDGE_TO_EDGE_MIN_SDK)
|
||||
Modifier.windowInsetsPadding(WindowInsets.systemBars)
|
||||
else
|
||||
Modifier
|
||||
),
|
||||
) {
|
||||
LibraryTabs(
|
||||
modifier = modifier,
|
||||
pagerState = pagerState,
|
||||
configuration = configuration,
|
||||
screenState = screenState,
|
||||
)
|
||||
LibraryTabsContent(
|
||||
modifier = modifier,
|
||||
pagerState = pagerState,
|
||||
configuration = listOf(configuration.types, configuration.relations),
|
||||
state = uiState,
|
||||
vmEventStream = viewModel::eventStream,
|
||||
vmAnalyticsStream = viewModel::analyticsStream,
|
||||
screenState = screenState,
|
||||
effects = effects
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
BackHandler {
|
||||
if (screenState.value == ScreenState.SEARCH) {
|
||||
screenState.value = ScreenState.CONTENT
|
||||
} else {
|
||||
onBackPressed.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Menu(
|
||||
viewModel: LibraryViewModel,
|
||||
modifier: Modifier = Modifier,
|
||||
screenState: MutableState<ScreenState>,
|
||||
onCreateObjectLongClicked: () -> Unit = {},
|
||||
onBackLongClicked: () -> Unit
|
||||
) {
|
||||
val isImeVisible = WindowInsets.ime.getBottom(LocalDensity.current) > 0
|
||||
if (isImeVisible) return
|
||||
BottomNavigationMenu(
|
||||
modifier = modifier,
|
||||
backClick = {
|
||||
if (screenState.value == ScreenState.SEARCH) {
|
||||
screenState.value = ScreenState.CONTENT
|
||||
} else {
|
||||
viewModel.eventStream(LibraryEvent.BottomMenu.Back)
|
||||
}
|
||||
},
|
||||
backLongClick = {
|
||||
onBackLongClicked()
|
||||
},
|
||||
searchClick = { viewModel.eventStream(LibraryEvent.BottomMenu.Search) },
|
||||
addDocClick = { viewModel.eventStream(LibraryEvent.BottomMenu.CreateObject) },
|
||||
addDocLongClick = onCreateObjectLongClicked,
|
||||
isOwnerOrEditor = false
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun WrapWithLibraryAnimation(
|
||||
visible: Boolean,
|
||||
content: @Composable AnimatedVisibilityScope.() -> Unit
|
||||
) {
|
||||
val animationSpecFade = TweenSpec<Float>(
|
||||
easing = FastOutLinearInEasing,
|
||||
durationMillis = FADE_DURATION_MILLIS
|
||||
)
|
||||
val animationSpecSlide = spring(
|
||||
stiffness = Spring.StiffnessMediumLow,
|
||||
visibilityThreshold = IntSize.VisibilityThreshold
|
||||
)
|
||||
AnimatedVisibility(
|
||||
visible = visible,
|
||||
enter = fadeIn(animationSpecFade) + expandVertically(animationSpecSlide),
|
||||
exit = fadeOut(animationSpecFade) + shrinkVertically(animationSpecSlide)
|
||||
) { content() }
|
||||
}
|
||||
|
||||
enum class ScreenState {
|
||||
CONTENT,
|
||||
SEARCH;
|
||||
|
||||
fun visible() = this == CONTENT
|
||||
}
|
||||
|
||||
private const val INITIAL_TAB = 0
|
||||
private const val FADE_DURATION_MILLIS = 50
|
|
@ -1,67 +0,0 @@
|
|||
package com.anytypeio.anytype.ui.library
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.anytypeio.anytype.R
|
||||
|
||||
data class LibraryConfiguration(
|
||||
val types: LibraryScreenConfig = LibraryScreenConfig.Types(),
|
||||
val relations: LibraryScreenConfig = LibraryScreenConfig.Relations()
|
||||
)
|
||||
|
||||
sealed class LibraryScreenConfig(
|
||||
@StringRes val mainTitle: Int,
|
||||
@StringRes val mainBtnTitle: Int,
|
||||
@StringRes val description: Int,
|
||||
val listConfig: List<LibraryListConfig>,
|
||||
val index: Int,
|
||||
val titleAlignment: Alignment.Horizontal,
|
||||
val titlePaddingEnd: Dp,
|
||||
val titlePaddingStart: Dp
|
||||
) {
|
||||
class Types : LibraryScreenConfig(
|
||||
R.string.library_title_types,
|
||||
R.string.library_button_create_type,
|
||||
R.string.library_description_types,
|
||||
listOf(LibraryListConfig.Types, LibraryListConfig.TypesLibrary),
|
||||
0,
|
||||
Alignment.End,
|
||||
0.dp,
|
||||
16.dp
|
||||
)
|
||||
|
||||
class Relations : LibraryScreenConfig(
|
||||
R.string.library_title_relations,
|
||||
R.string.library_button_create_relation,
|
||||
R.string.library_description_relations,
|
||||
listOf(LibraryListConfig.Relations, LibraryListConfig.RelationsLibrary),
|
||||
1,
|
||||
Alignment.Start,
|
||||
16.dp,
|
||||
0.dp
|
||||
)
|
||||
}
|
||||
|
||||
sealed class LibraryListConfig(
|
||||
@StringRes val title: Int,
|
||||
val subtitleTabOffset: Dp,
|
||||
) {
|
||||
object Types : LibraryListConfig(
|
||||
R.string.library_subtitle_types, 0.dp
|
||||
)
|
||||
|
||||
object TypesLibrary : LibraryListConfig(
|
||||
R.string.library_subtitle_library, (-14).dp
|
||||
)
|
||||
|
||||
object Relations : LibraryListConfig(
|
||||
R.string.library_subtitle_relations, 0.dp
|
||||
)
|
||||
|
||||
object RelationsLibrary : LibraryListConfig(
|
||||
R.string.library_subtitle_library, (-14).dp
|
||||
)
|
||||
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
package com.anytypeio.anytype.ui.library.styles
|
||||
|
||||
import androidx.compose.ui.text.font.Font
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.anytypeio.anytype.R
|
||||
import androidx.compose.ui.text.TextStyle as TextStyle
|
||||
|
||||
val fonts = FontFamily(
|
||||
Font(R.font.inter_regular),
|
||||
Font(R.font.inter_bold, weight = FontWeight.Bold),
|
||||
Font(R.font.inter_medium, weight = FontWeight.Medium),
|
||||
Font(R.font.inter_semibold, weight = FontWeight.SemiBold)
|
||||
)
|
||||
|
||||
val TabTitleStyle = TextStyle(
|
||||
fontFamily = fonts,
|
||||
fontSize = 15.sp,
|
||||
fontWeight = FontWeight.SemiBold
|
||||
)
|
|
@ -1,53 +0,0 @@
|
|||
package com.anytypeio.anytype.ui.library.views
|
||||
|
||||
import androidx.compose.animation.core.FastOutSlowInEasing
|
||||
import androidx.compose.animation.core.animateDpAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.wrapContentSize
|
||||
import androidx.compose.material.TabPosition
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
fun Modifier.libraryTabOffset(
|
||||
currentTabPosition: TabPosition,
|
||||
tabWidth: Dp
|
||||
): Modifier {
|
||||
val currentTabWidth = animateDpAsState(
|
||||
targetValue = tabWidth,
|
||||
animationSpec = tween(
|
||||
durationMillis = ANIMATION_LENGTH,
|
||||
easing = FastOutSlowInEasing
|
||||
)
|
||||
)
|
||||
|
||||
val targetValue = if (currentTabPosition.left == 0.dp) {
|
||||
(currentTabPosition.left + currentTabPosition.right - tabWidth - TAB_OFFSET.dp)
|
||||
} else {
|
||||
(currentTabPosition.left)
|
||||
}
|
||||
|
||||
val indicatorOffset = animateDpAsState(
|
||||
targetValue = targetValue,
|
||||
animationSpec = tween(
|
||||
durationMillis = ANIMATION_LENGTH,
|
||||
easing = FastOutSlowInEasing
|
||||
)
|
||||
)
|
||||
return fillMaxWidth()
|
||||
.wrapContentSize(Alignment.BottomStart)
|
||||
.offset(x = indicatorOffset.value)
|
||||
.height(INDICATOR_HEIGHT.dp)
|
||||
.width(currentTabWidth.value + TAB_OFFSET.dp)
|
||||
}
|
||||
|
||||
private const val TAB_OFFSET = 32
|
||||
private const val INDICATOR_HEIGHT = 1
|
||||
private const val ANIMATION_LENGTH = 150
|
|
@ -1,142 +0,0 @@
|
|||
package com.anytypeio.anytype.ui.library.views
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.pager.PagerState
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.LocalRippleConfiguration
|
||||
import androidx.compose.material.RippleConfiguration
|
||||
import androidx.compose.material.Tab
|
||||
import androidx.compose.material.TabRow
|
||||
import androidx.compose.material.TabRowDefaults
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.ripple.RippleAlpha
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.TextLayoutResult
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.anytypeio.anytype.R
|
||||
import com.anytypeio.anytype.ui.library.LibraryConfiguration
|
||||
import com.anytypeio.anytype.ui.library.LibraryScreenConfig
|
||||
import com.anytypeio.anytype.ui.library.ScreenState
|
||||
import com.anytypeio.anytype.ui.library.WrapWithLibraryAnimation
|
||||
import com.anytypeio.anytype.ui.library.styles.TabTitleStyle
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class, ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
fun LibraryTabs(
|
||||
modifier: Modifier,
|
||||
pagerState: PagerState,
|
||||
configuration: LibraryConfiguration,
|
||||
screenState: MutableState<ScreenState>
|
||||
) = WrapWithLibraryAnimation(visible = screenState.value.visible()) {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
val density = LocalDensity.current
|
||||
val tabWidths = remember {
|
||||
val tabWidthStateList = mutableStateListOf(0.dp, 0.dp)
|
||||
tabWidthStateList
|
||||
}
|
||||
|
||||
TabRow(
|
||||
modifier = modifier,
|
||||
selectedTabIndex = pagerState.currentPage,
|
||||
backgroundColor = colorResource(id = R.color.background_primary),
|
||||
divider = {},
|
||||
indicator = { tabPositions ->
|
||||
TabRowDefaults.Indicator(
|
||||
modifier = Modifier
|
||||
.libraryTabOffset(
|
||||
currentTabPosition = tabPositions[pagerState.currentPage],
|
||||
tabWidth = tabWidths[pagerState.currentPage]
|
||||
),
|
||||
color = colorResource(id = R.color.black)
|
||||
)
|
||||
},
|
||||
tabs = {
|
||||
CompositionLocalProvider(LocalRippleConfiguration provides LibraryRippleTheme) {
|
||||
LibraryTab(
|
||||
modifier = modifier,
|
||||
config = configuration.types,
|
||||
pagerState = pagerState,
|
||||
onClick = {
|
||||
coroutineScope.launch {
|
||||
pagerState.animateScrollToPage(0)
|
||||
}
|
||||
},
|
||||
onTextLayout = { tlResult ->
|
||||
tabWidths[0] = with(density) { tlResult.size.width.toDp() }
|
||||
}
|
||||
)
|
||||
LibraryTab(
|
||||
modifier = modifier,
|
||||
config = configuration.relations,
|
||||
pagerState = pagerState,
|
||||
onClick = {
|
||||
coroutineScope.launch {
|
||||
pagerState.animateScrollToPage(1)
|
||||
}
|
||||
},
|
||||
onTextLayout = { tlResult ->
|
||||
tabWidths[1] = with(density) { tlResult.size.width.toDp() }
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
private val LibraryRippleTheme = RippleConfiguration(color = Color.Unspecified, rippleAlpha = RippleAlpha(0f, 0f, 0f, 0f))
|
||||
|
||||
@Composable
|
||||
fun LibraryTab(
|
||||
modifier: Modifier,
|
||||
config: LibraryScreenConfig,
|
||||
pagerState: PagerState,
|
||||
onClick: () -> Unit,
|
||||
onTextLayout: (TextLayoutResult) -> Unit
|
||||
) {
|
||||
|
||||
Tab(
|
||||
modifier = modifier,
|
||||
selectedContentColor = colorResource(id = R.color.glyph_selected),
|
||||
unselectedContentColor = colorResource(id = R.color.glyph_active),
|
||||
text = {
|
||||
Column(
|
||||
horizontalAlignment = config.titleAlignment,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
start = config.titlePaddingStart,
|
||||
end = config.titlePaddingEnd,
|
||||
)
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = config.mainTitle),
|
||||
style = TabTitleStyle.copy(
|
||||
color = if (pagerState.currentPage == config.index) {
|
||||
colorResource(id = R.color.glyph_selected)
|
||||
} else {
|
||||
colorResource(id = R.color.glyph_active)
|
||||
}
|
||||
),
|
||||
onTextLayout = onTextLayout::invoke
|
||||
)
|
||||
}
|
||||
},
|
||||
selected = pagerState.currentPage == config.index,
|
||||
onClick = onClick,
|
||||
)
|
||||
}
|
|
@ -1,147 +0,0 @@
|
|||
package com.anytypeio.anytype.ui.library.views
|
||||
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.pager.HorizontalPager
|
||||
import androidx.compose.foundation.pager.PagerState
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.anytypeio.anytype.R
|
||||
import com.anytypeio.anytype.core_ui.views.ButtonPrimary
|
||||
import com.anytypeio.anytype.core_ui.views.ButtonSize
|
||||
import com.anytypeio.anytype.core_ui.views.HeadlineTitle
|
||||
import com.anytypeio.anytype.presentation.library.LibraryAnalyticsEvent
|
||||
import com.anytypeio.anytype.presentation.library.LibraryEvent
|
||||
import com.anytypeio.anytype.presentation.library.LibraryScreenState
|
||||
import com.anytypeio.anytype.presentation.library.LibraryViewModel
|
||||
import com.anytypeio.anytype.ui.library.LibraryScreenConfig
|
||||
import com.anytypeio.anytype.ui.library.ScreenState
|
||||
import com.anytypeio.anytype.ui.library.WrapWithLibraryAnimation
|
||||
import com.anytypeio.anytype.ui.library.views.list.LibraryListView
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
|
||||
@ExperimentalAnimationApi
|
||||
@FlowPreview
|
||||
@Composable
|
||||
fun LibraryTabsContent(
|
||||
modifier: Modifier,
|
||||
pagerState: PagerState,
|
||||
configuration: List<LibraryScreenConfig>,
|
||||
state: LibraryScreenState,
|
||||
vmEventStream: (LibraryEvent) -> Unit,
|
||||
vmAnalyticsStream: (LibraryAnalyticsEvent.Ui) -> Unit,
|
||||
screenState: MutableState<ScreenState>,
|
||||
effects: LibraryViewModel.Effect,
|
||||
) {
|
||||
HorizontalPager(
|
||||
modifier = modifier,
|
||||
state = pagerState
|
||||
) { page ->
|
||||
val dataTabs = when (configuration[page]) {
|
||||
is LibraryScreenConfig.Types -> {
|
||||
state.types
|
||||
}
|
||||
is LibraryScreenConfig.Relations -> {
|
||||
state.relations
|
||||
}
|
||||
}
|
||||
TabContentScreen(
|
||||
modifier = modifier,
|
||||
config = configuration[page],
|
||||
tabs = dataTabs,
|
||||
vmEventStream = vmEventStream,
|
||||
vmAnalyticsStream = vmAnalyticsStream,
|
||||
screenState = screenState,
|
||||
effects = effects
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ExperimentalAnimationApi
|
||||
@FlowPreview
|
||||
@Composable
|
||||
fun TabContentScreen(
|
||||
modifier: Modifier,
|
||||
config: LibraryScreenConfig,
|
||||
tabs: LibraryScreenState.Tabs,
|
||||
vmEventStream: (LibraryEvent) -> Unit,
|
||||
vmAnalyticsStream: (LibraryAnalyticsEvent.Ui) -> Unit,
|
||||
screenState: MutableState<ScreenState>,
|
||||
effects: LibraryViewModel.Effect
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier.fillMaxSize(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Top,
|
||||
) {
|
||||
Header(config, vmEventStream, screenState)
|
||||
LibraryListView(
|
||||
libraryListConfig = config.listConfig,
|
||||
tabs = tabs,
|
||||
vmEventStream = vmEventStream,
|
||||
vmAnalyticsStream = vmAnalyticsStream,
|
||||
screenState = screenState,
|
||||
effects = effects
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Header(
|
||||
config: LibraryScreenConfig,
|
||||
vmEventStream: (LibraryEvent) -> Unit,
|
||||
screenState: MutableState<ScreenState>
|
||||
) = WrapWithLibraryAnimation(visible = screenState.value.visible()) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(config.description),
|
||||
style = HeadlineTitle.copy(
|
||||
color = colorResource(id = R.color.text_primary)
|
||||
),
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.padding(
|
||||
top = 58.dp,
|
||||
start = HeaderDefaults.HeaderPadding,
|
||||
end = HeaderDefaults.HeaderPadding
|
||||
)
|
||||
)
|
||||
Box(Modifier.height(14.dp))
|
||||
ButtonPrimary(
|
||||
onClick = {
|
||||
when (config) {
|
||||
is LibraryScreenConfig.Types -> {
|
||||
vmEventStream.invoke(LibraryEvent.Type.Create())
|
||||
}
|
||||
is LibraryScreenConfig.Relations -> {
|
||||
vmEventStream.invoke(LibraryEvent.Relation.Create())
|
||||
}
|
||||
}
|
||||
},
|
||||
modifier = Modifier.padding(bottom = 48.dp),
|
||||
text = stringResource(config.mainBtnTitle),
|
||||
size = ButtonSize.Medium.apply {
|
||||
contentPadding = PaddingValues(28.dp, 10.dp, 28.dp, 10.dp)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private object HeaderDefaults {
|
||||
val HeaderPadding = 20.dp
|
||||
}
|
|
@ -1,123 +0,0 @@
|
|||
package com.anytypeio.anytype.ui.library.views
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.LocalTextStyle
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.TextFieldColors
|
||||
import androidx.compose.material.TextFieldDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.graphics.takeOrElse
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.input.VisualTransformation
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.anytypeio.anytype.ui.library.views.LibraryTextFieldDefaults.ContentPaddingEnd
|
||||
import com.anytypeio.anytype.ui.library.views.LibraryTextFieldDefaults.OffsetXSearchText
|
||||
|
||||
@Composable
|
||||
fun LibraryTextField(
|
||||
value: String,
|
||||
onValueChange: (String) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
readOnly: Boolean = false,
|
||||
textStyle: TextStyle = LocalTextStyle.current,
|
||||
label: @Composable (() -> Unit)? = null,
|
||||
placeholder: @Composable (() -> Unit)? = null,
|
||||
leadingIcon: @Composable (() -> Unit)? = null,
|
||||
trailingIcon: @Composable (() -> Unit)? = null,
|
||||
isError: Boolean = false,
|
||||
visualTransformation: VisualTransformation = VisualTransformation.None,
|
||||
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
|
||||
keyboardActions: KeyboardActions = KeyboardActions.Default,
|
||||
singleLine: Boolean = false,
|
||||
maxLines: Int = Int.MAX_VALUE,
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
shape: Shape = MaterialTheme.shapes.small,
|
||||
colors: TextFieldColors = TextFieldDefaults.outlinedTextFieldColors()
|
||||
) {
|
||||
// If color is not provided via text style, use content color as a default
|
||||
val textColor = textStyle.color.takeOrElse {
|
||||
colors.textColor(enabled).value
|
||||
}
|
||||
val mergedTextStyle = textStyle.merge(TextStyle(color = textColor))
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
BasicTextField(
|
||||
value = value,
|
||||
modifier = if (label != null) {
|
||||
modifier.semantics(mergeDescendants = true) {}
|
||||
} else {
|
||||
modifier
|
||||
}
|
||||
.background(colors.backgroundColor(enabled).value, shape)
|
||||
.defaultMinSize(
|
||||
minWidth = TextFieldDefaults.MinWidth,
|
||||
minHeight = LibraryTextFieldDefaults.MinHeight
|
||||
).offset(
|
||||
x = OffsetXSearchText,
|
||||
),
|
||||
onValueChange = onValueChange,
|
||||
enabled = enabled,
|
||||
readOnly = readOnly,
|
||||
textStyle = mergedTextStyle,
|
||||
cursorBrush = SolidColor(colors.cursorColor(isError).value),
|
||||
visualTransformation = visualTransformation,
|
||||
keyboardOptions = keyboardOptions,
|
||||
keyboardActions = keyboardActions,
|
||||
interactionSource = interactionSource,
|
||||
singleLine = singleLine,
|
||||
maxLines = maxLines,
|
||||
decorationBox = @Composable { innerTextField ->
|
||||
TextFieldDefaults.OutlinedTextFieldDecorationBox(
|
||||
value = value,
|
||||
visualTransformation = visualTransformation,
|
||||
innerTextField = innerTextField,
|
||||
placeholder = placeholder,
|
||||
label = label,
|
||||
leadingIcon = leadingIcon,
|
||||
trailingIcon = trailingIcon,
|
||||
singleLine = singleLine,
|
||||
enabled = enabled,
|
||||
isError = isError,
|
||||
interactionSource = interactionSource,
|
||||
colors = colors,
|
||||
contentPadding = PaddingValues(
|
||||
start = 0.dp,
|
||||
top = 0.dp,
|
||||
end = ContentPaddingEnd,
|
||||
bottom = 0.dp
|
||||
),
|
||||
border = {
|
||||
TextFieldDefaults.BorderBox(
|
||||
enabled,
|
||||
isError,
|
||||
interactionSource,
|
||||
colors,
|
||||
shape
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Immutable
|
||||
object LibraryTextFieldDefaults {
|
||||
val MinHeight = 36.dp
|
||||
val ContentPaddingEnd = 16.dp
|
||||
val OffsetXSearchText = (-12).dp
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
package com.anytypeio.anytype.ui.library.views.list
|
||||
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import com.anytypeio.anytype.R
|
||||
import com.anytypeio.anytype.presentation.library.LibraryAnalyticsEvent
|
||||
import com.anytypeio.anytype.presentation.library.LibraryEvent
|
||||
import com.anytypeio.anytype.presentation.library.LibraryScreenState
|
||||
import com.anytypeio.anytype.presentation.library.LibraryViewModel
|
||||
import com.anytypeio.anytype.ui.library.LibraryListConfig
|
||||
import com.anytypeio.anytype.ui.library.ScreenState
|
||||
|
||||
@ExperimentalAnimationApi
|
||||
@Composable
|
||||
fun LibraryListView(
|
||||
libraryListConfig: List<LibraryListConfig>,
|
||||
tabs: LibraryScreenState.Tabs,
|
||||
vmEventStream: (LibraryEvent) -> Unit,
|
||||
vmAnalyticsStream: (LibraryAnalyticsEvent.Ui) -> Unit,
|
||||
screenState: MutableState<ScreenState>,
|
||||
effects: LibraryViewModel.Effect,
|
||||
) {
|
||||
val pagerState = rememberPagerState(
|
||||
initialPage = INITIAL_TAB,
|
||||
pageCount = { 2 }
|
||||
)
|
||||
val modifier = Modifier.background(
|
||||
color = colorResource(id = R.color.background_primary)
|
||||
)
|
||||
Column {
|
||||
LibraryListTabs(pagerState, libraryListConfig, modifier)
|
||||
LibraryListTabsContent(
|
||||
modifier = modifier,
|
||||
pagerState = pagerState,
|
||||
configuration = libraryListConfig,
|
||||
tabs = tabs,
|
||||
vmEventStream = vmEventStream,
|
||||
vmAnalyticsStream = vmAnalyticsStream,
|
||||
screenState = screenState,
|
||||
effects = effects
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private const val INITIAL_TAB = 0
|
|
@ -1,100 +0,0 @@
|
|||
package com.anytypeio.anytype.ui.library.views.list
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.TextFieldDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.onFocusEvent
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.anytypeio.anytype.R
|
||||
import com.anytypeio.anytype.core_ui.views.UXBody
|
||||
import com.anytypeio.anytype.presentation.library.LibraryEvent
|
||||
import com.anytypeio.anytype.ui.library.LibraryListConfig
|
||||
import com.anytypeio.anytype.ui.library.ScreenState
|
||||
import com.anytypeio.anytype.ui.library.views.LibraryTextField
|
||||
import com.anytypeio.anytype.ui.library.views.list.LibraryListSearchWidgetDefaults.CornerRadius
|
||||
import com.anytypeio.anytype.ui.library.views.list.LibraryListSearchWidgetDefaults.Height
|
||||
import com.anytypeio.anytype.ui.library.views.list.LibraryListSearchWidgetDefaults.LeadingIconOffset
|
||||
import com.anytypeio.anytype.ui.library.views.list.LibraryListSearchWidgetDefaults.PaddingVertical
|
||||
|
||||
@Composable
|
||||
fun LibraryListSearchWidget(
|
||||
vmEventStream: (LibraryEvent) -> Unit,
|
||||
config: LibraryListConfig,
|
||||
modifier: Modifier,
|
||||
animationStartState: MutableState<Boolean>,
|
||||
screenState: MutableState<ScreenState>,
|
||||
input: MutableState<String>
|
||||
) {
|
||||
LibraryTextField(
|
||||
value = input.value,
|
||||
onValueChange = {
|
||||
input.value = it
|
||||
vmEventStream.invoke(
|
||||
config.toEvent(input.value)
|
||||
)
|
||||
},
|
||||
shape = RoundedCornerShape(CornerRadius),
|
||||
modifier = modifier
|
||||
.padding(vertical = PaddingVertical)
|
||||
.height(Height)
|
||||
.onFocusEvent {
|
||||
if (it.isFocused && animationStartState.value.not()) {
|
||||
animationStartState.value = true
|
||||
screenState.value = ScreenState.SEARCH
|
||||
}
|
||||
},
|
||||
textStyle = UXBody,
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringResource(id = R.string.search),
|
||||
style = UXBody
|
||||
)
|
||||
},
|
||||
colors = TextFieldDefaults.outlinedTextFieldColors(
|
||||
textColor = colorResource(id = R.color.text_primary),
|
||||
backgroundColor = colorResource(id = R.color.shape_transparent),
|
||||
disabledBorderColor = Color.Transparent,
|
||||
errorBorderColor = Color.Transparent,
|
||||
focusedBorderColor = Color.Transparent,
|
||||
unfocusedBorderColor = Color.Transparent,
|
||||
placeholderColor = colorResource(id = R.color.glyph_active),
|
||||
cursorColor = colorResource(id = R.color.orange)
|
||||
),
|
||||
singleLine = true,
|
||||
maxLines = 1,
|
||||
leadingIcon = {
|
||||
Image(
|
||||
painterResource(id = R.drawable.ic_search),
|
||||
"",
|
||||
modifier = Modifier.offset(x = LeadingIconOffset)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun LibraryListConfig.toEvent(query: String): LibraryEvent.Query = when (this) {
|
||||
LibraryListConfig.Relations -> LibraryEvent.Query.MyRelations(query)
|
||||
LibraryListConfig.RelationsLibrary -> LibraryEvent.Query.LibraryRelations(query)
|
||||
LibraryListConfig.Types -> LibraryEvent.Query.MyTypes(query)
|
||||
LibraryListConfig.TypesLibrary -> LibraryEvent.Query.LibraryTypes(query)
|
||||
}
|
||||
|
||||
@Immutable
|
||||
private object LibraryListSearchWidgetDefaults {
|
||||
val Height = 36.dp
|
||||
val PaddingVertical = 6.dp
|
||||
val CornerRadius = 10.dp
|
||||
val LeadingIconOffset = 8.dp
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
package com.anytypeio.anytype.ui.library.views.list
|
||||
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.wrapContentWidth
|
||||
import androidx.compose.foundation.pager.PagerState
|
||||
import androidx.compose.material.ScrollableTabRow
|
||||
import androidx.compose.material.Tab
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.LocalRippleConfiguration
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.anytypeio.anytype.R
|
||||
import com.anytypeio.anytype.core_ui.views.HeadlineSubheading
|
||||
import com.anytypeio.anytype.ui.library.LibraryListConfig
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@ExperimentalAnimationApi
|
||||
@Composable
|
||||
fun LibraryListTabs(
|
||||
pagerState: PagerState,
|
||||
configuration: List<LibraryListConfig>,
|
||||
modifier: Modifier,
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
ScrollableTabRow(
|
||||
selectedTabIndex = pagerState.currentPage,
|
||||
backgroundColor = colorResource(id = R.color.background_primary),
|
||||
indicator = {},
|
||||
divider = {},
|
||||
edgePadding = 0.dp,
|
||||
modifier = modifier.padding(start = 4.dp),
|
||||
tabs = {
|
||||
CompositionLocalProvider(LocalRippleConfiguration provides null) {
|
||||
configuration.forEachIndexed { index, it ->
|
||||
LibraryListTab(
|
||||
config = it,
|
||||
pagerState = pagerState,
|
||||
onClick = {
|
||||
scope.launch {
|
||||
pagerState.animateScrollToPage(index)
|
||||
}
|
||||
},
|
||||
index = index,
|
||||
modifier
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
fun LibraryListTab(
|
||||
config: LibraryListConfig,
|
||||
pagerState: PagerState,
|
||||
onClick: () -> Unit,
|
||||
index: Int,
|
||||
modifier: Modifier
|
||||
) {
|
||||
Tab(
|
||||
selectedContentColor = colorResource(id = R.color.glyph_selected),
|
||||
unselectedContentColor = colorResource(id = R.color.glyph_active),
|
||||
modifier = modifier.wrapContentWidth(),
|
||||
text = {
|
||||
Text(
|
||||
text = stringResource(id = config.title),
|
||||
style = HeadlineSubheading.copy(
|
||||
color = if (pagerState.currentPage == index) {
|
||||
colorResource(id = R.color.glyph_selected)
|
||||
} else {
|
||||
colorResource(id = R.color.glyph_active)
|
||||
}
|
||||
),
|
||||
modifier = modifier
|
||||
.wrapContentWidth()
|
||||
.offset(x = config.subtitleTabOffset),
|
||||
)
|
||||
},
|
||||
selected = pagerState.currentPage == index,
|
||||
onClick = onClick,
|
||||
)
|
||||
}
|
|
@ -1,316 +0,0 @@
|
|||
package com.anytypeio.anytype.ui.library.views.list
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.pager.HorizontalPager
|
||||
import androidx.compose.foundation.pager.PagerState
|
||||
import androidx.compose.material.Divider
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.snapshotFlow
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.anytypeio.anytype.R
|
||||
import com.anytypeio.anytype.core_ui.foundation.noRippleClickable
|
||||
import com.anytypeio.anytype.core_ui.views.UXBody
|
||||
import com.anytypeio.anytype.presentation.library.DependentData
|
||||
import com.anytypeio.anytype.presentation.library.LibraryAnalyticsEvent
|
||||
import com.anytypeio.anytype.presentation.library.LibraryEvent
|
||||
import com.anytypeio.anytype.presentation.library.LibraryScreenState
|
||||
import com.anytypeio.anytype.presentation.library.LibraryView
|
||||
import com.anytypeio.anytype.presentation.library.LibraryViewModel
|
||||
import com.anytypeio.anytype.ui.library.LibraryListConfig
|
||||
import com.anytypeio.anytype.ui.library.ScreenState
|
||||
import com.anytypeio.anytype.ui.library.views.list.LibraryListDefaults.SearchBarPadding
|
||||
import com.anytypeio.anytype.ui.library.views.list.LibraryListDefaults.SearchCancelPaddingStart
|
||||
import com.anytypeio.anytype.ui.library.views.list.LibraryListDefaults.SearchCancelPaddingTop
|
||||
import com.anytypeio.anytype.ui.library.views.list.items.CreateNewRelationItem
|
||||
import com.anytypeio.anytype.ui.library.views.list.items.CreateNewTypeItem
|
||||
import com.anytypeio.anytype.ui.library.views.list.items.ItemDefaults
|
||||
import com.anytypeio.anytype.ui.library.views.list.items.LibRelationItem
|
||||
import com.anytypeio.anytype.ui.library.views.list.items.LibTypeItem
|
||||
import com.anytypeio.anytype.ui.library.views.list.items.LibraryObjectEmptyItem
|
||||
import com.anytypeio.anytype.ui.library.views.list.items.MyRelationItem
|
||||
import com.anytypeio.anytype.ui.library.views.list.items.MyTypeItem
|
||||
|
||||
@ExperimentalAnimationApi
|
||||
@Composable
|
||||
fun LibraryListTabsContent(
|
||||
modifier: Modifier,
|
||||
pagerState: PagerState,
|
||||
configuration: List<LibraryListConfig>,
|
||||
tabs: LibraryScreenState.Tabs,
|
||||
vmEventStream: (LibraryEvent) -> Unit,
|
||||
vmAnalyticsStream: (LibraryAnalyticsEvent.Ui) -> Unit,
|
||||
screenState: MutableState<ScreenState>,
|
||||
effects: LibraryViewModel.Effect,
|
||||
) {
|
||||
|
||||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
val focusManager = LocalFocusManager.current
|
||||
val animationStartState = remember {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
val input = remember { mutableStateOf(String()) }
|
||||
|
||||
HorizontalPager(
|
||||
modifier = modifier,
|
||||
state = pagerState,
|
||||
userScrollEnabled = screenState.value == ScreenState.CONTENT
|
||||
) { index ->
|
||||
val data = when (configuration[index]) {
|
||||
is LibraryListConfig.Types, is LibraryListConfig.Relations -> {
|
||||
tabs.my
|
||||
}
|
||||
is LibraryListConfig.TypesLibrary, is LibraryListConfig.RelationsLibrary -> {
|
||||
tabs.lib
|
||||
}
|
||||
}
|
||||
val itemsListState = rememberLazyListState()
|
||||
Column(
|
||||
modifier = modifier,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Top
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.padding(start = SearchBarPadding, end = SearchBarPadding)
|
||||
) {
|
||||
LaunchedEffect(key1 = effects) {
|
||||
if (effects is LibraryViewModel.Effect.ObjectCreated) {
|
||||
input.value = ""
|
||||
vmEventStream.invoke(
|
||||
configuration[index].toEvent(input.value)
|
||||
)
|
||||
animationStartState.value = false
|
||||
keyboardController?.hide()
|
||||
focusManager.clearFocus()
|
||||
screenState.value = ScreenState.CONTENT
|
||||
itemsListState.animateScrollToItem(0)
|
||||
}
|
||||
}
|
||||
LibraryListSearchWidget(
|
||||
vmEventStream = vmEventStream,
|
||||
config = configuration[index],
|
||||
modifier = Modifier.weight(1f),
|
||||
screenState = screenState,
|
||||
animationStartState = animationStartState,
|
||||
input = input
|
||||
)
|
||||
SearchCancel(
|
||||
modifier = Modifier
|
||||
.padding(start = SearchCancelPaddingStart, top = SearchCancelPaddingTop)
|
||||
.noRippleClickable {
|
||||
input.value = ""
|
||||
vmEventStream.invoke(
|
||||
configuration[index].toEvent(input.value)
|
||||
)
|
||||
keyboardController?.hide()
|
||||
focusManager.clearFocus()
|
||||
animationStartState.value = false
|
||||
screenState.value = ScreenState.CONTENT
|
||||
},
|
||||
visible = screenState.value.visible().not()
|
||||
)
|
||||
}
|
||||
LibraryList(data, vmEventStream, screenState, itemsListState)
|
||||
}
|
||||
}
|
||||
attachAnalytics(pagerState, configuration, vmAnalyticsStream)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun attachAnalytics(
|
||||
pagerState: PagerState,
|
||||
configuration: List<LibraryListConfig>,
|
||||
vmAnalyticsStream: (LibraryAnalyticsEvent.Ui) -> Unit
|
||||
) {
|
||||
LaunchedEffect(pagerState) {
|
||||
snapshotFlow { pagerState.currentPage }.collect { page ->
|
||||
when (configuration[page]) {
|
||||
is LibraryListConfig.Types -> {
|
||||
vmAnalyticsStream.invoke(LibraryAnalyticsEvent.Ui.TabView.Types)
|
||||
}
|
||||
is LibraryListConfig.Relations -> {
|
||||
vmAnalyticsStream.invoke(LibraryAnalyticsEvent.Ui.TabView.Relations)
|
||||
}
|
||||
is LibraryListConfig.TypesLibrary -> {
|
||||
vmAnalyticsStream.invoke(LibraryAnalyticsEvent.Ui.TabView.LibTypes)
|
||||
}
|
||||
is LibraryListConfig.RelationsLibrary -> {
|
||||
vmAnalyticsStream.invoke(LibraryAnalyticsEvent.Ui.TabView.LibRelations)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SearchCancel(modifier: Modifier = Modifier, visible: Boolean = false) {
|
||||
AnimatedVisibility(visible = visible) {
|
||||
Text(
|
||||
modifier = modifier,
|
||||
text = stringResource(id = R.string.cancel),
|
||||
style = UXBody.copy(
|
||||
color = colorResource(id = R.color.glyph_active)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun LibraryList(
|
||||
data: LibraryScreenState.Tabs.TabData,
|
||||
vmEventStream: (LibraryEvent) -> Unit,
|
||||
screenState: MutableState<ScreenState>,
|
||||
itemsListState: LazyListState,
|
||||
) {
|
||||
|
||||
val itemModifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(ItemDefaults.ITEM_HEIGHT)
|
||||
|
||||
LazyColumn(modifier = Modifier.fillMaxSize(), state = itemsListState) {
|
||||
items(
|
||||
count = data.items.size,
|
||||
key = { index -> "library-item-${data.items[index].id}" },
|
||||
itemContent = { ix ->
|
||||
when (val item = data.items[ix]) {
|
||||
is LibraryView.LibraryTypeView -> {
|
||||
LibTypeItem(
|
||||
name = item.name,
|
||||
icon = item.icon,
|
||||
installed = item.dependentData is DependentData.Model,
|
||||
modifier = itemModifier,
|
||||
onClick = {
|
||||
vmEventStream.invoke(
|
||||
LibraryEvent.ToggleInstall.Type(item)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
is LibraryView.MyTypeView -> {
|
||||
MyTypeItem(
|
||||
name = item.name,
|
||||
icon = item.icon,
|
||||
readOnly = item.readOnly,
|
||||
modifier = itemModifier.clickable(item.editable) {
|
||||
vmEventStream.invoke(
|
||||
LibraryEvent.Type.Edit(item)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
is LibraryView.LibraryRelationView -> {
|
||||
LibRelationItem(
|
||||
modifier = itemModifier,
|
||||
name = item.name,
|
||||
format = item.format,
|
||||
installed = item.dependentData is DependentData.Model,
|
||||
onClick = {
|
||||
vmEventStream.invoke(
|
||||
LibraryEvent.ToggleInstall.Relation(item)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
is LibraryView.MyRelationView -> {
|
||||
MyRelationItem(
|
||||
modifier = itemModifier.clickable(item.editable) {
|
||||
vmEventStream.invoke(
|
||||
LibraryEvent.Relation.Edit(item)
|
||||
)
|
||||
},
|
||||
name = item.name,
|
||||
readOnly = item.readOnly,
|
||||
format = item.format
|
||||
)
|
||||
}
|
||||
is LibraryView.CreateNewTypeView -> {
|
||||
CreateNewTypeItem(
|
||||
modifier = itemModifier.clickable {
|
||||
vmEventStream.invoke(
|
||||
LibraryEvent.Type.Create(item.name)
|
||||
)
|
||||
},
|
||||
name = item.name
|
||||
)
|
||||
}
|
||||
is LibraryView.CreateNewRelationView -> {
|
||||
CreateNewRelationItem(
|
||||
modifier = itemModifier.clickable {
|
||||
vmEventStream.invoke(
|
||||
LibraryEvent.Relation.Create(item.name)
|
||||
)
|
||||
},
|
||||
name = item.name
|
||||
)
|
||||
}
|
||||
is LibraryView.LibraryTypesPlaceholderView -> {
|
||||
LibraryObjectEmptyItem(LibraryObjectTypes.TYPES.type, item.name)
|
||||
}
|
||||
is LibraryView.LibraryRelationsPlaceholderView -> {
|
||||
LibraryObjectEmptyItem(LibraryObjectTypes.RELATIONS.type, item.name)
|
||||
}
|
||||
is LibraryView.UnknownView -> {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
if (ix < data.items.lastIndex) {
|
||||
LibraryDivider()
|
||||
}
|
||||
if (ix == data.items.lastIndex && screenState.value.visible()) {
|
||||
Spacer(modifier = Modifier.height(48.dp))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun LibraryDivider() {
|
||||
Divider(
|
||||
thickness = LibraryListDefaults.DividerThickness,
|
||||
modifier = Modifier.padding(
|
||||
start = LibraryListDefaults.DividerPadding,
|
||||
end = LibraryListDefaults.DividerPadding
|
||||
),
|
||||
color = colorResource(id = R.color.shape_primary)
|
||||
)
|
||||
}
|
||||
|
||||
@Immutable
|
||||
internal object LibraryListDefaults {
|
||||
val ItemPadding = 20.dp
|
||||
val DividerPadding = 20.dp
|
||||
val DividerThickness = 0.5.dp
|
||||
val SearchBarPadding = 20.dp
|
||||
val SearchCancelPaddingStart = 8.dp
|
||||
val SearchCancelPaddingTop = 12.dp
|
||||
}
|
||||
|
||||
enum class LibraryObjectTypes(val type: String) {
|
||||
TYPES("types"), RELATIONS("relations")
|
||||
}
|
|
@ -1,290 +0,0 @@
|
|||
package com.anytypeio.anytype.ui.library.views.list.items
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import com.anytypeio.anytype.R
|
||||
import com.anytypeio.anytype.core_models.RelationFormat
|
||||
import com.anytypeio.anytype.core_ui.extensions.simpleIcon
|
||||
import com.anytypeio.anytype.core_ui.foundation.noRippleClickable
|
||||
|
||||
import com.anytypeio.anytype.core_ui.views.UXBody
|
||||
import com.anytypeio.anytype.core_ui.widgets.ObjectIconWidget
|
||||
import com.anytypeio.anytype.presentation.objects.ObjectIcon
|
||||
import com.anytypeio.anytype.ui.library.views.list.LibraryListDefaults
|
||||
import com.anytypeio.anytype.ui.library.views.list.items.ItemDefaults.TEXT_PADDING_START
|
||||
|
||||
@Composable
|
||||
fun MyTypeItem(
|
||||
name: String,
|
||||
icon: ObjectIcon?,
|
||||
readOnly: Boolean = false,
|
||||
modifier: Modifier
|
||||
) {
|
||||
Row(
|
||||
modifier.padding(
|
||||
start = LibraryListDefaults.ItemPadding,
|
||||
end = LibraryListDefaults.ItemPadding
|
||||
),
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(icon = icon)
|
||||
Text(
|
||||
text = name,
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
modifier = Modifier
|
||||
.padding(start = TEXT_PADDING_START),
|
||||
style = UXBody
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
if (readOnly) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_object_locked),
|
||||
contentDescription = "",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LibTypeItem(
|
||||
name: String,
|
||||
icon: ObjectIcon?,
|
||||
installed: Boolean = false,
|
||||
modifier: Modifier,
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
Row(
|
||||
modifier.padding(
|
||||
start = LibraryListDefaults.ItemPadding,
|
||||
end = LibraryListDefaults.ItemPadding
|
||||
),
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(icon = icon)
|
||||
Text(
|
||||
text = name,
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
modifier = Modifier
|
||||
.padding(start = TEXT_PADDING_START),
|
||||
style = UXBody
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
val installedImageRes = if (installed) {
|
||||
R.drawable.ic_type_installed
|
||||
} else {
|
||||
R.drawable.ic_type_not_installed
|
||||
}
|
||||
Image(
|
||||
painter = painterResource(id = installedImageRes),
|
||||
contentDescription = installedImageRes.toString(),
|
||||
modifier = Modifier.noRippleClickable(enabled = installed.not()) {
|
||||
onClick()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MyRelationItem(
|
||||
modifier: Modifier,
|
||||
name: String,
|
||||
format: RelationFormat,
|
||||
readOnly: Boolean = false,
|
||||
) {
|
||||
Row(
|
||||
modifier.padding(
|
||||
start = LibraryListDefaults.ItemPadding,
|
||||
end = LibraryListDefaults.ItemPadding
|
||||
),
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
format.simpleIcon()?.let {
|
||||
Image(painter = painterResource(id = it), contentDescription = "")
|
||||
}
|
||||
Text(
|
||||
text = name,
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
modifier = Modifier
|
||||
.padding(start = TEXT_PADDING_START),
|
||||
style = UXBody
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
if (readOnly) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_object_locked),
|
||||
contentDescription = ""
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LibRelationItem(
|
||||
modifier: Modifier,
|
||||
name: String,
|
||||
format: RelationFormat,
|
||||
installed: Boolean,
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
Row(
|
||||
modifier.padding(
|
||||
start = LibraryListDefaults.ItemPadding,
|
||||
end = LibraryListDefaults.ItemPadding
|
||||
),
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
format.simpleIcon()?.let {
|
||||
Image(painter = painterResource(id = it), contentDescription = "")
|
||||
}
|
||||
Text(
|
||||
text = name,
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
modifier = Modifier
|
||||
.padding(start = TEXT_PADDING_START),
|
||||
style = UXBody
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
val installedImageRes = if (installed) {
|
||||
R.drawable.ic_type_installed
|
||||
} else {
|
||||
R.drawable.ic_type_not_installed
|
||||
}
|
||||
Image(
|
||||
painter = painterResource(id = installedImageRes),
|
||||
contentDescription = installedImageRes.toString(),
|
||||
Modifier.noRippleClickable(enabled = installed.not()) {
|
||||
onClick()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CreateNewTypeItem(
|
||||
modifier: Modifier,
|
||||
name: String,
|
||||
) {
|
||||
Row(
|
||||
modifier.padding(
|
||||
start = LibraryListDefaults.ItemPadding,
|
||||
end = LibraryListDefaults.ItemPadding
|
||||
),
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_default_plus),
|
||||
contentDescription = "",
|
||||
)
|
||||
Text(
|
||||
text = stringResource(
|
||||
id = R.string.library_create_new_type,
|
||||
formatArgs = arrayOf(name)
|
||||
),
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
modifier = Modifier.padding(start = TEXT_PADDING_START),
|
||||
style = UXBody
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LibraryObjectEmptyItem(objectType: String, name: String) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 48.dp)
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(
|
||||
id = R.string.library_objects_empty,
|
||||
formatArgs = arrayOf(objectType, name)
|
||||
),
|
||||
style = UXBody,
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.padding(bottom = 9.dp)
|
||||
)
|
||||
Text(
|
||||
text = stringResource(
|
||||
id = R.string.library_objects_empty_action
|
||||
),
|
||||
color = colorResource(id = R.color.text_secondary),
|
||||
style = UXBody,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CreateNewRelationItem(
|
||||
modifier: Modifier,
|
||||
name: String,
|
||||
) {
|
||||
Row(
|
||||
modifier.padding(
|
||||
start = LibraryListDefaults.ItemPadding,
|
||||
end = LibraryListDefaults.ItemPadding
|
||||
),
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_default_plus),
|
||||
contentDescription = "",
|
||||
)
|
||||
Text(
|
||||
text = stringResource(
|
||||
id = R.string.library_create_new_relation,
|
||||
formatArgs = arrayOf(name)
|
||||
),
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
modifier = Modifier.padding(start = TEXT_PADDING_START),
|
||||
style = UXBody,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Icon(icon: ObjectIcon?) {
|
||||
icon?.let {
|
||||
AndroidView(
|
||||
factory = { ctx ->
|
||||
ObjectIconWidget(ctx)
|
||||
},
|
||||
modifier = Modifier.size(20.dp),
|
||||
update = {
|
||||
it.setIcon(icon)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Immutable
|
||||
object ItemDefaults {
|
||||
val ITEM_HEIGHT = 52.dp
|
||||
val TEXT_PADDING_START = 10.dp
|
||||
}
|
|
@ -1,107 +0,0 @@
|
|||
package com.anytypeio.anytype.ui.widgets.types
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.anytypeio.anytype.R
|
||||
import com.anytypeio.anytype.core_ui.views.HeadlineSubheading
|
||||
import com.anytypeio.anytype.presentation.home.InteractionMode
|
||||
import com.anytypeio.anytype.presentation.widgets.DropDownMenuAction
|
||||
import com.anytypeio.anytype.ui.widgets.menu.WidgetMenu
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun LibraryWidgetCard(
|
||||
mode: InteractionMode,
|
||||
onClick: () -> Unit,
|
||||
onDropDownMenuAction: (DropDownMenuAction) -> Unit,
|
||||
) {
|
||||
val haptic = LocalHapticFeedback.current
|
||||
val isCardMenuExpanded = remember {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
val isHeaderMenuExpanded = remember {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 20.dp, end = 20.dp, top = 6.dp, bottom = 6.dp)
|
||||
.alpha(if (isCardMenuExpanded.value || isHeaderMenuExpanded.value) 0.8f else 1f)
|
||||
.background(
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
color = colorResource(id = R.color.dashboard_card_background)
|
||||
)
|
||||
.then(
|
||||
if (mode is InteractionMode.Default)
|
||||
Modifier.combinedClickable(
|
||||
onClick = {
|
||||
onClick()
|
||||
},
|
||||
onLongClick = {
|
||||
isCardMenuExpanded.value = !isCardMenuExpanded.value
|
||||
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
|
||||
},
|
||||
indication = null,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
)
|
||||
else
|
||||
Modifier
|
||||
)
|
||||
) {
|
||||
Box(
|
||||
Modifier
|
||||
.padding(vertical = 6.dp)
|
||||
.height(40.dp)
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_widget_library),
|
||||
contentDescription = "Bin icon",
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterStart)
|
||||
.padding(start = 14.dp)
|
||||
)
|
||||
Text(
|
||||
text = stringResource(id = R.string.library),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterStart)
|
||||
.padding(start = 42.dp, end = 16.dp),
|
||||
style = HeadlineSubheading,
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
)
|
||||
}
|
||||
WidgetMenu(
|
||||
isExpanded = isCardMenuExpanded,
|
||||
onDropDownMenuAction = onDropDownMenuAction,
|
||||
canRemove = false,
|
||||
canChangeType = false,
|
||||
canChangeSource = false,
|
||||
canEmptyBin = false,
|
||||
canEditWidgets = mode is InteractionMode.Default,
|
||||
canAddBelow = false
|
||||
)
|
||||
}
|
||||
}
|
|
@ -460,32 +460,6 @@
|
|||
app:destination="@id/selectSpaceScreen"/>
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/libraryFragment"
|
||||
android:name="com.anytypeio.anytype.ui.library.LibraryFragment"
|
||||
android:label="LibraryFragment">
|
||||
<action
|
||||
android:id="@+id/openTypeCreationScreen"
|
||||
app:destination="@id/typeCreationFragment" />
|
||||
<action
|
||||
android:id="@+id/openTypeEditingScreen"
|
||||
app:destination="@id/typeEditingFragment" />
|
||||
<action
|
||||
android:id="@+id/openRelationCreationScreen"
|
||||
app:destination="@id/relationCreationFragment" />
|
||||
<action
|
||||
android:id="@+id/openRelationEditingScreen"
|
||||
app:destination="@id/relationEditingFragment" />
|
||||
<action
|
||||
android:id="@+id/actionOpenVault"
|
||||
app:destination="@id/vaultScreen"
|
||||
app:popUpTo="@id/vaultScreen"
|
||||
app:popUpToInclusive="true" />
|
||||
<action
|
||||
android:id="@+id/actionExitToSpaceWidgets"
|
||||
app:popUpTo="@+id/homeScreen"
|
||||
app:popUpToInclusive="false" />
|
||||
</fragment>
|
||||
<dialog
|
||||
android:id="@+id/typeCreationFragment"
|
||||
android:name="com.anytypeio.anytype.ui.types.create.CreateObjectTypeFragment"
|
||||
|
|
|
@ -18,7 +18,6 @@ import com.anytypeio.anytype.core_models.restrictions.ObjectRestriction
|
|||
import com.anytypeio.anytype.domain.all_content.RestoreAllContentState
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.feature_allcontent.presentation.AllContentViewModel.Companion.DEFAULT_INITIAL_TAB
|
||||
import com.anytypeio.anytype.presentation.library.DependentData
|
||||
import com.anytypeio.anytype.presentation.mapper.objectIcon
|
||||
import com.anytypeio.anytype.presentation.objects.ObjectIcon
|
||||
import com.anytypeio.anytype.presentation.objects.getDescriptionOrSnippet
|
||||
|
@ -144,8 +143,7 @@ sealed class UiContentItem {
|
|||
val sourceObject: Id? = null,
|
||||
val uniqueKey: Key? = null,
|
||||
val readOnly: Boolean = true,
|
||||
val editable: Boolean = true,
|
||||
val dependentData: DependentData = DependentData.None
|
||||
val editable: Boolean = true
|
||||
) : UiContentItem()
|
||||
|
||||
data class Relation(
|
||||
|
|
|
@ -60,7 +60,6 @@ import com.anytypeio.anytype.presentation.extension.sendAnalyticsAllContentToBin
|
|||
import com.anytypeio.anytype.presentation.extension.sendAnalyticsObjectCreateEvent
|
||||
import com.anytypeio.anytype.presentation.home.OpenObjectNavigation
|
||||
import com.anytypeio.anytype.presentation.home.navigation
|
||||
import com.anytypeio.anytype.presentation.library.LibraryViewModel.LibraryItem
|
||||
import com.anytypeio.anytype.presentation.objects.getCreateObjectParams
|
||||
import java.time.Instant
|
||||
import java.time.LocalDate
|
||||
|
@ -955,7 +954,7 @@ class AllContentViewModel(
|
|||
}
|
||||
}
|
||||
|
||||
fun uninstallObject(id: Id, type: LibraryItem, name: String) {
|
||||
fun uninstallObject(id: Id, tab: AllContentTab, name: String) {
|
||||
viewModelScope.launch {
|
||||
removeObjectsFromWorkspace.execute(
|
||||
RemoveObjectsFromWorkspace.Params(listOf(id))
|
||||
|
@ -965,13 +964,16 @@ class AllContentViewModel(
|
|||
commands.emit(Command.SendToast.Error("Error while uninstalling object"))
|
||||
},
|
||||
onSuccess = {
|
||||
when (type) {
|
||||
LibraryItem.TYPE -> {
|
||||
when (tab) {
|
||||
AllContentTab.TYPES -> {
|
||||
commands.emit(Command.SendToast.TypeRemoved(name))
|
||||
}
|
||||
LibraryItem.RELATION -> {
|
||||
AllContentTab.RELATIONS -> {
|
||||
commands.emit(Command.SendToast.RelationRemoved(name))
|
||||
}
|
||||
else -> {
|
||||
//do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
@ -1014,4 +1016,8 @@ class AllContentViewModel(
|
|||
val DEFAULT_INITIAL_TAB = AllContentTab.PAGES
|
||||
val DEFAULT_QUERY = ""
|
||||
}
|
||||
|
||||
enum class LibraryItem {
|
||||
TYPE, RELATION
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1775,19 +1775,6 @@ class HomeScreenViewModel(
|
|||
}
|
||||
}
|
||||
|
||||
fun onLibraryClicked() {
|
||||
viewModelScope.launch {
|
||||
val space = spaceManager.get()
|
||||
if (space.isNotEmpty()) {
|
||||
navigation(
|
||||
Navigation.OpenLibrary(space)
|
||||
)
|
||||
} else {
|
||||
Timber.w("Space is missing: ${space}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onSearchIconClicked() {
|
||||
viewModelScope.launch {
|
||||
commands.emit(
|
||||
|
@ -2127,7 +2114,6 @@ class HomeScreenViewModel(
|
|||
data class OpenSet(val ctx: Id, val space: Id, val view: Id?) : Navigation()
|
||||
data class ExpandWidget(val subscription: Subscription, val space: Id) : Navigation()
|
||||
data object OpenSpaceSwitcher: Navigation()
|
||||
data class OpenLibrary(val space: Id) : Navigation()
|
||||
data class OpenAllContent(val space: Id) : Navigation()
|
||||
}
|
||||
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
package com.anytypeio.anytype.presentation.library
|
||||
|
||||
sealed class LibraryEvent {
|
||||
|
||||
sealed class BottomMenu : LibraryEvent() {
|
||||
object Back : BottomMenu()
|
||||
object Search : BottomMenu()
|
||||
object CreateObject : BottomMenu()
|
||||
object OpenProfile : BottomMenu()
|
||||
}
|
||||
|
||||
sealed class Query(open val query: String) : LibraryEvent() {
|
||||
class MyTypes(override val query: String) : Query(query)
|
||||
class LibraryTypes(override val query: String) : Query(query)
|
||||
class MyRelations(override val query: String) : Query(query)
|
||||
class LibraryRelations(override val query: String) : Query(query)
|
||||
}
|
||||
|
||||
sealed class ToggleInstall(open val item: LibraryView) : LibraryEvent() {
|
||||
class Type(override val item: LibraryView) : ToggleInstall(item)
|
||||
class Relation(override val item: LibraryView) : ToggleInstall(item)
|
||||
}
|
||||
|
||||
sealed class Type : LibraryEvent() {
|
||||
class Create(val name: String = "") : Type()
|
||||
class Edit(val item: LibraryView.MyTypeView) : Type()
|
||||
}
|
||||
|
||||
sealed class Relation : LibraryEvent() {
|
||||
class Create(val name: String = "") : Relation()
|
||||
class Edit(val item: LibraryView.MyRelationView) : Relation()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sealed class LibraryAnalyticsEvent {
|
||||
sealed class Ui {
|
||||
object Idle : Ui()
|
||||
sealed class TabView(val type: String, val view: String) : Ui() {
|
||||
object Types : TabView(type = TypeType, view = ViewYour)
|
||||
object Relations : TabView(type = TypeRelation, view = ViewYour)
|
||||
object LibTypes : TabView(type = TypeType, view = ViewLibrary)
|
||||
object LibRelations : TabView(type = TypeRelation, view = ViewLibrary)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const val TypeType = "type"
|
||||
private const val TypeRelation = "relation"
|
||||
private const val ViewYour = "your"
|
||||
private const val ViewLibrary = "library"
|
|
@ -1,30 +0,0 @@
|
|||
package com.anytypeio.anytype.presentation.library
|
||||
|
||||
import com.anytypeio.anytype.core_models.ObjectWrapper
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.debounce
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
|
||||
interface LibraryListDelegate {
|
||||
|
||||
val queryFlow: MutableStateFlow<String>
|
||||
val itemsFlow: Flow<LibraryScreenState.Tabs.TabData>
|
||||
|
||||
fun itemsFlow(): Flow<List<ObjectWrapper.Basic>>
|
||||
|
||||
@FlowPreview
|
||||
fun queryFlow() = queryFlow
|
||||
.debounce(DEBOUNCE_TIMEOUT)
|
||||
.distinctUntilChanged()
|
||||
|
||||
suspend fun unsubscribe()
|
||||
|
||||
}
|
||||
|
||||
fun List<LibraryView>.filterByQuery(query: String): List<LibraryView> {
|
||||
return filter { it.name.contains(query.trim(), true) }
|
||||
}
|
||||
|
||||
private const val DEBOUNCE_TIMEOUT = 100L
|
|
@ -1,17 +0,0 @@
|
|||
package com.anytypeio.anytype.presentation.library
|
||||
|
||||
interface QueryListenerMyTypes {
|
||||
fun onQueryMyTypes(string: String)
|
||||
}
|
||||
|
||||
interface QueryListenerMyRelations {
|
||||
fun onQueryMyRelations(string: String)
|
||||
}
|
||||
|
||||
interface QueryListenerLibTypes {
|
||||
fun onQueryLibTypes(string: String)
|
||||
}
|
||||
|
||||
interface QueryListenerLibRelations {
|
||||
fun onQueryLibRelations(string: String)
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
package com.anytypeio.anytype.presentation.library
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Resources
|
||||
import com.anytypeio.anytype.presentation.R
|
||||
import javax.inject.Inject
|
||||
|
||||
interface LibraryResourceManager {
|
||||
|
||||
fun messageRelationAdded(name: String): String
|
||||
fun messageRelationRemoved(name: String): String
|
||||
fun messageTypeAdded(name: String): String
|
||||
fun messageTypeRemoved(name: String): String
|
||||
|
||||
val errorMessage: String
|
||||
|
||||
class Impl @Inject constructor(
|
||||
val context: Context
|
||||
) : LibraryResourceManager {
|
||||
|
||||
private val resources: Resources = context.resources
|
||||
|
||||
override fun messageRelationAdded(name: String) =
|
||||
resources.getString(R.string.library_relation_added, name)
|
||||
|
||||
override fun messageRelationRemoved(name: String) =
|
||||
resources.getString(R.string.library_relation_removed, name)
|
||||
|
||||
override fun messageTypeAdded(name: String) =
|
||||
resources.getString(R.string.library_type_added, name)
|
||||
|
||||
|
||||
override fun messageTypeRemoved(name: String) =
|
||||
resources.getString(R.string.library_type_removed, name)
|
||||
|
||||
override val errorMessage: String =
|
||||
resources.getString(R.string.library_something_went_wrong)
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
package com.anytypeio.anytype.presentation.library
|
||||
|
||||
|
||||
class LibraryScreenState(
|
||||
val types: Tabs.Types,
|
||||
val relations: Tabs.Relations
|
||||
) {
|
||||
sealed class Tabs(
|
||||
val my: TabData,
|
||||
val lib: TabData
|
||||
) {
|
||||
class Types(
|
||||
myTypes: TabData,
|
||||
libTypes: TabData
|
||||
) : Tabs(myTypes, libTypes)
|
||||
|
||||
class Relations(
|
||||
myRelations: TabData,
|
||||
libRelations: TabData
|
||||
) : Tabs(myRelations, libRelations)
|
||||
|
||||
data class TabData(
|
||||
val items: List<LibraryView> = emptyList()
|
||||
)
|
||||
|
||||
}
|
||||
}
|
|
@ -1,89 +0,0 @@
|
|||
package com.anytypeio.anytype.presentation.library
|
||||
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.Key
|
||||
import com.anytypeio.anytype.core_models.RelationFormat
|
||||
import com.anytypeio.anytype.presentation.objects.ObjectIcon
|
||||
|
||||
sealed interface DependentData {
|
||||
|
||||
class Model(
|
||||
val item: LibraryView,
|
||||
) : DependentData
|
||||
|
||||
object None : DependentData
|
||||
|
||||
}
|
||||
|
||||
sealed interface LibraryView {
|
||||
val id: Id
|
||||
val name: String
|
||||
val dependentData: DependentData
|
||||
|
||||
class MyTypeView(
|
||||
override val id: Id,
|
||||
override val name: String,
|
||||
val icon: ObjectIcon? = null,
|
||||
val sourceObject: Id? = null,
|
||||
val uniqueKey: Key? = null,
|
||||
val readOnly: Boolean = true,
|
||||
val editable: Boolean = true,
|
||||
override val dependentData: DependentData = DependentData.None
|
||||
) : LibraryView
|
||||
|
||||
data class LibraryTypeView(
|
||||
override val id: Id,
|
||||
override val name: String,
|
||||
val icon: ObjectIcon? = null,
|
||||
val uniqueKey: Key? = null,
|
||||
override val dependentData: DependentData = DependentData.None
|
||||
) : LibraryView
|
||||
|
||||
class MyRelationView(
|
||||
override val id: Id,
|
||||
override val name: String,
|
||||
val format: RelationFormat,
|
||||
val sourceObject: Id? = null,
|
||||
val readOnly: Boolean = true,
|
||||
val editable: Boolean = true,
|
||||
override val dependentData: DependentData = DependentData.None
|
||||
) : LibraryView
|
||||
|
||||
data class LibraryRelationView(
|
||||
override val id: Id,
|
||||
override val name: String,
|
||||
val format: RelationFormat,
|
||||
override val dependentData: DependentData = DependentData.None
|
||||
) : LibraryView
|
||||
|
||||
class UnknownView(
|
||||
override val id: Id,
|
||||
override val name: String = "",
|
||||
override val dependentData: DependentData = DependentData.None
|
||||
) : LibraryView
|
||||
|
||||
class CreateNewTypeView(
|
||||
override val id: Id = "",
|
||||
override val name: String = "",
|
||||
override val dependentData: DependentData = DependentData.None
|
||||
) : LibraryView
|
||||
|
||||
class CreateNewRelationView(
|
||||
override val id: Id = "",
|
||||
override val name: String = "",
|
||||
override val dependentData: DependentData = DependentData.None
|
||||
) : LibraryView
|
||||
|
||||
class LibraryTypesPlaceholderView(
|
||||
override val id: Id = "",
|
||||
override val name: String = "",
|
||||
override val dependentData: DependentData = DependentData.None,
|
||||
) : LibraryView
|
||||
|
||||
class LibraryRelationsPlaceholderView(
|
||||
override val id: Id = "",
|
||||
override val name: String = "",
|
||||
override val dependentData: DependentData = DependentData.None,
|
||||
) : LibraryView
|
||||
|
||||
}
|
|
@ -1,584 +0,0 @@
|
|||
package com.anytypeio.anytype.presentation.library
|
||||
|
||||
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.EventsDictionary.libraryScreenRelation
|
||||
import com.anytypeio.anytype.analytics.base.EventsDictionary.libraryScreenType
|
||||
import com.anytypeio.anytype.analytics.base.EventsDictionary.libraryView
|
||||
import com.anytypeio.anytype.analytics.base.sendEvent
|
||||
import com.anytypeio.anytype.analytics.props.Props
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.ObjectWrapper
|
||||
import com.anytypeio.anytype.core_models.Relations
|
||||
import com.anytypeio.anytype.core_models.primitives.SpaceId
|
||||
import com.anytypeio.anytype.core_utils.ext.allUniqueBy
|
||||
import com.anytypeio.anytype.core_utils.ext.orNull
|
||||
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
|
||||
import com.anytypeio.anytype.domain.base.fold
|
||||
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.`object`.SetObjectDetails
|
||||
import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
|
||||
import com.anytypeio.anytype.domain.page.CreateObject
|
||||
import com.anytypeio.anytype.domain.workspace.AddObjectToWorkspace
|
||||
import com.anytypeio.anytype.domain.workspace.RemoveObjectsFromWorkspace
|
||||
import com.anytypeio.anytype.domain.workspace.SpaceManager
|
||||
import com.anytypeio.anytype.presentation.BuildConfig
|
||||
import com.anytypeio.anytype.presentation.analytics.AnalyticSpaceHelperDelegate
|
||||
import com.anytypeio.anytype.presentation.extension.sendAnalyticsObjectCreateEvent
|
||||
import com.anytypeio.anytype.presentation.home.HomeScreenViewModel.Companion.HOME_SCREEN_PROFILE_OBJECT_SUBSCRIPTION
|
||||
import com.anytypeio.anytype.presentation.home.OpenObjectNavigation
|
||||
import com.anytypeio.anytype.presentation.home.navigation
|
||||
import com.anytypeio.anytype.presentation.library.delegates.LibraryRelationsDelegate
|
||||
import com.anytypeio.anytype.presentation.library.delegates.LibraryTypesDelegate
|
||||
import com.anytypeio.anytype.presentation.library.delegates.MyRelationsDelegate
|
||||
import com.anytypeio.anytype.presentation.library.delegates.MyTypesDelegate
|
||||
import com.anytypeio.anytype.presentation.navigation.NavigationViewModel
|
||||
import com.anytypeio.anytype.presentation.objects.getCreateObjectParams
|
||||
import com.anytypeio.anytype.presentation.profile.ProfileIconView
|
||||
import com.anytypeio.anytype.presentation.profile.profileIcon
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.collectIndexed
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.filterIsInstance
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
|
||||
class LibraryViewModel(
|
||||
private val params: Params,
|
||||
private val myTypesDelegate: MyTypesDelegate,
|
||||
private val libraryTypesDelegate: LibraryTypesDelegate,
|
||||
private val myRelationsDelegate: MyRelationsDelegate,
|
||||
private val libraryRelationsDelegate: LibraryRelationsDelegate,
|
||||
private val addObjectToWorkspace: AddObjectToWorkspace,
|
||||
private val removeObjectsFromWorkspace: RemoveObjectsFromWorkspace,
|
||||
private val resourceManager: LibraryResourceManager,
|
||||
private val setObjectDetails: SetObjectDetails,
|
||||
private val createObject: CreateObject,
|
||||
private val analytics: Analytics,
|
||||
private val spaceManager: SpaceManager,
|
||||
private val storelessSubscriptionContainer: StorelessSubscriptionContainer,
|
||||
private val appCoroutineDispatchers: AppCoroutineDispatchers,
|
||||
private val urlBuilder: UrlBuilder,
|
||||
private val storeOfObjectTypes: StoreOfObjectTypes,
|
||||
private val analyticSpaceHelperDelegate: AnalyticSpaceHelperDelegate
|
||||
) : NavigationViewModel<LibraryViewModel.Navigation>(), AnalyticSpaceHelperDelegate by analyticSpaceHelperDelegate {
|
||||
|
||||
val icon = MutableStateFlow<ProfileIconView>(ProfileIconView.Loading)
|
||||
|
||||
private val uiEvents = MutableStateFlow<LibraryEvent>(LibraryEvent.Query.MyTypes(""))
|
||||
private val analyticsEvents = MutableStateFlow<LibraryAnalyticsEvent.Ui>(
|
||||
LibraryAnalyticsEvent.Ui.Idle
|
||||
)
|
||||
|
||||
val effects = MutableStateFlow<Effect>(Effect.Idle)
|
||||
|
||||
val uiState: StateFlow<LibraryScreenState> = combine(
|
||||
myTypesDelegate.itemsFlow,
|
||||
libraryTypesDelegate.itemsFlow,
|
||||
myRelationsDelegate.itemsFlow,
|
||||
libraryRelationsDelegate.itemsFlow
|
||||
) { myTypes, libTypes, myRel, libRel ->
|
||||
|
||||
val libTypesItems = updateInstalledValueForTypes(
|
||||
libTypes,
|
||||
myTypes
|
||||
)
|
||||
val libRelItems = updateInstalledValueForRelations(
|
||||
libRel,
|
||||
myRel
|
||||
)
|
||||
|
||||
LibraryScreenState(
|
||||
types = LibraryScreenState.Tabs.Types(myTypes, libTypesItems),
|
||||
relations = LibraryScreenState.Tabs.Relations(myRel, libRelItems)
|
||||
)
|
||||
|
||||
}.stateIn(
|
||||
scope = viewModelScope,
|
||||
started = SharingStarted.WhileSubscribed(STOP_SUBSCRIPTION_TIMEOUT),
|
||||
initialValue = LibraryScreenState(
|
||||
types = LibraryScreenState.Tabs.Types(
|
||||
myTypes = LibraryScreenState.Tabs.TabData(),
|
||||
libTypes = LibraryScreenState.Tabs.TabData()
|
||||
),
|
||||
relations = LibraryScreenState.Tabs.Relations(
|
||||
myRelations = LibraryScreenState.Tabs.TabData(),
|
||||
libRelations = LibraryScreenState.Tabs.TabData()
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
init {
|
||||
Timber.i("LibraryViewModel, init")
|
||||
proceedWithObservingProfileIcon()
|
||||
viewModelScope.launch {
|
||||
uiEvents.collect {
|
||||
when (it) {
|
||||
is LibraryEvent.Query -> proceedQueryEvent(it)
|
||||
is LibraryEvent.ToggleInstall -> proceedWithToggleInstall(it.item)
|
||||
is LibraryEvent.Type -> proceedWithTypeActions(it)
|
||||
is LibraryEvent.Relation -> proceedWithRelationActions(it)
|
||||
is LibraryEvent.BottomMenu -> proceedWithBottomMenuActions(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
viewModelScope.launch {
|
||||
analyticsEvents.filterIsInstance<LibraryAnalyticsEvent.Ui.TabView>()
|
||||
.collectIndexed { index, it ->
|
||||
val route = if (index == 0) ROUTE_OUTER else ROUTE_INNER
|
||||
proceedWithViewAnalytics(it, route)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun proceedWithObservingProfileIcon() {
|
||||
viewModelScope.launch {
|
||||
spaceManager
|
||||
.observe()
|
||||
.flatMapLatest { config ->
|
||||
storelessSubscriptionContainer.subscribe(
|
||||
StoreSearchByIdsParams(
|
||||
space = SpaceId(config.techSpace),
|
||||
subscription = HOME_SCREEN_PROFILE_OBJECT_SUBSCRIPTION,
|
||||
targets = listOf(config.profile),
|
||||
keys = listOf(
|
||||
Relations.ID,
|
||||
Relations.NAME,
|
||||
Relations.ICON_EMOJI,
|
||||
Relations.ICON_IMAGE,
|
||||
Relations.ICON_OPTION
|
||||
)
|
||||
)
|
||||
).map { result ->
|
||||
val obj = result.firstOrNull()
|
||||
obj?.profileIcon(urlBuilder) ?: ProfileIconView.Placeholder(null)
|
||||
}
|
||||
}
|
||||
.catch { Timber.e(it, "Error while observing space icon") }
|
||||
.flowOn(appCoroutineDispatchers.io)
|
||||
.collect { icon.value = it }
|
||||
}
|
||||
}
|
||||
|
||||
private fun proceedWithViewAnalytics(it: LibraryAnalyticsEvent.Ui.TabView, route: String) {
|
||||
viewModelScope.sendEvent(
|
||||
analytics = analytics,
|
||||
eventName = libraryView,
|
||||
props = Props(
|
||||
mapOf(
|
||||
"type" to it.type,
|
||||
"view" to it.view,
|
||||
"route" to route
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun proceedWithBottomMenuActions(it: LibraryEvent.BottomMenu) {
|
||||
when (it) {
|
||||
is LibraryEvent.BottomMenu.Back -> navigate(Navigation.Back())
|
||||
is LibraryEvent.BottomMenu.Search -> navigate(Navigation.Search())
|
||||
is LibraryEvent.BottomMenu.CreateObject -> proceedWithCreateDoc()
|
||||
is LibraryEvent.BottomMenu.OpenProfile -> navigate(Navigation.ExitToVault)
|
||||
}
|
||||
}
|
||||
|
||||
fun onCreateObjectOfTypeClicked(objType: ObjectWrapper.Type) {
|
||||
proceedWithCreateDoc(objType)
|
||||
}
|
||||
|
||||
private fun proceedWithCreateDoc(
|
||||
objType: ObjectWrapper.Type? = null
|
||||
) {
|
||||
val startTime = System.currentTimeMillis()
|
||||
viewModelScope.launch {
|
||||
val params = objType?.uniqueKey.getCreateObjectParams(
|
||||
space = SpaceId(spaceManager.get()),
|
||||
objType?.defaultTemplateId
|
||||
)
|
||||
createObject.async(params).fold(
|
||||
onSuccess = {
|
||||
result -> proceedWithOpeningObject(result.obj)
|
||||
sendAnalyticsObjectCreateEvent(
|
||||
analytics = analytics,
|
||||
route = EventsDictionary.Routes.allContentRoute,
|
||||
startTime = startTime,
|
||||
objType = objType ?: storeOfObjectTypes.getByKey(result.typeKey.key),
|
||||
view = EventsDictionary.View.viewHome,
|
||||
spaceParams = provideParams(spaceManager.get())
|
||||
)
|
||||
},
|
||||
onFailure = { e -> Timber.e(e, "Error while creating a new object") }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun proceedWithOpeningObject(obj: ObjectWrapper.Basic) {
|
||||
when (val navigation = obj.navigation()) {
|
||||
is OpenObjectNavigation.OpenDataView -> {
|
||||
navigate(Navigation.OpenSetOrCollection(navigation.target))
|
||||
}
|
||||
is OpenObjectNavigation.OpenEditor -> {
|
||||
navigate(Navigation.OpenEditor(navigation.target))
|
||||
}
|
||||
is OpenObjectNavigation.OpenDiscussion -> {
|
||||
sendToast("not implemented")
|
||||
}
|
||||
is OpenObjectNavigation.UnexpectedLayoutError -> {
|
||||
sendToast("Unexpected layout: ${navigation.layout}")
|
||||
}
|
||||
OpenObjectNavigation.NonValidObject -> {
|
||||
sendToast("Object id is missing")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun proceedQueryEvent(event: LibraryEvent.Query) {
|
||||
when (event) {
|
||||
is LibraryEvent.Query.MyTypes -> {
|
||||
myTypesDelegate.onQueryMyTypes(event.query)
|
||||
}
|
||||
is LibraryEvent.Query.LibraryTypes -> {
|
||||
libraryTypesDelegate.onQueryLibTypes(event.query)
|
||||
}
|
||||
is LibraryEvent.Query.MyRelations -> {
|
||||
myRelationsDelegate.onQueryMyRelations(event.query)
|
||||
}
|
||||
is LibraryEvent.Query.LibraryRelations -> {
|
||||
libraryRelationsDelegate.onQueryLibRelations(event.query)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun proceedWithToggleInstall(item: LibraryView) {
|
||||
when (val dependentData = item.dependentData) {
|
||||
is DependentData.Model -> uninstallObject(item, dependentData.item.id)
|
||||
is DependentData.None -> installObject(item)
|
||||
}
|
||||
}
|
||||
|
||||
private fun proceedWithTypeActions(event: LibraryEvent.Type) {
|
||||
when (event) {
|
||||
is LibraryEvent.Type.Create -> {
|
||||
navigate(Navigation.OpenTypeCreation(event.name))
|
||||
}
|
||||
is LibraryEvent.Type.Edit -> {
|
||||
viewModelScope.sendEvent(
|
||||
analytics = analytics,
|
||||
eventName = libraryScreenType,
|
||||
props = Props(
|
||||
map = mapOf("objectType" to event.item.id)
|
||||
)
|
||||
)
|
||||
navigate(Navigation.OpenTypeEditing(event.item))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun proceedWithRelationActions(event: LibraryEvent.Relation) {
|
||||
when (event) {
|
||||
is LibraryEvent.Relation.Create -> navigate(Navigation.OpenRelationCreation(event.name))
|
||||
is LibraryEvent.Relation.Edit -> {
|
||||
viewModelScope.sendEvent(
|
||||
analytics = analytics,
|
||||
eventName = libraryScreenRelation,
|
||||
props = Props(
|
||||
map = mapOf("relationKey" to event.item.id)
|
||||
)
|
||||
)
|
||||
navigate(Navigation.OpenRelationEditing(event.item))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun installObject(item: LibraryView) {
|
||||
viewModelScope.launch {
|
||||
addObjectToWorkspace(
|
||||
AddObjectToWorkspace.Params(
|
||||
space = spaceManager.get(),
|
||||
objects = listOf (item.id)
|
||||
)
|
||||
).proceed(
|
||||
success = {
|
||||
when (item) {
|
||||
is LibraryView.LibraryRelationView -> {
|
||||
sendToast(resourceManager.messageRelationAdded(item.name))
|
||||
}
|
||||
is LibraryView.LibraryTypeView -> {
|
||||
sendToast(resourceManager.messageTypeAdded(item.name))
|
||||
}
|
||||
else -> {
|
||||
Timber.e("Unsupported item type: $item")
|
||||
}
|
||||
}
|
||||
},
|
||||
failure = {
|
||||
Timber.e(it, "Error while adding relation to workspace.")
|
||||
sendToast(resourceManager.errorMessage)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun uninstallObject(item: LibraryView, id: Id) {
|
||||
viewModelScope.launch {
|
||||
removeObjectsFromWorkspace.execute(
|
||||
RemoveObjectsFromWorkspace.Params(listOf(id))
|
||||
).fold(
|
||||
onFailure = {
|
||||
Timber.e(it, "Error while removing relation from workspace.")
|
||||
sendToast(resourceManager.errorMessage)
|
||||
},
|
||||
onSuccess = {
|
||||
when (item) {
|
||||
is LibraryView.LibraryRelationView -> {
|
||||
sendToast(resourceManager.messageRelationRemoved(item.name))
|
||||
}
|
||||
is LibraryView.LibraryTypeView -> {
|
||||
sendToast(resourceManager.messageTypeRemoved(item.name))
|
||||
}
|
||||
else -> {
|
||||
Timber.e("Unsupported item type: $item")
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun uninstallObject(id: Id, type: LibraryItem, name: String) {
|
||||
viewModelScope.launch {
|
||||
removeObjectsFromWorkspace.execute(
|
||||
RemoveObjectsFromWorkspace.Params(listOf(id))
|
||||
).fold(
|
||||
onFailure = {
|
||||
Timber.e(it, "Error while uninstalling object")
|
||||
sendToast(resourceManager.errorMessage)
|
||||
},
|
||||
onSuccess = {
|
||||
val message = when (type) {
|
||||
LibraryItem.TYPE -> resourceManager.messageTypeRemoved(name)
|
||||
LibraryItem.RELATION -> resourceManager.messageRelationRemoved(name)
|
||||
}
|
||||
sendToast(message)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun eventStream(event: LibraryEvent) {
|
||||
uiEvents.value = event
|
||||
}
|
||||
|
||||
fun analyticsStream(event: LibraryAnalyticsEvent.Ui) {
|
||||
analyticsEvents.value = event
|
||||
}
|
||||
|
||||
private fun updateInstalledValueForTypes(
|
||||
libTypes: LibraryScreenState.Tabs.TabData,
|
||||
myTypes: LibraryScreenState.Tabs.TabData
|
||||
): LibraryScreenState.Tabs.TabData {
|
||||
if (BuildConfig.DEBUG) {
|
||||
assert(libTypes.items.allUniqueBy { it.id })
|
||||
assert(myTypes.items.allUniqueBy { it.id })
|
||||
}
|
||||
val myTypeViews = myTypes
|
||||
.items
|
||||
.filterIsInstance<LibraryView.MyTypeView>()
|
||||
|
||||
return libTypes.copy(
|
||||
items = libTypes.items.map { libType ->
|
||||
if (libType is LibraryView.LibraryTypeView) {
|
||||
with(
|
||||
myTypeViews.find { it.uniqueKey == libType.uniqueKey }
|
||||
) {
|
||||
libType.copy(
|
||||
dependentData = if (this != null) {
|
||||
DependentData.Model(item = this)
|
||||
} else {
|
||||
DependentData.None
|
||||
}
|
||||
)
|
||||
}
|
||||
} else {
|
||||
libType
|
||||
}
|
||||
}.distinctBy { view -> view.id }
|
||||
)
|
||||
}
|
||||
|
||||
private fun updateInstalledValueForRelations(
|
||||
libRelations: LibraryScreenState.Tabs.TabData,
|
||||
myRelations: LibraryScreenState.Tabs.TabData
|
||||
): LibraryScreenState.Tabs.TabData {
|
||||
if (BuildConfig.DEBUG) {
|
||||
assert(libRelations.items.allUniqueBy { it.id })
|
||||
assert(myRelations.items.allUniqueBy { it.id })
|
||||
}
|
||||
|
||||
val updatedLibraryRelations = updateLibraryRelationItems(
|
||||
libraryItems = libRelations.items,
|
||||
myRelationItems = myRelations.items
|
||||
)
|
||||
return libRelations.copy(
|
||||
items = updatedLibraryRelations.distinctBy { view -> view.id }
|
||||
)
|
||||
}
|
||||
|
||||
private fun updateLibraryRelationItems(
|
||||
libraryItems: List<LibraryView>,
|
||||
myRelationItems: List<LibraryView>
|
||||
): List<LibraryView> {
|
||||
return libraryItems.map { libraryItem ->
|
||||
if (libraryItem !is LibraryView.LibraryRelationView) {
|
||||
return@map libraryItem
|
||||
}
|
||||
val relationInstalled = myRelationItems.firstOrNull { myRelationItem ->
|
||||
(myRelationItem as? LibraryView.MyRelationView)?.sourceObject == libraryItem.id
|
||||
}
|
||||
val dependedData = if (relationInstalled != null) {
|
||||
DependentData.Model(item = relationInstalled)
|
||||
} else {
|
||||
DependentData.None
|
||||
}
|
||||
libraryItem.copy(dependentData = dependedData)
|
||||
}
|
||||
}
|
||||
|
||||
fun updateObject(id: String, name: String, icon: String?) {
|
||||
viewModelScope.launch {
|
||||
setObjectDetails.execute(
|
||||
SetObjectDetails.Params(
|
||||
ctx = id,
|
||||
details = mapOf(
|
||||
Relations.NAME to name,
|
||||
Relations.ICON_EMOJI to icon.orNull(),
|
||||
)
|
||||
)
|
||||
).fold(
|
||||
onFailure = {
|
||||
Timber.e(it, "Error while updating object details")
|
||||
sendToast(resourceManager.errorMessage)
|
||||
},
|
||||
onSuccess = {
|
||||
// do nothing
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun onObjectCreated() {
|
||||
effects.value = Effect.ObjectCreated()
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
GlobalScope.launch {
|
||||
myRelationsDelegate.unsubscribe()
|
||||
libraryRelationsDelegate.unsubscribe()
|
||||
myTypesDelegate.unsubscribe()
|
||||
libraryTypesDelegate.unsubscribe()
|
||||
}
|
||||
super.onCleared()
|
||||
}
|
||||
|
||||
class Factory @Inject constructor(
|
||||
private val params: Params,
|
||||
private val myTypesDelegate: MyTypesDelegate,
|
||||
private val libraryTypesDelegate: LibraryTypesDelegate,
|
||||
private val myRelationsDelegate: MyRelationsDelegate,
|
||||
private val libraryRelationsDelegate: LibraryRelationsDelegate,
|
||||
private val addObjectToWorkspace: AddObjectToWorkspace,
|
||||
private val removeObjectsFromWorkspace: RemoveObjectsFromWorkspace,
|
||||
private val resourceManager: LibraryResourceManager,
|
||||
private val setObjectDetails: SetObjectDetails,
|
||||
private val createObject: CreateObject,
|
||||
private val analytics: Analytics,
|
||||
private val spaceManager: SpaceManager,
|
||||
private val storelessSubscriptionContainer: StorelessSubscriptionContainer,
|
||||
private val appCoroutineDispatchers: AppCoroutineDispatchers,
|
||||
private val urlBuilder: UrlBuilder,
|
||||
private val storeOfObjectTypes: StoreOfObjectTypes,
|
||||
private val analyticSpaceHelperDelegate: AnalyticSpaceHelperDelegate
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return LibraryViewModel(
|
||||
params = params,
|
||||
myTypesDelegate = myTypesDelegate,
|
||||
libraryTypesDelegate = libraryTypesDelegate,
|
||||
myRelationsDelegate = myRelationsDelegate,
|
||||
libraryRelationsDelegate = libraryRelationsDelegate,
|
||||
addObjectToWorkspace = addObjectToWorkspace,
|
||||
removeObjectsFromWorkspace = removeObjectsFromWorkspace,
|
||||
resourceManager = resourceManager,
|
||||
setObjectDetails = setObjectDetails,
|
||||
createObject = createObject,
|
||||
analytics = analytics,
|
||||
spaceManager = spaceManager,
|
||||
storelessSubscriptionContainer = storelessSubscriptionContainer,
|
||||
appCoroutineDispatchers = appCoroutineDispatchers,
|
||||
urlBuilder = urlBuilder,
|
||||
storeOfObjectTypes = storeOfObjectTypes,
|
||||
analyticSpaceHelperDelegate = analyticSpaceHelperDelegate
|
||||
) as T
|
||||
}
|
||||
}
|
||||
|
||||
class Params(val space: SpaceId)
|
||||
|
||||
sealed class Navigation {
|
||||
class OpenTypeCreation(
|
||||
val name: String = ""
|
||||
) : Navigation()
|
||||
|
||||
class OpenRelationCreation(
|
||||
val name: String = ""
|
||||
) : Navigation()
|
||||
|
||||
class OpenTypeEditing(
|
||||
val view: LibraryView.MyTypeView
|
||||
) : Navigation()
|
||||
|
||||
class OpenRelationEditing(
|
||||
val view: LibraryView.MyRelationView
|
||||
) : Navigation()
|
||||
|
||||
object ExitToVault: Navigation()
|
||||
|
||||
class Back : Navigation()
|
||||
|
||||
class Search : Navigation()
|
||||
|
||||
class OpenEditor(val id: Id) : Navigation()
|
||||
|
||||
class OpenSetOrCollection(val id: Id) : Navigation()
|
||||
}
|
||||
|
||||
sealed class Effect {
|
||||
class ObjectCreated : Effect()
|
||||
object Idle : Effect()
|
||||
}
|
||||
|
||||
enum class LibraryItem {
|
||||
TYPE, RELATION
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private const val STOP_SUBSCRIPTION_TIMEOUT = 1_000L
|
||||
private const val ROUTE_INNER = "inner"
|
||||
private const val ROUTE_OUTER = "outer"
|
|
@ -1,74 +0,0 @@
|
|||
package com.anytypeio.anytype.presentation.library.delegates
|
||||
|
||||
import com.anytypeio.anytype.core_models.Marketplace.MARKETPLACE_SPACE_ID
|
||||
import com.anytypeio.anytype.core_models.primitives.SpaceId
|
||||
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
|
||||
import com.anytypeio.anytype.domain.library.StoreSearchParams
|
||||
import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.presentation.library.LibraryListDelegate
|
||||
import com.anytypeio.anytype.presentation.library.LibraryScreenState
|
||||
import com.anytypeio.anytype.presentation.library.LibraryView
|
||||
import com.anytypeio.anytype.presentation.library.QueryListenerLibRelations
|
||||
import com.anytypeio.anytype.presentation.library.filterByQuery
|
||||
import com.anytypeio.anytype.presentation.objects.toLibraryViews
|
||||
import com.anytypeio.anytype.presentation.search.ObjectSearchConstants
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
|
||||
class LibraryRelationsDelegate @Inject constructor(
|
||||
private val container: StorelessSubscriptionContainer,
|
||||
private val urlBuilder: UrlBuilder,
|
||||
private val dispatchers: AppCoroutineDispatchers
|
||||
) : LibraryListDelegate, QueryListenerLibRelations {
|
||||
|
||||
override val queryFlow: MutableStateFlow<String> = MutableStateFlow("")
|
||||
|
||||
override fun onQueryLibRelations(string: String) {
|
||||
queryFlow.value = string
|
||||
}
|
||||
|
||||
override val itemsFlow: Flow<LibraryScreenState.Tabs.TabData> = combine(
|
||||
itemsFlow(),
|
||||
queryFlow()
|
||||
) { items, query ->
|
||||
LibraryScreenState.Tabs.TabData(
|
||||
items
|
||||
.toLibraryViews(urlBuilder)
|
||||
.filterByQuery(query)
|
||||
.optAddEmptyPlaceholder(query)
|
||||
)
|
||||
}
|
||||
|
||||
override fun itemsFlow() = container.subscribe(buildSearchParams())
|
||||
|
||||
private fun buildSearchParams(): StoreSearchParams {
|
||||
return StoreSearchParams(
|
||||
space = SpaceId(MARKETPLACE_SPACE_ID),
|
||||
subscription = SUB_LIBRARY_RELATIONS,
|
||||
keys = ObjectSearchConstants.defaultRelationKeys,
|
||||
filters = buildList {
|
||||
addAll(ObjectSearchConstants.filterMarketplaceRelations())
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun unsubscribe() = with(dispatchers.io) {
|
||||
container.unsubscribe(listOf(SUB_LIBRARY_RELATIONS))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun List<LibraryView>.optAddEmptyPlaceholder(query: String): List<LibraryView> {
|
||||
val q = query.trim()
|
||||
val result = this
|
||||
return if (q.isNotEmpty() && result.isEmpty()) {
|
||||
listOf<LibraryView>(LibraryView.LibraryRelationsPlaceholderView(name = q))
|
||||
} else {
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
private const val SUB_LIBRARY_RELATIONS = "subscription.library_relations"
|
|
@ -1,73 +0,0 @@
|
|||
package com.anytypeio.anytype.presentation.library.delegates
|
||||
|
||||
import com.anytypeio.anytype.core_models.Marketplace.MARKETPLACE_SPACE_ID
|
||||
import com.anytypeio.anytype.core_models.primitives.SpaceId
|
||||
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
|
||||
import com.anytypeio.anytype.domain.library.StoreSearchParams
|
||||
import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.presentation.library.LibraryListDelegate
|
||||
import com.anytypeio.anytype.presentation.library.LibraryScreenState
|
||||
import com.anytypeio.anytype.presentation.library.LibraryView
|
||||
import com.anytypeio.anytype.presentation.library.QueryListenerLibTypes
|
||||
import com.anytypeio.anytype.presentation.library.filterByQuery
|
||||
import com.anytypeio.anytype.presentation.objects.SupportedLayouts
|
||||
import com.anytypeio.anytype.presentation.objects.toLibraryViews
|
||||
import com.anytypeio.anytype.presentation.search.ObjectSearchConstants
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
|
||||
class LibraryTypesDelegate @Inject constructor(
|
||||
private val container: StorelessSubscriptionContainer,
|
||||
private val urlBuilder: UrlBuilder,
|
||||
private val dispatchers: AppCoroutineDispatchers
|
||||
) : LibraryListDelegate, QueryListenerLibTypes {
|
||||
|
||||
override val queryFlow: MutableStateFlow<String> = MutableStateFlow("")
|
||||
|
||||
override fun onQueryLibTypes(string: String) {
|
||||
queryFlow.value = string
|
||||
}
|
||||
|
||||
override val itemsFlow: Flow<LibraryScreenState.Tabs.TabData> = combine(
|
||||
itemsFlow(),
|
||||
queryFlow()
|
||||
) { items, query ->
|
||||
LibraryScreenState.Tabs.TabData(
|
||||
items
|
||||
.toLibraryViews(urlBuilder)
|
||||
.filterByQuery(query)
|
||||
.optAddEmptyPlaceholder(query)
|
||||
)
|
||||
}
|
||||
|
||||
override fun itemsFlow() = container.subscribe(buildSearchParams())
|
||||
|
||||
private fun buildSearchParams(): StoreSearchParams {
|
||||
return StoreSearchParams(
|
||||
space = SpaceId(MARKETPLACE_SPACE_ID),
|
||||
subscription = SUB_LIBRARY_TYPES,
|
||||
keys = ObjectSearchConstants.defaultKeys,
|
||||
filters = ObjectSearchConstants.filterTypes()
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun unsubscribe() = with(dispatchers.io) {
|
||||
container.unsubscribe(listOf(SUB_LIBRARY_TYPES))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun List<LibraryView>.optAddEmptyPlaceholder(query: String): List<LibraryView> {
|
||||
val q = query.trim()
|
||||
val result = this
|
||||
return if (q.isNotEmpty() && result.isEmpty()) {
|
||||
listOf(LibraryView.LibraryTypesPlaceholderView(name = q))
|
||||
} else {
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
private const val SUB_LIBRARY_TYPES = "subscription.library_types"
|
|
@ -1,92 +0,0 @@
|
|||
package com.anytypeio.anytype.presentation.library.delegates
|
||||
|
||||
import com.anytypeio.anytype.core_models.DVFilter
|
||||
import com.anytypeio.anytype.core_models.DVFilterCondition
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.Relations
|
||||
import com.anytypeio.anytype.core_models.primitives.SpaceId
|
||||
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
|
||||
import com.anytypeio.anytype.domain.library.StoreSearchParams
|
||||
import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.domain.workspace.SpaceManager
|
||||
import com.anytypeio.anytype.presentation.library.LibraryListDelegate
|
||||
import com.anytypeio.anytype.presentation.library.LibraryScreenState
|
||||
import com.anytypeio.anytype.presentation.library.LibraryView
|
||||
import com.anytypeio.anytype.presentation.library.QueryListenerMyRelations
|
||||
import com.anytypeio.anytype.presentation.library.filterByQuery
|
||||
import com.anytypeio.anytype.presentation.objects.toLibraryViews
|
||||
import com.anytypeio.anytype.presentation.search.ObjectSearchConstants
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.flatMapMerge
|
||||
import kotlinx.coroutines.flow.flow
|
||||
|
||||
class MyRelationsDelegate @Inject constructor(
|
||||
private val container: StorelessSubscriptionContainer,
|
||||
private val spaceManager: SpaceManager,
|
||||
private val urlBuilder: UrlBuilder,
|
||||
private val dispatchers: AppCoroutineDispatchers
|
||||
) : LibraryListDelegate, QueryListenerMyRelations {
|
||||
|
||||
override val queryFlow: MutableStateFlow<String> = MutableStateFlow("")
|
||||
|
||||
override fun onQueryMyRelations(string: String) {
|
||||
queryFlow.value = string
|
||||
}
|
||||
|
||||
@FlowPreview
|
||||
override val itemsFlow: Flow<LibraryScreenState.Tabs.TabData> = combine(
|
||||
itemsFlow(),
|
||||
queryFlow()
|
||||
) { items, query ->
|
||||
LibraryScreenState.Tabs.TabData(
|
||||
items
|
||||
.toLibraryViews(urlBuilder)
|
||||
.filterByQuery(query)
|
||||
.optAddCreateRelationView(query)
|
||||
)
|
||||
}
|
||||
|
||||
@FlowPreview
|
||||
override fun itemsFlow() = flow {
|
||||
emit(spaceManager.get())
|
||||
}.flatMapMerge { space: Id ->
|
||||
val searchParams = buildSearchParams(space)
|
||||
container.subscribe(searchParams)
|
||||
}
|
||||
|
||||
private fun buildSearchParams(space: Id): StoreSearchParams {
|
||||
return StoreSearchParams(
|
||||
space = SpaceId(space),
|
||||
subscription = SUB_LIBRARY_MY_RELATIONS,
|
||||
keys = ObjectSearchConstants.defaultRelationKeys,
|
||||
filters = buildList {
|
||||
addAll(ObjectSearchConstants.filterMyRelations())
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun unsubscribe() = with(dispatchers.io) {
|
||||
container.unsubscribe(listOf(SUB_LIBRARY_MY_RELATIONS))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun List<LibraryView>.optAddCreateRelationView(query: String): List<LibraryView> {
|
||||
val q = query.trim()
|
||||
val result = this
|
||||
return if (q.isNotEmpty() && result.none { it.name.lowercase() == q.lowercase() }) {
|
||||
buildList {
|
||||
add(LibraryView.CreateNewRelationView(name = q))
|
||||
addAll(result)
|
||||
}
|
||||
} else {
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
private const val SUB_LIBRARY_MY_RELATIONS = "subscription.library_my_relations"
|
|
@ -1,92 +0,0 @@
|
|||
package com.anytypeio.anytype.presentation.library.delegates
|
||||
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.Relations
|
||||
import com.anytypeio.anytype.core_models.primitives.SpaceId
|
||||
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
|
||||
import com.anytypeio.anytype.domain.library.StoreSearchParams
|
||||
import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.domain.workspace.SpaceManager
|
||||
import com.anytypeio.anytype.presentation.library.LibraryListDelegate
|
||||
import com.anytypeio.anytype.presentation.library.LibraryScreenState
|
||||
import com.anytypeio.anytype.presentation.library.LibraryView
|
||||
import com.anytypeio.anytype.presentation.library.QueryListenerMyTypes
|
||||
import com.anytypeio.anytype.presentation.library.filterByQuery
|
||||
import com.anytypeio.anytype.presentation.objects.toLibraryViews
|
||||
import com.anytypeio.anytype.presentation.search.ObjectSearchConstants
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.flatMapMerge
|
||||
import kotlinx.coroutines.flow.flow
|
||||
|
||||
class MyTypesDelegate @Inject constructor(
|
||||
private val container: StorelessSubscriptionContainer,
|
||||
private val spaceManager: SpaceManager,
|
||||
private val urlBuilder: UrlBuilder,
|
||||
private val dispatchers: AppCoroutineDispatchers
|
||||
) : LibraryListDelegate, QueryListenerMyTypes {
|
||||
|
||||
override val queryFlow: MutableStateFlow<String> = MutableStateFlow("")
|
||||
|
||||
override fun onQueryMyTypes(string: String) {
|
||||
queryFlow.value = string
|
||||
}
|
||||
|
||||
@FlowPreview
|
||||
override val itemsFlow: Flow<LibraryScreenState.Tabs.TabData> = combine(
|
||||
itemsFlow(), queryFlow()
|
||||
) { items, query ->
|
||||
LibraryScreenState.Tabs.TabData(
|
||||
items
|
||||
.toLibraryViews(urlBuilder)
|
||||
.filterByQuery(query)
|
||||
.optAddCreateTypeView(query)
|
||||
)
|
||||
}
|
||||
|
||||
@FlowPreview
|
||||
override fun itemsFlow() = flow {
|
||||
emit(spaceManager.get())
|
||||
}.flatMapMerge { space: Id ->
|
||||
val searchParams = buildSearchParams(space = space)
|
||||
container.subscribe(searchParams)
|
||||
}
|
||||
|
||||
private fun buildSearchParams(space: Id): StoreSearchParams {
|
||||
return StoreSearchParams(
|
||||
space = SpaceId(space),
|
||||
subscription = SUB_LIBRARY_MY_TYPES,
|
||||
keys = ObjectSearchConstants.defaultKeys + listOf(
|
||||
Relations.SOURCE_OBJECT,
|
||||
Relations.RESTRICTIONS
|
||||
),
|
||||
filters = ObjectSearchConstants.filterTypes(
|
||||
excludeParticipant = false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun unsubscribe() = with(dispatchers.io) {
|
||||
container.unsubscribe(listOf(SUB_LIBRARY_MY_TYPES))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun List<LibraryView>.optAddCreateTypeView(query: String): List<LibraryView> {
|
||||
val q = query.trim()
|
||||
val result = this
|
||||
return if (q.isNotEmpty() && result.none { it.name.lowercase() == q.lowercase() }) {
|
||||
buildList {
|
||||
add(LibraryView.CreateNewTypeView(name = q))
|
||||
addAll(result)
|
||||
}
|
||||
} else {
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
private const val SUB_LIBRARY_MY_TYPES = "subscription.library_my_types"
|
|
@ -45,8 +45,6 @@ interface AppNavigation {
|
|||
|
||||
fun deletedAccountScreen(deadline: Long)
|
||||
|
||||
fun openLibrary(space: Id)
|
||||
|
||||
fun logout()
|
||||
|
||||
fun migrationErrorScreen()
|
||||
|
@ -97,8 +95,6 @@ interface AppNavigation {
|
|||
data class DeletedAccountScreen(val deadline: Long) : Command()
|
||||
|
||||
data class OpenTemplates(val typeId: Id) : Command()
|
||||
|
||||
data class OpenLibrary(val space: Id): Command()
|
||||
}
|
||||
|
||||
interface Provider {
|
||||
|
|
|
@ -1,48 +1,20 @@
|
|||
package com.anytypeio.anytype.presentation.objects
|
||||
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.Marketplace
|
||||
import com.anytypeio.anytype.core_models.MarketplaceObjectTypeIds
|
||||
import com.anytypeio.anytype.core_models.ObjectType
|
||||
import com.anytypeio.anytype.core_models.ObjectTypeUniqueKeys
|
||||
import com.anytypeio.anytype.core_models.ObjectWrapper
|
||||
import com.anytypeio.anytype.core_models.Relations
|
||||
import com.anytypeio.anytype.core_models.Relations.SOURCE_OBJECT
|
||||
import com.anytypeio.anytype.core_models.ext.DateParser
|
||||
import com.anytypeio.anytype.core_models.restrictions.ObjectRestriction
|
||||
import com.anytypeio.anytype.core_utils.ext.readableFileSize
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.presentation.extension.getProperObjectName
|
||||
import com.anytypeio.anytype.presentation.library.LibraryView
|
||||
import com.anytypeio.anytype.presentation.linking.LinkToItemView
|
||||
import com.anytypeio.anytype.presentation.mapper.objectIcon
|
||||
import com.anytypeio.anytype.presentation.navigation.DefaultObjectView
|
||||
import com.anytypeio.anytype.presentation.sets.filter.CreateFilterView
|
||||
import com.anytypeio.anytype.presentation.widgets.collection.CollectionView
|
||||
|
||||
@Deprecated("To be deleted")
|
||||
fun List<ObjectWrapper.Basic>.toView(
|
||||
urlBuilder: UrlBuilder,
|
||||
objectTypes: List<ObjectWrapper.Type>
|
||||
): List<DefaultObjectView> =
|
||||
this.map { obj ->
|
||||
val typeUrl = obj.getProperType()
|
||||
val layout = obj.getProperLayout()
|
||||
DefaultObjectView(
|
||||
id = obj.id,
|
||||
name = obj.getProperName(),
|
||||
type = typeUrl,
|
||||
typeName = getProperTypeName(
|
||||
id = typeUrl,
|
||||
types = objectTypes
|
||||
),
|
||||
description = obj.description,
|
||||
layout = layout,
|
||||
icon = obj.objectIcon(urlBuilder),
|
||||
space = requireNotNull(obj.spaceId)
|
||||
)
|
||||
}
|
||||
|
||||
fun List<ObjectWrapper.Basic>.toViews(
|
||||
urlBuilder: UrlBuilder,
|
||||
objectTypes: List<ObjectWrapper.Type>
|
||||
|
@ -79,57 +51,6 @@ fun ObjectWrapper.Basic.toView(
|
|||
)
|
||||
}
|
||||
|
||||
fun List<ObjectWrapper.Basic>.toLibraryViews(
|
||||
urlBuilder: UrlBuilder
|
||||
): List<LibraryView> = map { obj ->
|
||||
val space = obj.getValue<Id?>(Relations.SPACE_ID)
|
||||
when (obj.layout) {
|
||||
ObjectType.Layout.OBJECT_TYPE -> {
|
||||
if (space == Marketplace.MARKETPLACE_SPACE_ID) {
|
||||
LibraryView.LibraryTypeView(
|
||||
id = obj.id,
|
||||
name = obj.name.orEmpty(),
|
||||
icon = obj.objectIcon(urlBuilder),
|
||||
uniqueKey = obj.uniqueKey
|
||||
)
|
||||
} else {
|
||||
LibraryView.MyTypeView(
|
||||
id = obj.id,
|
||||
name = obj.name.orEmpty(),
|
||||
icon = obj.objectIcon(urlBuilder),
|
||||
sourceObject = obj.map[SOURCE_OBJECT]?.toString(),
|
||||
uniqueKey = obj.uniqueKey,
|
||||
readOnly = obj.restrictions.contains(ObjectRestriction.DELETE),
|
||||
editable = !obj.restrictions.contains(ObjectRestriction.DETAILS)
|
||||
)
|
||||
}
|
||||
}
|
||||
ObjectType.Layout.RELATION -> {
|
||||
val relation = ObjectWrapper.Relation(obj.map)
|
||||
if (space == Marketplace.MARKETPLACE_SPACE_ID) {
|
||||
LibraryView.LibraryRelationView(
|
||||
id = obj.id,
|
||||
name = obj.name.orEmpty(),
|
||||
format = relation.format
|
||||
)
|
||||
} else {
|
||||
LibraryView.MyRelationView(
|
||||
id = obj.id,
|
||||
name = obj.name.orEmpty(),
|
||||
format = relation.format,
|
||||
sourceObject = obj.map[SOURCE_OBJECT]?.toString(),
|
||||
readOnly = obj.restrictions.contains(ObjectRestriction.DELETE),
|
||||
editable = !obj.restrictions.contains(ObjectRestriction.DETAILS)
|
||||
)
|
||||
}
|
||||
}
|
||||
else -> LibraryView.UnknownView(
|
||||
id = obj.id,
|
||||
name = obj.name.orEmpty()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun List<ObjectWrapper.Basic>.toLinkToView(
|
||||
urlBuilder: UrlBuilder,
|
||||
objectTypes: List<ObjectWrapper.Type>
|
||||
|
|
|
@ -136,11 +136,6 @@ sealed class WidgetView {
|
|||
}
|
||||
}
|
||||
|
||||
data object Library : WidgetView() {
|
||||
override val id: Id get() = "id.button.library"
|
||||
override val isLoading: Boolean = false
|
||||
}
|
||||
|
||||
sealed class Action : WidgetView() {
|
||||
data object EditWidgets : Action() {
|
||||
override val id: Id get() = "id.action.edit-widgets"
|
||||
|
|
|
@ -4,7 +4,7 @@ import com.anytypeio.anytype.core_models.ObjectType
|
|||
import com.anytypeio.anytype.core_models.ObjectWrapper
|
||||
import com.anytypeio.anytype.core_models.Relations
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.presentation.objects.toView
|
||||
import com.anytypeio.anytype.presentation.objects.toViews
|
||||
import com.anytypeio.anytype.test_utils.MockDataFactory
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNull
|
||||
|
@ -92,7 +92,7 @@ class ObjectWrapperExtensionsKtTest {
|
|||
|
||||
)
|
||||
|
||||
val result = listOf(obj).toView(urlBuilder, objectTypes = listOf())
|
||||
val result = listOf(obj).toViews(urlBuilder, objectTypes = listOf())
|
||||
|
||||
assertEquals(
|
||||
expected = "OMr2Y",
|
||||
|
@ -116,7 +116,10 @@ class ObjectWrapperExtensionsKtTest {
|
|||
|
||||
)
|
||||
|
||||
val result = listOf(obj).toView(urlBuilder, objectTypes = listOf())
|
||||
val result = listOf(obj).toViews(
|
||||
urlBuilder = urlBuilder,
|
||||
objectTypes = listOf()
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
expected = "LmL7R",
|
||||
|
@ -141,7 +144,7 @@ class ObjectWrapperExtensionsKtTest {
|
|||
|
||||
)
|
||||
|
||||
val result = listOf(obj).toView(urlBuilder, objectTypes = listOf())
|
||||
val result = listOf(obj).toViews(urlBuilder, objectTypes = listOf())
|
||||
|
||||
assertEquals(
|
||||
expected = "Anytype is next-generation sof",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue