mirror of
https://github.com/anyproto/anytype-kotlin.git
synced 2025-06-08 05:47:05 +09:00
DROID-2905 Primitives | Epic | Foundation for primitives (#2098)
Co-authored-by: Evgenii Kozlov <enklave.mare.balticum@protonmail.com>
This commit is contained in:
parent
88aa30d64b
commit
4bc1e060f3
153 changed files with 10877 additions and 1616 deletions
|
@ -234,6 +234,14 @@ object EventsDictionary {
|
|||
const val clickDateCalendarView = "ClickDateCalendarView"
|
||||
const val objectListSort = "ObjectListSort"
|
||||
|
||||
//ObjectType
|
||||
const val screenObjectType = "ScreenType"
|
||||
const val editType = "EditType"
|
||||
const val changeRecommendedLayout = "ChangeRecommendedLayout"
|
||||
const val changeTypeSort = "ChangeTypeSort"
|
||||
const val screenTemplate = "ScreenTemplate"
|
||||
|
||||
|
||||
const val searchBacklink = "SearchBacklink"
|
||||
|
||||
object SharingSpacesTypes {
|
||||
|
|
|
@ -176,6 +176,7 @@ dependencies {
|
|||
implementation project(':gallery-experience')
|
||||
implementation project(':feature-all-content')
|
||||
implementation project(':feature-date')
|
||||
implementation project(':feature-object-type')
|
||||
|
||||
//Compile time dependencies
|
||||
ksp libs.daggerCompiler
|
||||
|
@ -223,7 +224,7 @@ dependencies {
|
|||
implementation libs.composeAccompanistNavigation
|
||||
implementation libs.preference
|
||||
implementation libs.activityCompose
|
||||
implementation libs.composeReorderable
|
||||
implementation libs.composeReorderableLegacy
|
||||
|
||||
implementation libs.room
|
||||
implementation libs.appUpdater
|
||||
|
|
|
@ -14,6 +14,7 @@ import com.anytypeio.anytype.presentation.MockBlockContentFactory
|
|||
import com.anytypeio.anytype.presentation.MockBlockFactory
|
||||
import com.anytypeio.anytype.presentation.editor.cover.CoverColor
|
||||
import com.anytypeio.anytype.core_models.ObjectViewDetails
|
||||
import com.anytypeio.anytype.core_models.Relations
|
||||
import com.anytypeio.anytype.test_utils.MockDataFactory
|
||||
import com.anytypeio.anytype.test_utils.utils.checkHasText
|
||||
import com.anytypeio.anytype.test_utils.utils.checkIsDisplayed
|
||||
|
@ -85,7 +86,7 @@ class LayoutTesting : EditorTestSetup() {
|
|||
root to
|
||||
mapOf(
|
||||
"iconEmoji" to DefaultDocumentEmojiIconProvider.DOCUMENT_SET.random(),
|
||||
"layout" to ObjectType.Layout.TODO.code.toDouble()
|
||||
Relations.LAYOUT to ObjectType.Layout.TODO.code.toDouble()
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -135,7 +136,7 @@ class LayoutTesting : EditorTestSetup() {
|
|||
root to
|
||||
mapOf(
|
||||
"iconEmoji" to DefaultDocumentEmojiIconProvider.DOCUMENT_SET.random(),
|
||||
"layout" to ObjectType.Layout.TODO.code.toDouble()
|
||||
Relations.LAYOUT to ObjectType.Layout.TODO.code.toDouble()
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -180,7 +181,7 @@ class LayoutTesting : EditorTestSetup() {
|
|||
root to
|
||||
mapOf(
|
||||
"iconEmoji" to DefaultDocumentEmojiIconProvider.DOCUMENT_SET.random(),
|
||||
"layout" to ObjectType.Layout.TODO.code.toDouble(),
|
||||
Relations.LAYOUT to ObjectType.Layout.TODO.code.toDouble(),
|
||||
"coverType" to CoverType.COLOR.code.toDouble(),
|
||||
"coverId" to CoverColor.BLUE.code,
|
||||
)
|
||||
|
@ -226,7 +227,7 @@ class LayoutTesting : EditorTestSetup() {
|
|||
root to
|
||||
mapOf(
|
||||
"iconEmoji" to DefaultDocumentEmojiIconProvider.DOCUMENT_SET.random(),
|
||||
"layout" to ObjectType.Layout.PROFILE.code.toDouble()
|
||||
Relations.LAYOUT to ObjectType.Layout.PROFILE.code.toDouble()
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -270,7 +271,7 @@ class LayoutTesting : EditorTestSetup() {
|
|||
root to
|
||||
mapOf(
|
||||
"iconEmoji" to DefaultDocumentEmojiIconProvider.DOCUMENT_SET.random(),
|
||||
"layout" to ObjectType.Layout.PROFILE.code.toDouble(),
|
||||
Relations.LAYOUT to ObjectType.Layout.PROFILE.code.toDouble(),
|
||||
"coverType" to CoverType.COLOR.code.toDouble(),
|
||||
"coverId" to CoverColor.BLUE.code,
|
||||
)
|
||||
|
@ -316,7 +317,7 @@ class LayoutTesting : EditorTestSetup() {
|
|||
root to
|
||||
mapOf(
|
||||
"iconEmoji" to DefaultDocumentEmojiIconProvider.DOCUMENT_SET.random(),
|
||||
"layout" to ObjectType.Layout.BASIC.code.toDouble()
|
||||
Relations.LAYOUT to ObjectType.Layout.BASIC.code.toDouble()
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -360,7 +361,7 @@ class LayoutTesting : EditorTestSetup() {
|
|||
root to
|
||||
mapOf(
|
||||
"iconEmoji" to DefaultDocumentEmojiIconProvider.DOCUMENT_SET.random(),
|
||||
"layout" to ObjectType.Layout.BASIC.code.toDouble(),
|
||||
Relations.LAYOUT to ObjectType.Layout.BASIC.code.toDouble(),
|
||||
"coverType" to CoverType.COLOR.code.toDouble(),
|
||||
"coverId" to CoverColor.BLUE.code,
|
||||
)
|
||||
|
|
|
@ -14,6 +14,7 @@ import com.anytypeio.anytype.features.editor.base.EditorTestSetup
|
|||
import com.anytypeio.anytype.presentation.MockBlockContentFactory.StubTextContent
|
||||
import com.anytypeio.anytype.presentation.editor.cover.CoverColor
|
||||
import com.anytypeio.anytype.core_models.ObjectViewDetails
|
||||
import com.anytypeio.anytype.core_models.Relations
|
||||
import com.anytypeio.anytype.test_utils.MockDataFactory
|
||||
import com.anytypeio.anytype.test_utils.utils.checkHasText
|
||||
import com.anytypeio.anytype.test_utils.utils.checkIsDisplayed
|
||||
|
@ -182,7 +183,7 @@ class ProfileTesting : EditorTestSetup() {
|
|||
root to
|
||||
mapOf(
|
||||
"iconImage" to "anyimage",
|
||||
"layout" to ObjectType.Layout.PROFILE.code.toDouble(),
|
||||
Relations.LAYOUT to ObjectType.Layout.PROFILE.code.toDouble(),
|
||||
"coverType" to CoverType.COLOR.code.toDouble(),
|
||||
"coverId" to CoverColor.BLUE.code,
|
||||
)
|
||||
|
@ -195,7 +196,7 @@ class ProfileTesting : EditorTestSetup() {
|
|||
mapOf(
|
||||
root to
|
||||
mapOf(
|
||||
"layout" to ObjectType.Layout.PROFILE.code.toDouble(),
|
||||
Relations.LAYOUT to ObjectType.Layout.PROFILE.code.toDouble(),
|
||||
"coverType" to CoverType.COLOR.code.toDouble(),
|
||||
"coverId" to CoverColor.BLUE.code,
|
||||
)
|
||||
|
|
|
@ -11,6 +11,7 @@ import com.anytypeio.anytype.di.feature.DaggerBacklinkOrAddToObjectComponent
|
|||
import com.anytypeio.anytype.di.feature.DaggerDateObjectComponent
|
||||
import com.anytypeio.anytype.di.feature.DaggerLinkToObjectComponent
|
||||
import com.anytypeio.anytype.di.feature.DaggerMoveToComponent
|
||||
import com.anytypeio.anytype.di.feature.DaggerObjectTypeComponent
|
||||
import com.anytypeio.anytype.di.feature.DaggerSplashComponent
|
||||
import com.anytypeio.anytype.di.feature.DebugSettingsModule
|
||||
import com.anytypeio.anytype.di.feature.DefaultComponentParam
|
||||
|
@ -105,6 +106,7 @@ import com.anytypeio.anytype.di.main.MainComponent
|
|||
import com.anytypeio.anytype.feature_allcontent.presentation.AllContentViewModel
|
||||
import com.anytypeio.anytype.feature_chats.presentation.ChatReactionViewModel
|
||||
import com.anytypeio.anytype.feature_chats.presentation.ChatViewModel
|
||||
import com.anytypeio.anytype.feature_object_type.ui.ObjectTypeVmParams
|
||||
import com.anytypeio.anytype.feature_chats.presentation.SelectChatReactionViewModel
|
||||
import com.anytypeio.anytype.feature_date.viewmodel.DateObjectVmParams
|
||||
import com.anytypeio.anytype.gallery_experience.viewmodel.GalleryInstallationViewModel
|
||||
|
@ -1133,6 +1135,12 @@ class ComponentManager(
|
|||
.create(findComponentDependencies())
|
||||
}
|
||||
|
||||
val objectTypeComponent = ComponentWithParams { params: ObjectTypeVmParams ->
|
||||
DaggerObjectTypeComponent
|
||||
.factory()
|
||||
.create(params, findComponentDependencies())
|
||||
}
|
||||
|
||||
class Component<T>(private val builder: () -> T) {
|
||||
|
||||
private var instance: T? = null
|
||||
|
|
|
@ -16,6 +16,7 @@ import com.anytypeio.anytype.domain.misc.DeepLinkResolver
|
|||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.domain.multiplayer.GetSpaceInviteLink
|
||||
import com.anytypeio.anytype.domain.multiplayer.SpaceViewSubscriptionContainer
|
||||
import com.anytypeio.anytype.domain.multiplayer.UserPermissionProvider
|
||||
import com.anytypeio.anytype.domain.`object`.DuplicateObject
|
||||
import com.anytypeio.anytype.domain.`object`.SetObjectDetails
|
||||
import com.anytypeio.anytype.domain.objects.SetObjectListIsArchived
|
||||
|
@ -23,6 +24,8 @@ import com.anytypeio.anytype.domain.page.AddBackLinkToObject
|
|||
import com.anytypeio.anytype.domain.page.CloseBlock
|
||||
import com.anytypeio.anytype.domain.page.OpenPage
|
||||
import com.anytypeio.anytype.domain.primitives.FieldParser
|
||||
import com.anytypeio.anytype.domain.relations.AddToFeaturedRelations
|
||||
import com.anytypeio.anytype.domain.relations.RemoveFromFeaturedRelations
|
||||
import com.anytypeio.anytype.domain.templates.CreateTemplateFromObject
|
||||
import com.anytypeio.anytype.domain.widgets.CreateWidget
|
||||
import com.anytypeio.anytype.domain.workspace.SpaceManager
|
||||
|
@ -122,7 +125,10 @@ object ObjectMenuModule {
|
|||
setObjectIsArchived: SetObjectListIsArchived,
|
||||
fieldParser: FieldParser,
|
||||
spaceViewSubscriptionContainer: SpaceViewSubscriptionContainer,
|
||||
getSpaceInviteLink: GetSpaceInviteLink
|
||||
getSpaceInviteLink: GetSpaceInviteLink,
|
||||
addToFeaturedRelations: AddToFeaturedRelations,
|
||||
removeFromFeaturedRelations: RemoveFromFeaturedRelations,
|
||||
userPermissionProvider: UserPermissionProvider
|
||||
): ObjectMenuViewModel.Factory = ObjectMenuViewModel.Factory(
|
||||
setObjectIsArchived = setObjectIsArchived,
|
||||
duplicateObject = duplicateObject,
|
||||
|
@ -147,7 +153,10 @@ object ObjectMenuModule {
|
|||
setObjectListIsFavorite = setObjectListIsFavorite,
|
||||
fieldParser = fieldParser,
|
||||
getSpaceInviteLink = getSpaceInviteLink,
|
||||
spaceViewSubscriptionContainer = spaceViewSubscriptionContainer
|
||||
spaceViewSubscriptionContainer = spaceViewSubscriptionContainer,
|
||||
addToFeaturedRelations = addToFeaturedRelations,
|
||||
removeFromFeaturedRelations = removeFromFeaturedRelations,
|
||||
userPermissionProvider = userPermissionProvider
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
|
@ -214,6 +223,18 @@ object ObjectMenuModule {
|
|||
repo: BlockRepository,
|
||||
dispatchers: AppCoroutineDispatchers
|
||||
): SetObjectListIsArchived = SetObjectListIsArchived(repo = repo, dispatchers = dispatchers)
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@PerDialog
|
||||
fun addToFeaturedRelations(repo: BlockRepository): AddToFeaturedRelations =
|
||||
AddToFeaturedRelations(repo)
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@PerDialog
|
||||
fun removeFromFeaturedRelations(repo: BlockRepository): RemoveFromFeaturedRelations =
|
||||
RemoveFromFeaturedRelations(repo)
|
||||
}
|
||||
|
||||
@Module
|
||||
|
@ -242,7 +263,10 @@ object ObjectSetMenuModule {
|
|||
setObjectIsArchived: SetObjectListIsArchived,
|
||||
fieldParser: FieldParser,
|
||||
getSpaceInviteLink: GetSpaceInviteLink,
|
||||
spaceViewSubscriptionContainer: SpaceViewSubscriptionContainer
|
||||
spaceViewSubscriptionContainer: SpaceViewSubscriptionContainer,
|
||||
addToFeaturedRelations: AddToFeaturedRelations,
|
||||
removeFromFeaturedRelations: RemoveFromFeaturedRelations,
|
||||
userPermissionProvider: UserPermissionProvider
|
||||
): ObjectSetMenuViewModel.Factory = ObjectSetMenuViewModel.Factory(
|
||||
setObjectListIsArchived = setObjectIsArchived,
|
||||
addBackLinkToObject = addBackLinkToObject,
|
||||
|
@ -263,7 +287,10 @@ object ObjectSetMenuModule {
|
|||
setObjectListIsFavorite = setObjectListIsFavorite,
|
||||
fieldParser = fieldParser,
|
||||
getSpaceInviteLink = getSpaceInviteLink,
|
||||
spaceViewSubscriptionContainer = spaceViewSubscriptionContainer
|
||||
spaceViewSubscriptionContainer = spaceViewSubscriptionContainer,
|
||||
addToFeaturedRelations = addToFeaturedRelations,
|
||||
removeFromFeaturedRelations = removeFromFeaturedRelations,
|
||||
userPermissionProvider = userPermissionProvider
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
|
@ -333,4 +360,16 @@ object ObjectSetMenuModule {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@PerDialog
|
||||
fun addToFeaturedRelations(repo: BlockRepository): AddToFeaturedRelations =
|
||||
AddToFeaturedRelations(repo)
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@PerDialog
|
||||
fun removeFromFeaturedRelations(repo: BlockRepository): RemoveFromFeaturedRelations =
|
||||
RemoveFromFeaturedRelations(repo)
|
||||
}
|
|
@ -3,11 +3,16 @@ package com.anytypeio.anytype.di.feature
|
|||
import com.anytypeio.anytype.analytics.base.Analytics
|
||||
import com.anytypeio.anytype.core_models.Payload
|
||||
import com.anytypeio.anytype.core_utils.di.scope.PerModal
|
||||
import com.anytypeio.anytype.core_utils.di.scope.PerScreen
|
||||
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
|
||||
import com.anytypeio.anytype.domain.block.repo.BlockRepository
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.domain.multiplayer.UserPermissionProvider
|
||||
import com.anytypeio.anytype.domain.`object`.UpdateDetail
|
||||
import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
|
||||
import com.anytypeio.anytype.domain.objects.StoreOfRelations
|
||||
import com.anytypeio.anytype.domain.primitives.FieldParser
|
||||
import com.anytypeio.anytype.domain.primitives.SetObjectTypeRecommendedFields
|
||||
import com.anytypeio.anytype.domain.relations.AddRelationToObject
|
||||
import com.anytypeio.anytype.domain.relations.AddToFeaturedRelations
|
||||
import com.anytypeio.anytype.domain.relations.DeleteRelationFromObject
|
||||
|
@ -18,6 +23,7 @@ import com.anytypeio.anytype.presentation.relations.ObjectRelationListViewModelF
|
|||
import com.anytypeio.anytype.presentation.relations.RelationListViewModel
|
||||
import com.anytypeio.anytype.presentation.relations.providers.ObjectRelationListProvider
|
||||
import com.anytypeio.anytype.presentation.util.Dispatcher
|
||||
import com.anytypeio.anytype.ui.primitives.ObjectFieldsFragment
|
||||
import com.anytypeio.anytype.ui.relations.ObjectRelationListFragment
|
||||
import dagger.BindsInstance
|
||||
import dagger.Module
|
||||
|
@ -37,6 +43,7 @@ interface ObjectRelationListComponent {
|
|||
}
|
||||
|
||||
fun inject(fragment: ObjectRelationListFragment)
|
||||
fun inject(fragment: ObjectFieldsFragment)
|
||||
}
|
||||
|
||||
@Module
|
||||
|
@ -56,9 +63,12 @@ object ObjectRelationListModule {
|
|||
deleteRelationFromObject: DeleteRelationFromObject,
|
||||
analytics: Analytics,
|
||||
storeOfRelations: StoreOfRelations,
|
||||
storeOfObjectTypes: StoreOfObjectTypes,
|
||||
addRelationToObject: AddRelationToObject,
|
||||
analyticSpaceHelperDelegate: AnalyticSpaceHelperDelegate,
|
||||
fieldParser: FieldParser,
|
||||
userPermissionProvider: UserPermissionProvider,
|
||||
setObjectTypeRecommendedFields: SetObjectTypeRecommendedFields
|
||||
): ObjectRelationListViewModelFactory {
|
||||
return ObjectRelationListViewModelFactory(
|
||||
vmParams = vmParams,
|
||||
|
@ -72,9 +82,12 @@ object ObjectRelationListModule {
|
|||
deleteRelationFromObject = deleteRelationFromObject,
|
||||
analytics = analytics,
|
||||
storeOfRelations = storeOfRelations,
|
||||
storeOfObjectTypes = storeOfObjectTypes,
|
||||
addRelationToObject = addRelationToObject,
|
||||
analyticSpaceHelperDelegate = analyticSpaceHelperDelegate,
|
||||
fieldParser = fieldParser,
|
||||
userPermissionProvider = userPermissionProvider,
|
||||
setObjectTypeRecommendedFields = setObjectTypeRecommendedFields
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -95,4 +108,12 @@ object ObjectRelationListModule {
|
|||
@PerModal
|
||||
fun deleteRelationFromObject(repo: BlockRepository): DeleteRelationFromObject =
|
||||
DeleteRelationFromObject(repo)
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@PerModal
|
||||
fun provideTypeSetRecommendedFields(
|
||||
repo: BlockRepository,
|
||||
dispatchers: AppCoroutineDispatchers
|
||||
): SetObjectTypeRecommendedFields = SetObjectTypeRecommendedFields(repo, dispatchers)
|
||||
}
|
|
@ -0,0 +1,189 @@
|
|||
package com.anytypeio.anytype.di.feature
|
||||
|
||||
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.interactor.sets.CreateObjectSet
|
||||
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.event.interactor.EventChannel
|
||||
import com.anytypeio.anytype.domain.event.interactor.SpaceSyncAndP2PStatusProvider
|
||||
import com.anytypeio.anytype.domain.launch.GetDefaultObjectType
|
||||
import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
|
||||
import com.anytypeio.anytype.domain.misc.LocaleProvider
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.domain.multiplayer.UserPermissionProvider
|
||||
import com.anytypeio.anytype.domain.`object`.DuplicateObjects
|
||||
import com.anytypeio.anytype.domain.`object`.SetObjectDetails
|
||||
import com.anytypeio.anytype.domain.objects.DeleteObjects
|
||||
import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
|
||||
import com.anytypeio.anytype.domain.objects.StoreOfRelations
|
||||
import com.anytypeio.anytype.domain.page.CreateObject
|
||||
import com.anytypeio.anytype.domain.primitives.FieldParser
|
||||
import com.anytypeio.anytype.domain.primitives.GetObjectTypeConflictingFields
|
||||
import com.anytypeio.anytype.domain.primitives.SetObjectTypeHeaderRecommendedFields
|
||||
import com.anytypeio.anytype.domain.primitives.SetObjectTypeRecommendedFields
|
||||
import com.anytypeio.anytype.domain.resources.StringResourceProvider
|
||||
import com.anytypeio.anytype.domain.search.SubscriptionEventChannel
|
||||
import com.anytypeio.anytype.feature_object_type.viewmodel.ObjectTypeVMFactory
|
||||
import com.anytypeio.anytype.feature_object_type.ui.ObjectTypeVmParams
|
||||
import com.anytypeio.anytype.presentation.analytics.AnalyticSpaceHelperDelegate
|
||||
import com.anytypeio.anytype.presentation.editor.cover.CoverImageHashProvider
|
||||
import com.anytypeio.anytype.providers.DefaultCoverImageHashProvider
|
||||
import com.anytypeio.anytype.ui.primitives.ObjectTypeFieldsFragment
|
||||
import com.anytypeio.anytype.ui.primitives.ObjectTypeFragment
|
||||
import dagger.Binds
|
||||
import dagger.BindsInstance
|
||||
import dagger.Component
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
||||
@Component(
|
||||
dependencies = [ObjectTypeDependencies::class],
|
||||
modules = [
|
||||
ObjectTypeModule::class,
|
||||
ObjectTypeModule.Declarations::class
|
||||
]
|
||||
)
|
||||
@PerScreen
|
||||
interface ObjectTypeComponent {
|
||||
@Component.Factory
|
||||
interface Factory {
|
||||
fun create(
|
||||
@BindsInstance vmParams: ObjectTypeVmParams,
|
||||
dependencies: ObjectTypeDependencies
|
||||
): ObjectTypeComponent
|
||||
}
|
||||
|
||||
fun inject(fragment: ObjectTypeFragment)
|
||||
fun inject(fragment: ObjectTypeFieldsFragment)
|
||||
}
|
||||
|
||||
@Module
|
||||
object ObjectTypeModule {
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@PerScreen
|
||||
fun provideStoreLessSubscriptionContainer(
|
||||
repo: BlockRepository,
|
||||
channel: SubscriptionEventChannel,
|
||||
dispatchers: AppCoroutineDispatchers,
|
||||
logger: Logger
|
||||
): StorelessSubscriptionContainer = StorelessSubscriptionContainer.Impl(
|
||||
repo = repo,
|
||||
channel = channel,
|
||||
dispatchers = dispatchers,
|
||||
logger = logger
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@PerScreen
|
||||
fun createObject(
|
||||
repo: BlockRepository,
|
||||
getDefaultObjectType: GetDefaultObjectType,
|
||||
dispatchers: AppCoroutineDispatchers,
|
||||
): CreateObject = CreateObject(
|
||||
repo = repo,
|
||||
getDefaultObjectType = getDefaultObjectType,
|
||||
dispatchers = dispatchers
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@PerScreen
|
||||
fun provideUpdateDetailUseCase(
|
||||
repository: BlockRepository,
|
||||
dispatchers: AppCoroutineDispatchers
|
||||
): SetObjectDetails = SetObjectDetails(repository, dispatchers)
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@PerScreen
|
||||
fun coverHashProvider(): CoverImageHashProvider = DefaultCoverImageHashProvider()
|
||||
|
||||
@JvmStatic
|
||||
@PerScreen
|
||||
@Provides
|
||||
fun getDeleteObjects(
|
||||
repo: BlockRepository,
|
||||
dispatchers: AppCoroutineDispatchers
|
||||
): DeleteObjects = DeleteObjects(repo, dispatchers)
|
||||
|
||||
@JvmStatic
|
||||
@PerScreen
|
||||
@Provides
|
||||
fun getObjectTypeConflictingFields(
|
||||
repo: BlockRepository,
|
||||
dispatchers: AppCoroutineDispatchers
|
||||
): GetObjectTypeConflictingFields = GetObjectTypeConflictingFields(repo, dispatchers)
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@PerScreen
|
||||
fun provideCreateObjectSetUseCase(
|
||||
repo: BlockRepository
|
||||
): CreateObjectSet = CreateObjectSet(repo = repo)
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@PerScreen
|
||||
fun provideDuplicateObjectsListUseCase(
|
||||
repo: BlockRepository,
|
||||
dispatchers: AppCoroutineDispatchers
|
||||
): DuplicateObjects = DuplicateObjects(
|
||||
repo = repo,
|
||||
dispatchers = dispatchers
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@PerScreen
|
||||
fun provideTypeSetRecommendedFields(
|
||||
repo: BlockRepository,
|
||||
dispatchers: AppCoroutineDispatchers
|
||||
): SetObjectTypeRecommendedFields = SetObjectTypeRecommendedFields(repo, dispatchers)
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@PerScreen
|
||||
fun provideTypeSetHeaderRecommendedFields(
|
||||
repo: BlockRepository,
|
||||
dispatchers: AppCoroutineDispatchers
|
||||
): SetObjectTypeHeaderRecommendedFields =
|
||||
SetObjectTypeHeaderRecommendedFields(repo, dispatchers)
|
||||
|
||||
@Module
|
||||
interface Declarations {
|
||||
@PerScreen
|
||||
@Binds
|
||||
fun bindViewModelFactory(
|
||||
factory: ObjectTypeVMFactory
|
||||
): ViewModelProvider.Factory
|
||||
}
|
||||
}
|
||||
|
||||
interface ObjectTypeDependencies : ComponentDependencies {
|
||||
fun blockRepository(): BlockRepository
|
||||
fun analytics(): Analytics
|
||||
fun urlBuilder(): UrlBuilder
|
||||
fun dispatchers(): AppCoroutineDispatchers
|
||||
fun storeOfObjectTypes(): StoreOfObjectTypes
|
||||
fun analyticsHelper(): AnalyticSpaceHelperDelegate
|
||||
fun subEventChannel(): SubscriptionEventChannel
|
||||
fun logger(): Logger
|
||||
fun localeProvider(): LocaleProvider
|
||||
fun config(): ConfigStorage
|
||||
fun userPermissionProvider(): UserPermissionProvider
|
||||
fun provideStoreOfRelations(): StoreOfRelations
|
||||
fun provideSpaceSyncAndP2PStatusProvider(): SpaceSyncAndP2PStatusProvider
|
||||
fun provideUserSettingsRepository(): UserSettingsRepository
|
||||
fun fieldParser(): FieldParser
|
||||
fun provideEventChannel(): EventChannel
|
||||
fun provideStringResourceProvider(): StringResourceProvider
|
||||
}
|
|
@ -17,6 +17,7 @@ import com.anytypeio.anytype.di.feature.MainEntrySubComponent
|
|||
import com.anytypeio.anytype.di.feature.MoveToDependencies
|
||||
import com.anytypeio.anytype.di.feature.ObjectSetSubComponent
|
||||
import com.anytypeio.anytype.di.feature.ObjectTypeChangeSubComponent
|
||||
import com.anytypeio.anytype.di.feature.ObjectTypeDependencies
|
||||
import com.anytypeio.anytype.di.feature.PersonalizationSettingsSubComponent
|
||||
import com.anytypeio.anytype.di.feature.SplashDependencies
|
||||
import com.anytypeio.anytype.di.feature.auth.DeletedAccountDependencies
|
||||
|
@ -138,6 +139,7 @@ interface MainComponent :
|
|||
LinkToObjectDependencies,
|
||||
MoveToDependencies,
|
||||
DateObjectDependencies,
|
||||
ObjectTypeDependencies,
|
||||
SelectChatReactionDependencies,
|
||||
ChatReactionDependencies,
|
||||
ParticipantComponentDependencies,
|
||||
|
@ -388,6 +390,11 @@ abstract class ComponentDependenciesModule {
|
|||
@ComponentDependenciesKey(DateObjectDependencies::class)
|
||||
abstract fun provideDateObjectDependencies(component: MainComponent): ComponentDependencies
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ComponentDependenciesKey(ObjectTypeDependencies::class)
|
||||
abstract fun provideObjectTypeDependencies(component: MainComponent): ComponentDependencies
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ComponentDependenciesKey(SelectChatReactionDependencies::class)
|
||||
|
|
|
@ -16,7 +16,9 @@ import com.anytypeio.anytype.ui.date.DateObjectFragment
|
|||
import com.anytypeio.anytype.ui.editor.EditorFragment
|
||||
import com.anytypeio.anytype.ui.editor.EditorModalFragment
|
||||
import com.anytypeio.anytype.ui.multiplayer.ShareSpaceFragment
|
||||
import com.anytypeio.anytype.ui.primitives.ObjectTypeFieldsFragment
|
||||
import com.anytypeio.anytype.ui.profile.ParticipantFragment
|
||||
import com.anytypeio.anytype.ui.primitives.ObjectTypeFragment
|
||||
import com.anytypeio.anytype.ui.relations.RelationCreateFromScratchForObjectFragment
|
||||
import com.anytypeio.anytype.ui.relations.RelationEditFragment
|
||||
import com.anytypeio.anytype.ui.search.GlobalSearchFragment
|
||||
|
@ -357,4 +359,30 @@ class Navigator : AppNavigation {
|
|||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun openObjectType(
|
||||
objectId: Id,
|
||||
space: Id
|
||||
) {
|
||||
navController?.navigate(
|
||||
resId = R.id.objectTypeScreen,
|
||||
args = ObjectTypeFragment.args(
|
||||
objectId = objectId,
|
||||
space = space
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun openCurrentObjectTypeFields(
|
||||
objectId: Id,
|
||||
space: Id
|
||||
) {
|
||||
navController?.navigate(
|
||||
resId = R.id.objectTypeFieldsScreen,
|
||||
args = ObjectTypeFieldsFragment.args(
|
||||
objectId = objectId,
|
||||
space = space
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -207,11 +207,9 @@ class AllContentFragment : BaseComposeFragment(), ObjectTypeSelectionListener {
|
|||
|
||||
is AllContentViewModel.Command.OpenTypeEditing -> {
|
||||
runCatching {
|
||||
navigation().openTypeEditingScreen(
|
||||
id = command.item.id,
|
||||
name = command.item.name,
|
||||
icon = (command.item.icon as? ObjectIcon.Basic.Emoji)?.unicode ?: "",
|
||||
readOnly = command.item.readOnly
|
||||
navigation().openObjectType(
|
||||
objectId = command.item.id,
|
||||
space = space
|
||||
)
|
||||
}.onFailure {
|
||||
toast("Failed to open type editing screen")
|
||||
|
|
|
@ -167,7 +167,7 @@ import com.anytypeio.anytype.ui.objects.creation.ObjectTypeSelectionFragment
|
|||
import com.anytypeio.anytype.ui.objects.creation.ObjectTypeUpdateFragment
|
||||
import com.anytypeio.anytype.ui.objects.types.pickers.ObjectTypeSelectionListener
|
||||
import com.anytypeio.anytype.ui.objects.types.pickers.ObjectTypeUpdateListener
|
||||
import com.anytypeio.anytype.ui.relations.ObjectRelationListFragment
|
||||
import com.anytypeio.anytype.ui.primitives.ObjectFieldsFragment
|
||||
import com.anytypeio.anytype.ui.relations.RelationAddToObjectBlockFragment
|
||||
import com.anytypeio.anytype.ui.relations.RelationDateValueFragment
|
||||
import com.anytypeio.anytype.ui.relations.RelationTextValueFragment
|
||||
|
@ -1074,10 +1074,10 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
|
|||
R.id.pageScreen,
|
||||
R.id.objectRelationListScreen,
|
||||
bundleOf(
|
||||
ObjectRelationListFragment.ARG_CTX to command.ctx,
|
||||
ObjectRelationListFragment.ARG_SPACE to space,
|
||||
ObjectRelationListFragment.ARG_TARGET to command.target,
|
||||
ObjectRelationListFragment.ARG_LOCKED to command.isLocked,
|
||||
ObjectFieldsFragment.ARG_CTX to command.ctx,
|
||||
ObjectFieldsFragment.ARG_SPACE to space,
|
||||
ObjectFieldsFragment.ARG_TARGET to command.target,
|
||||
ObjectFieldsFragment.ARG_LOCKED to command.isLocked,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import com.anytypeio.anytype.presentation.editor.layout.ObjectLayoutViewModel
|
|||
import com.anytypeio.anytype.presentation.objects.ObjectLayoutView
|
||||
import javax.inject.Inject
|
||||
|
||||
@Deprecated("epic Primitives")
|
||||
class ObjectLayoutFragment : BaseBottomSheetFragment<FragmentObjectLayoutBinding>() {
|
||||
|
||||
private val ctx: String get() = argString(CONTEXT_ID_KEY)
|
||||
|
|
|
@ -31,13 +31,12 @@ import com.anytypeio.anytype.presentation.objects.menu.ObjectMenuViewModelBase
|
|||
import com.anytypeio.anytype.ui.base.navigation
|
||||
import com.anytypeio.anytype.ui.editor.cover.SelectCoverObjectFragment
|
||||
import com.anytypeio.anytype.ui.editor.cover.SelectCoverObjectSetFragment
|
||||
import com.anytypeio.anytype.ui.editor.layout.ObjectLayoutFragment
|
||||
import com.anytypeio.anytype.ui.editor.modals.IconPickerFragmentBase
|
||||
import com.anytypeio.anytype.ui.history.VersionHistoryFragment
|
||||
import com.anytypeio.anytype.ui.linking.BacklinkAction
|
||||
import com.anytypeio.anytype.ui.linking.BacklinkOrAddToObjectFragment
|
||||
import com.anytypeio.anytype.ui.moving.OnMoveToAction
|
||||
import com.anytypeio.anytype.ui.relations.ObjectRelationListFragment
|
||||
import com.anytypeio.anytype.ui.primitives.ObjectFieldsFragment
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import timber.log.Timber
|
||||
|
||||
|
@ -84,7 +83,7 @@ abstract class ObjectMenuBaseFragment :
|
|||
override fun onStart() {
|
||||
click(binding.objectDiagnostics) { vm.onDiagnosticsClicked(ctx = ctx) }
|
||||
click(binding.optionHistory) { vm.onHistoryClicked(ctx = ctx, space = space) }
|
||||
click(binding.optionLayout) { vm.onLayoutClicked(ctx = ctx, space = space) }
|
||||
click(binding.optionDescription) { vm.onDescriptionClicked(ctx = ctx, space = space) }
|
||||
click(binding.optionIcon) { vm.onIconClicked(ctx = ctx, space = space) }
|
||||
click(binding.optionRelations) { vm.onRelationsClicked() }
|
||||
click(binding.optionCover) { vm.onCoverClicked(ctx = ctx, space = space) }
|
||||
|
@ -116,19 +115,22 @@ abstract class ObjectMenuBaseFragment :
|
|||
private fun renderOptions(options: ObjectMenuOptionsProvider.Options) {
|
||||
val iconVisibility = options.hasIcon.toVisibility()
|
||||
val coverVisibility = options.hasCover.toVisibility()
|
||||
val layoutVisibility = options.hasLayout.toVisibility()
|
||||
val relationsVisibility = options.hasRelations.toVisibility()
|
||||
val historyVisibility = options.hasHistory.toVisibility()
|
||||
val objectDiagnosticsVisibility = options.hasDiagnosticsVisibility.toVisibility()
|
||||
|
||||
if (options.hasDescriptionShow) {
|
||||
binding.optionDescription.setAction(setAsHide = false)
|
||||
} else {
|
||||
binding.optionDescription.setAction(setAsHide = true)
|
||||
}
|
||||
|
||||
binding.optionIcon.visibility = iconVisibility
|
||||
binding.optionCover.visibility = coverVisibility
|
||||
binding.optionLayout.visibility = layoutVisibility
|
||||
binding.optionRelations.visibility = relationsVisibility
|
||||
binding.optionHistory.visibility = historyVisibility
|
||||
binding.iconDivider.visibility = iconVisibility
|
||||
binding.coverDivider.visibility = coverVisibility
|
||||
binding.layoutDivider.visibility = layoutVisibility
|
||||
binding.relationsDivider.visibility = relationsVisibility
|
||||
binding.historyDivider.visibility = historyVisibility
|
||||
binding.objectDiagnostics.visibility = objectDiagnosticsVisibility
|
||||
|
@ -139,7 +141,6 @@ abstract class ObjectMenuBaseFragment :
|
|||
when (command) {
|
||||
ObjectMenuViewModelBase.Command.OpenObjectCover -> openObjectCover()
|
||||
ObjectMenuViewModelBase.Command.OpenObjectIcons -> openObjectIcons()
|
||||
ObjectMenuViewModelBase.Command.OpenObjectLayout -> openObjectLayout()
|
||||
ObjectMenuViewModelBase.Command.OpenObjectRelations -> openObjectRelations()
|
||||
ObjectMenuViewModelBase.Command.OpenSetCover -> openSetCover()
|
||||
ObjectMenuViewModelBase.Command.OpenSetIcons -> openSetIcons()
|
||||
|
@ -218,20 +219,15 @@ abstract class ObjectMenuBaseFragment :
|
|||
)
|
||||
}
|
||||
|
||||
private fun openObjectLayout() {
|
||||
val fr = ObjectLayoutFragment.new(ctx = ctx, space = space)
|
||||
fr.showChildFragment()
|
||||
}
|
||||
|
||||
private fun openObjectRelations() {
|
||||
findNavController().navigate(
|
||||
R.id.objectRelationListScreen,
|
||||
bundleOf(
|
||||
ObjectRelationListFragment.ARG_CTX to ctx,
|
||||
ObjectRelationListFragment.ARG_SPACE to space,
|
||||
ObjectRelationListFragment.ARG_TARGET to null,
|
||||
ObjectRelationListFragment.ARG_LOCKED to isLocked,
|
||||
ObjectRelationListFragment.ARG_SET_FLOW to false
|
||||
ObjectFieldsFragment.ARG_CTX to ctx,
|
||||
ObjectFieldsFragment.ARG_SPACE to space,
|
||||
ObjectFieldsFragment.ARG_TARGET to null,
|
||||
ObjectFieldsFragment.ARG_LOCKED to isLocked,
|
||||
ObjectFieldsFragment.ARG_SET_FLOW to false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -240,11 +236,11 @@ abstract class ObjectMenuBaseFragment :
|
|||
findNavController().navigate(
|
||||
R.id.objectRelationListScreen,
|
||||
bundleOf(
|
||||
ObjectRelationListFragment.ARG_CTX to ctx,
|
||||
ObjectRelationListFragment.ARG_SPACE to space,
|
||||
ObjectRelationListFragment.ARG_TARGET to null,
|
||||
ObjectRelationListFragment.ARG_LOCKED to isLocked,
|
||||
ObjectRelationListFragment.ARG_SET_FLOW to true
|
||||
ObjectFieldsFragment.ARG_CTX to ctx,
|
||||
ObjectFieldsFragment.ARG_SPACE to space,
|
||||
ObjectFieldsFragment.ARG_TARGET to null,
|
||||
ObjectFieldsFragment.ARG_LOCKED to isLocked,
|
||||
ObjectFieldsFragment.ARG_SET_FLOW to true
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,313 @@
|
|||
package com.anytypeio.anytype.ui.primitives
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.fragment.compose.content
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.anytypeio.anytype.R
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.Key
|
||||
import com.anytypeio.anytype.core_models.TimeInMillis
|
||||
import com.anytypeio.anytype.core_models.primitives.SpaceId
|
||||
import com.anytypeio.anytype.core_ui.features.fields.FieldListScreen
|
||||
import com.anytypeio.anytype.core_utils.ext.arg
|
||||
import com.anytypeio.anytype.core_utils.ext.argString
|
||||
import com.anytypeio.anytype.core_utils.ext.argStringOrNull
|
||||
import com.anytypeio.anytype.core_utils.ext.safeNavigate
|
||||
import com.anytypeio.anytype.core_utils.ext.setupBottomSheetBehavior
|
||||
import com.anytypeio.anytype.core_utils.ext.subscribe
|
||||
import com.anytypeio.anytype.core_utils.ext.toast
|
||||
import com.anytypeio.anytype.core_utils.ext.withParent
|
||||
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetComposeFragment
|
||||
import com.anytypeio.anytype.di.common.componentManager
|
||||
import com.anytypeio.anytype.di.feature.DefaultComponentParam
|
||||
import com.anytypeio.anytype.feature_object_type.fields.ui.LocalInfoScreen
|
||||
import com.anytypeio.anytype.presentation.relations.ObjectRelationListViewModelFactory
|
||||
import com.anytypeio.anytype.presentation.relations.RelationListViewModel
|
||||
import com.anytypeio.anytype.presentation.relations.RelationListViewModel.Command
|
||||
import com.anytypeio.anytype.presentation.relations.value.tagstatus.RelationContext
|
||||
import com.anytypeio.anytype.ui.base.navigation
|
||||
import com.anytypeio.anytype.ui.editor.OnFragmentInteractionListener
|
||||
import com.anytypeio.anytype.ui.relations.RelationDateValueFragment
|
||||
import com.anytypeio.anytype.ui.relations.RelationTextValueFragment
|
||||
import com.anytypeio.anytype.ui.relations.value.ObjectValueFragment
|
||||
import com.anytypeio.anytype.ui.relations.value.TagOrStatusValueFragment
|
||||
import javax.inject.Inject
|
||||
import kotlin.getValue
|
||||
import timber.log.Timber
|
||||
|
||||
class ObjectFieldsFragment : BaseBottomSheetComposeFragment(),
|
||||
RelationTextValueFragment.TextValueEditReceiver,
|
||||
RelationDateValueFragment.DateValueEditReceiver {
|
||||
|
||||
private val vm by viewModels<RelationListViewModel> { factory }
|
||||
|
||||
private val ctx: String get() = argString(ARG_CTX)
|
||||
private val space: String get() = argString(ARG_SPACE)
|
||||
private val target: String? get() = argStringOrNull(ARG_TARGET)
|
||||
private val isSetFlow: Boolean get() = arg(ARG_SET_FLOW)
|
||||
|
||||
@Inject
|
||||
lateinit var factory: ObjectRelationListViewModelFactory
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
) =
|
||||
content {
|
||||
MaterialTheme {
|
||||
FieldListScreen(
|
||||
state = vm.views.collectAsStateWithLifecycle().value,
|
||||
onRelationClicked = {
|
||||
vm.onRelationClicked(
|
||||
ctx = ctx,
|
||||
target = target,
|
||||
view = it.view
|
||||
)
|
||||
},
|
||||
onLocalInfoIconClicked = {
|
||||
vm.onShowLocalInfo()
|
||||
},
|
||||
onTypeIconClicked = {
|
||||
vm.onTypeIconClicked()
|
||||
},
|
||||
onRemoveFromObjectClicked = vm::onRemoveFromObjectClicked,
|
||||
onAddToTypeClicked = vm::onAddToTypeClicked
|
||||
)
|
||||
val showLocalFieldExplanationScreen = vm.showLocalInfo.collectAsStateWithLifecycle().value
|
||||
if (showLocalFieldExplanationScreen) {
|
||||
val bottomSheetState = rememberModalBottomSheetState(
|
||||
skipPartiallyExpanded = true
|
||||
)
|
||||
LocalInfoScreen(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
bottomSheetState = bottomSheetState,
|
||||
onDismiss = { vm.onDismissLocalInfo() }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setupBottomSheetBehavior(DEFAULT_PADDING_TOP)
|
||||
}
|
||||
|
||||
private fun execute(command: Command) {
|
||||
when (command) {
|
||||
is Command.EditTextRelationValue -> {
|
||||
runCatching {
|
||||
val fr = RelationTextValueFragment.new(
|
||||
ctx = ctx,
|
||||
relationKey = command.relationKey,
|
||||
objectId = command.target,
|
||||
isLocked = command.isLocked,
|
||||
flow = if (isSetFlow)
|
||||
RelationTextValueFragment.FLOW_DATAVIEW
|
||||
else
|
||||
RelationTextValueFragment.FLOW_DEFAULT,
|
||||
space = space
|
||||
)
|
||||
fr.showChildFragment()
|
||||
}.onFailure {
|
||||
Timber.e(it, "Error while opening relation text value from relation list")
|
||||
}
|
||||
}
|
||||
|
||||
is Command.EditDateRelationValue -> {
|
||||
val fr = RelationDateValueFragment.new(
|
||||
ctx = ctx,
|
||||
space = space,
|
||||
relationKey = command.relationKey,
|
||||
objectId = command.target,
|
||||
flow = if (isSetFlow) {
|
||||
RelationDateValueFragment.FLOW_SET_OR_COLLECTION
|
||||
} else {
|
||||
RelationDateValueFragment.FLOW_DEFAULT
|
||||
},
|
||||
isLocked = command.isLocked
|
||||
)
|
||||
fr.showChildFragment()
|
||||
}
|
||||
|
||||
is Command.EditFileObjectRelationValue -> {
|
||||
val relationContext =
|
||||
if (isSetFlow) RelationContext.OBJECT_SET else RelationContext.OBJECT
|
||||
findNavController().safeNavigate(
|
||||
R.id.objectRelationListScreen,
|
||||
R.id.objectValueScreen,
|
||||
ObjectValueFragment.args(
|
||||
ctx = command.ctx,
|
||||
space = space,
|
||||
obj = command.target,
|
||||
relation = command.relationKey,
|
||||
isLocked = command.isLocked,
|
||||
relationContext = relationContext
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
is Command.SetRelationKey -> {
|
||||
withParent<OnFragmentInteractionListener> {
|
||||
onSetRelationKeyClicked(
|
||||
blockId = command.blockId,
|
||||
key = command.key
|
||||
)
|
||||
}
|
||||
dismiss()
|
||||
}
|
||||
|
||||
is Command.EditTagOrStatusRelationValue -> {
|
||||
val relationContext =
|
||||
if (isSetFlow) RelationContext.OBJECT_SET else RelationContext.OBJECT
|
||||
val bundle = TagOrStatusValueFragment.args(
|
||||
ctx = command.ctx,
|
||||
space = space,
|
||||
obj = command.target,
|
||||
relation = command.relationKey,
|
||||
isLocked = command.isLocked,
|
||||
context = relationContext
|
||||
)
|
||||
findNavController().safeNavigate(
|
||||
R.id.objectRelationListScreen,
|
||||
R.id.nav_relations,
|
||||
bundle
|
||||
)
|
||||
}
|
||||
|
||||
is Command.NavigateToDateObject -> {
|
||||
runCatching {
|
||||
navigation().openDateObject(
|
||||
objectId = command.objectId,
|
||||
space = space
|
||||
)
|
||||
}.onFailure {
|
||||
Timber.e(it, "Error while opening date object from relation list")
|
||||
}
|
||||
}
|
||||
|
||||
is Command.NavigateToObjectType -> {
|
||||
runCatching {
|
||||
navigation().openCurrentObjectTypeFields(
|
||||
objectId = command.objectTypeId,
|
||||
space = space
|
||||
)
|
||||
}.onFailure {
|
||||
Timber.e(it, "Error while opening object type fields from object fields list")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
jobs += lifecycleScope.subscribe(vm.commands) { command -> execute(command) }
|
||||
jobs += lifecycleScope.subscribe(vm.toasts) { toast(it) }
|
||||
super.onStart()
|
||||
vm.onStartListMode(ctx)
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
vm.onStop()
|
||||
}
|
||||
|
||||
override fun onTextValueChanged(ctx: Id, text: String, objectId: Id, relationKey: Key) {
|
||||
vm.onRelationTextValueChanged(
|
||||
ctx = ctx,
|
||||
relationKey = relationKey,
|
||||
value = text,
|
||||
isValueEmpty = text.isEmpty()
|
||||
)
|
||||
}
|
||||
|
||||
override fun onNumberValueChanged(ctx: Id, number: Double?, objectId: Id, relationKey: Key) {
|
||||
vm.onRelationTextValueChanged(
|
||||
ctx = ctx,
|
||||
relationKey = relationKey,
|
||||
value = number,
|
||||
isValueEmpty = number == null
|
||||
)
|
||||
}
|
||||
|
||||
override fun onDateValueChanged(
|
||||
ctx: Id,
|
||||
timeInSeconds: Number?,
|
||||
objectId: Id,
|
||||
relationKey: Key
|
||||
) {
|
||||
vm.onRelationTextValueChanged(
|
||||
ctx = ctx,
|
||||
relationKey = relationKey,
|
||||
value = timeInSeconds,
|
||||
isValueEmpty = timeInSeconds == null
|
||||
)
|
||||
}
|
||||
|
||||
override fun onOpenDateObject(timeInMillis: TimeInMillis) {
|
||||
vm.onOpenDateObjectByTimeInMillis(timeInMillis)
|
||||
}
|
||||
|
||||
override fun injectDependencies() {
|
||||
val param = DefaultComponentParam(
|
||||
ctx = ctx,
|
||||
space = SpaceId(space)
|
||||
)
|
||||
if (isSetFlow) {
|
||||
componentManager().objectSetRelationListComponent.get(param).inject(this)
|
||||
} else {
|
||||
componentManager().objectRelationListComponent.get(param).inject(this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun releaseDependencies() {
|
||||
if (isSetFlow) {
|
||||
componentManager().objectSetRelationListComponent.release()
|
||||
} else {
|
||||
componentManager().objectRelationListComponent.release()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This screen should be started from Objects with Editor Layouts
|
||||
* or from objects with Set or Collection Layouts
|
||||
* @param isSetFlow - true if started from Set or Collection
|
||||
*/
|
||||
companion object {
|
||||
fun new(
|
||||
ctx: Id,
|
||||
space: Id,
|
||||
target: String?,
|
||||
locked: Boolean = false,
|
||||
isSetFlow: Boolean = false,
|
||||
) = ObjectFieldsFragment().apply {
|
||||
arguments = bundleOf(
|
||||
ARG_CTX to ctx,
|
||||
ARG_SPACE to space,
|
||||
ARG_TARGET to target,
|
||||
ARG_LOCKED to locked,
|
||||
ARG_SET_FLOW to isSetFlow
|
||||
)
|
||||
}
|
||||
|
||||
const val ARG_CTX = "arg.document-relation.ctx"
|
||||
const val ARG_SPACE = "arg.document-relation.space"
|
||||
const val ARG_TARGET = "arg.document-relation.target"
|
||||
const val ARG_LOCKED = "arg.document-relation.locked"
|
||||
const val ARG_SET_FLOW = "arg.document-relation.set-flow"
|
||||
|
||||
const val DEFAULT_PADDING_TOP = 10
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
package com.anytypeio.anytype.ui.primitives
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.fragment.compose.content
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.primitives.SpaceId
|
||||
import com.anytypeio.anytype.core_utils.ext.argString
|
||||
import com.anytypeio.anytype.core_utils.ext.setupBottomSheetBehavior
|
||||
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetComposeFragment
|
||||
import com.anytypeio.anytype.di.common.componentManager
|
||||
import com.anytypeio.anytype.feature_object_type.fields.ui.FieldsMainScreen
|
||||
import com.anytypeio.anytype.feature_object_type.ui.ObjectTypeVmParams
|
||||
import com.anytypeio.anytype.feature_object_type.viewmodel.ObjectTypeVMFactory
|
||||
import com.anytypeio.anytype.feature_object_type.viewmodel.ObjectTypeViewModel
|
||||
import javax.inject.Inject
|
||||
import kotlin.getValue
|
||||
|
||||
class ObjectTypeFieldsFragment : BaseBottomSheetComposeFragment() {
|
||||
|
||||
@Inject
|
||||
lateinit var factory: ObjectTypeVMFactory
|
||||
|
||||
private val vm by viewModels<ObjectTypeViewModel> { factory }
|
||||
|
||||
private val space get() = argString(ARG_SPACE)
|
||||
private val typeId get() = argString(ARG_OBJECT_ID)
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
) = content {
|
||||
MaterialTheme {
|
||||
FieldsMainScreen(
|
||||
uiFieldsListState = vm.uiFieldsListState.collectAsStateWithLifecycle().value,
|
||||
uiTitleState = vm.uiTitleState.collectAsStateWithLifecycle().value,
|
||||
uiIconState = vm.uiIconState.collectAsStateWithLifecycle().value,
|
||||
uiFieldEditOrNewState = vm.uiFieldEditOrNewState.collectAsStateWithLifecycle().value,
|
||||
uiFieldLocalInfoState = vm.uiFieldLocalInfoState.collectAsStateWithLifecycle().value,
|
||||
uiAddFieldsScreenState = vm.uiAddFieldsState.collectAsStateWithLifecycle().value,
|
||||
fieldEvent = vm::onFieldEvent
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setupBottomSheetBehavior(DEFAULT_PADDING_TOP)
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
vm.onStart()
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
vm.onStop()
|
||||
}
|
||||
|
||||
override fun injectDependencies() {
|
||||
val params = ObjectTypeVmParams(
|
||||
spaceId = SpaceId(space),
|
||||
objectId = typeId,
|
||||
withSubscriptions = false,
|
||||
showHiddenFields = true
|
||||
)
|
||||
componentManager().objectTypeComponent.get(params).inject(this)
|
||||
}
|
||||
|
||||
override fun releaseDependencies() {
|
||||
componentManager().objectTypeComponent.release()
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val ARG_SPACE = "arg.object.type.space"
|
||||
const val ARG_OBJECT_ID = "arg.object.type.object_id"
|
||||
|
||||
fun args(space: Id, objectId: Id) = bundleOf(
|
||||
ARG_SPACE to space,
|
||||
ARG_OBJECT_ID to objectId
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,331 @@
|
|||
package com.anytypeio.anytype.ui.primitives
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.setFragmentResultListener
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.fragment.compose.content
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.anytypeio.anytype.R
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.primitives.SpaceId
|
||||
import com.anytypeio.anytype.core_ui.views.BaseAlertDialog
|
||||
import com.anytypeio.anytype.core_utils.ext.argString
|
||||
import com.anytypeio.anytype.core_utils.ext.subscribe
|
||||
import com.anytypeio.anytype.core_utils.ext.toast
|
||||
import com.anytypeio.anytype.core_utils.ui.BaseComposeFragment
|
||||
import com.anytypeio.anytype.di.common.componentManager
|
||||
import com.anytypeio.anytype.feature_object_type.ui.ObjectTypeMainScreen
|
||||
import com.anytypeio.anytype.feature_object_type.viewmodel.ObjectTypeVMFactory
|
||||
import com.anytypeio.anytype.feature_object_type.viewmodel.ObjectTypeViewModel
|
||||
import com.anytypeio.anytype.feature_object_type.ui.ObjectTypeVmParams
|
||||
import com.anytypeio.anytype.feature_object_type.ui.UiErrorState
|
||||
import com.anytypeio.anytype.feature_object_type.fields.ui.FieldsMainScreen
|
||||
import com.anytypeio.anytype.feature_object_type.ui.ObjectTypeCommand
|
||||
import com.anytypeio.anytype.presentation.home.OpenObjectNavigation
|
||||
import com.anytypeio.anytype.ui.chats.ChatFragment
|
||||
import com.anytypeio.anytype.ui.date.DateObjectFragment
|
||||
import com.anytypeio.anytype.ui.editor.EditorFragment
|
||||
import com.anytypeio.anytype.ui.editor.EditorModalFragment
|
||||
import com.anytypeio.anytype.ui.profile.ParticipantFragment
|
||||
import com.anytypeio.anytype.ui.relations.RelationAddToObjectFragment
|
||||
import com.anytypeio.anytype.ui.sets.ObjectSetFragment
|
||||
import com.anytypeio.anytype.ui.templates.EditorTemplateFragment.Companion.TYPE_TEMPLATE_EDIT
|
||||
import com.anytypeio.anytype.ui.types.picker.REQUEST_KEY_PICK_EMOJI
|
||||
import com.anytypeio.anytype.ui.types.picker.REQUEST_KEY_REMOVE_EMOJI
|
||||
import com.anytypeio.anytype.ui.types.picker.RESULT_EMOJI_UNICODE
|
||||
import com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi
|
||||
import com.google.accompanist.navigation.material.rememberBottomSheetNavigator
|
||||
import javax.inject.Inject
|
||||
import kotlin.getValue
|
||||
import timber.log.Timber
|
||||
|
||||
class ObjectTypeFragment : BaseComposeFragment() {
|
||||
@Inject
|
||||
lateinit var factory: ObjectTypeVMFactory
|
||||
|
||||
private val vm by viewModels<ObjectTypeViewModel> { factory }
|
||||
private lateinit var navComposeController: NavHostController
|
||||
|
||||
private val space get() = argString(ARG_SPACE)
|
||||
private val objectId get() = argString(ARG_OBJECT_ID)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setFragmentResultListener(REQUEST_KEY_PICK_EMOJI) { _, bundle ->
|
||||
val res = requireNotNull(bundle.getString(RESULT_EMOJI_UNICODE))
|
||||
vm.updateIcon(res)
|
||||
}
|
||||
setFragmentResultListener(REQUEST_KEY_REMOVE_EMOJI) { _, _ ->
|
||||
vm.removeIcon()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
) = content {
|
||||
MaterialTheme {
|
||||
ObjectTypeScreen()
|
||||
ErrorScreen()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
subscribe(vm.commands) { command ->
|
||||
Timber.d("Received command: $command")
|
||||
when (command) {
|
||||
ObjectTypeCommand.Back -> {
|
||||
runCatching {
|
||||
findNavController().popBackStack()
|
||||
}.onFailure { e ->
|
||||
Timber.e(e, "Error while exiting back from object type screen")
|
||||
}
|
||||
}
|
||||
ObjectTypeCommand.OpenEmojiPicker -> {
|
||||
runCatching {
|
||||
findNavController().navigate(R.id.openEmojiPicker)
|
||||
}.onFailure {
|
||||
Timber.w("Error while opening emoji picker")
|
||||
}
|
||||
}
|
||||
|
||||
is ObjectTypeCommand.OpenTemplate -> {
|
||||
findNavController().navigate(
|
||||
R.id.nav_editor_modal,
|
||||
bundleOf(
|
||||
EditorModalFragment.ARG_TEMPLATE_ID to command.templateId,
|
||||
EditorModalFragment.ARG_TEMPLATE_TYPE_ID to command.typeId,
|
||||
EditorModalFragment.ARG_TEMPLATE_TYPE_KEY to command.typeKey,
|
||||
EditorModalFragment.ARG_SCREEN_TYPE to TYPE_TEMPLATE_EDIT,
|
||||
EditorModalFragment.ARG_SPACE_ID to command.spaceId
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
ObjectTypeCommand.OpenFieldsScreen -> {
|
||||
navComposeController.navigate(OBJ_TYPE_FIELDS)
|
||||
}
|
||||
|
||||
is ObjectTypeCommand.OpenAddFieldScreen -> {
|
||||
RelationAddToObjectFragment.new(
|
||||
ctx = command.typeId,
|
||||
space = command.space,
|
||||
isSetOrCollection = command.isSet
|
||||
).showChildFragment()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
vm.onStart()
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
vm.onStop()
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterialNavigationApi::class)
|
||||
@Composable
|
||||
fun ObjectTypeScreen() {
|
||||
val bottomSheetNavigator = rememberBottomSheetNavigator()
|
||||
navComposeController = rememberNavController(bottomSheetNavigator)
|
||||
NavHost(
|
||||
navController = navComposeController,
|
||||
startDestination = OBJ_TYPE_MAIN
|
||||
) {
|
||||
composable(route = OBJ_TYPE_MAIN) {
|
||||
ObjectTypeMainScreen(
|
||||
uiSyncStatusBadgeState = vm.uiSyncStatusBadgeState.collectAsStateWithLifecycle().value,
|
||||
uiSyncStatusState = vm.uiSyncStatusWidgetState.collectAsStateWithLifecycle().value,
|
||||
uiTitleState = vm.uiTitleState.collectAsStateWithLifecycle().value,
|
||||
uiIconState = vm.uiIconState.collectAsStateWithLifecycle().value,
|
||||
uiFieldsButtonState = vm.uiFieldsButtonState.collectAsStateWithLifecycle().value,
|
||||
uiLayoutButtonState = vm.uiLayoutButtonState.collectAsStateWithLifecycle().value,
|
||||
uiTemplatesHeaderState = vm.uiTemplatesHeaderState.collectAsStateWithLifecycle().value,
|
||||
uiTemplatesAddIconState = vm.uiTemplatesAddIconState.collectAsStateWithLifecycle().value,
|
||||
uiTemplatesListState = vm.uiTemplatesListState.collectAsStateWithLifecycle().value,
|
||||
uiObjectsHeaderState = vm.uiObjectsHeaderState.collectAsStateWithLifecycle().value,
|
||||
uiObjectsAddIconState = vm.uiObjectsAddIconState.collectAsStateWithLifecycle().value,
|
||||
uiObjectsSettingsIconState = vm.uiObjectsSettingsIconState.collectAsStateWithLifecycle().value,
|
||||
uiObjectsMenuState = vm.uiMenuState.collectAsStateWithLifecycle().value,
|
||||
uiObjectsListState = vm.uiObjectsListState.collectAsStateWithLifecycle().value,
|
||||
uiContentState = vm.uiContentState.collectAsStateWithLifecycle().value,
|
||||
uiDeleteAlertState = vm.uiAlertState.collectAsStateWithLifecycle().value,
|
||||
uiEditButtonState = vm.uiEditButtonState.collectAsStateWithLifecycle().value,
|
||||
uiLayoutTypeState = vm.uiTypeLayoutsState.collectAsStateWithLifecycle().value,
|
||||
onTypeEvent = vm::onTypeEvent
|
||||
)
|
||||
}
|
||||
composable(route = OBJ_TYPE_FIELDS) {
|
||||
FieldsMainScreen(
|
||||
uiFieldsListState = vm.uiFieldsListState.collectAsStateWithLifecycle().value,
|
||||
uiTitleState = vm.uiTitleState.collectAsStateWithLifecycle().value,
|
||||
uiIconState = vm.uiIconState.collectAsStateWithLifecycle().value,
|
||||
uiFieldEditOrNewState = vm.uiFieldEditOrNewState.collectAsStateWithLifecycle().value,
|
||||
uiFieldLocalInfoState = vm.uiFieldLocalInfoState.collectAsStateWithLifecycle().value,
|
||||
uiAddFieldsScreenState = vm.uiAddFieldsState.collectAsStateWithLifecycle().value,
|
||||
fieldEvent = vm::onFieldEvent
|
||||
)
|
||||
}
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
vm.navigation.collect { nav ->
|
||||
when (nav) {
|
||||
is OpenObjectNavigation.OpenEditor -> {
|
||||
findNavController().navigate(
|
||||
R.id.objectNavigation,
|
||||
EditorFragment.args(
|
||||
ctx = nav.target,
|
||||
space = nav.space
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
is OpenObjectNavigation.OpenDataView -> {
|
||||
findNavController().navigate(
|
||||
R.id.dataViewNavigation,
|
||||
ObjectSetFragment.args(
|
||||
ctx = nav.target,
|
||||
space = nav.space
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
is OpenObjectNavigation.OpenParticipant -> {
|
||||
runCatching {
|
||||
findNavController().navigate(
|
||||
R.id.participantScreen,
|
||||
ParticipantFragment.args(
|
||||
objectId = nav.target,
|
||||
space = nav.space
|
||||
)
|
||||
)
|
||||
}.onFailure {
|
||||
Timber.w("Error while opening participant screen")
|
||||
}
|
||||
}
|
||||
|
||||
is OpenObjectNavigation.OpenChat -> {
|
||||
findNavController().navigate(
|
||||
R.id.chatScreen,
|
||||
ChatFragment.args(
|
||||
ctx = nav.target,
|
||||
space = nav.space
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
OpenObjectNavigation.NonValidObject -> {
|
||||
toast(getString(R.string.error_non_valid_object))
|
||||
}
|
||||
|
||||
is OpenObjectNavigation.OpenDateObject -> {
|
||||
runCatching {
|
||||
findNavController().navigate(
|
||||
R.id.dateObjectScreen,
|
||||
DateObjectFragment.args(
|
||||
objectId = nav.target,
|
||||
space = nav.space
|
||||
)
|
||||
)
|
||||
}.onFailure {
|
||||
Timber.e(it, "Failed to navigate to date object screen")
|
||||
}
|
||||
}
|
||||
|
||||
is OpenObjectNavigation.UnexpectedLayoutError -> {
|
||||
toast(getString(R.string.error_unexpected_layout))
|
||||
}
|
||||
|
||||
else -> {
|
||||
// Do nothing.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun ErrorScreen() {
|
||||
val errorStateScreen = vm.errorState.collectAsStateWithLifecycle().value
|
||||
if (errorStateScreen is UiErrorState.Show) {
|
||||
when (val r = errorStateScreen.reason) {
|
||||
is UiErrorState.Reason.ErrorGettingObjects -> {
|
||||
BaseAlertDialog(
|
||||
dialogText = "${stringResource(R.string.object_type_open_type_error)}:\n${r.msg}",
|
||||
buttonText = stringResource(id = R.string.membership_error_button_text_dismiss),
|
||||
onButtonClick = vm::closeObject,
|
||||
onDismissRequest = vm::closeObject
|
||||
)
|
||||
}
|
||||
is UiErrorState.Reason.Other -> {
|
||||
BaseAlertDialog(
|
||||
dialogText = r.msg,
|
||||
buttonText = stringResource(id = R.string.membership_error_button_text_dismiss),
|
||||
onButtonClick = vm::hideError,
|
||||
onDismissRequest = vm::hideError
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
when (val state = errorStateScreen) {
|
||||
UiErrorState.Hidden -> {
|
||||
|
||||
}
|
||||
|
||||
is UiErrorState.Show -> {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun injectDependencies() {
|
||||
val params = ObjectTypeVmParams(
|
||||
spaceId = SpaceId(space),
|
||||
objectId = objectId,
|
||||
withSubscriptions = true,
|
||||
showHiddenFields = true
|
||||
)
|
||||
componentManager().objectTypeComponent.get(params).inject(this)
|
||||
}
|
||||
|
||||
override fun releaseDependencies() {
|
||||
componentManager().objectTypeComponent.release()
|
||||
}
|
||||
|
||||
override fun onApplyWindowRootInsets(view: View) {
|
||||
// Skipping this, since window insets will be applied by compose code.
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val OBJ_TYPE_MAIN = "obj_type_main"
|
||||
private const val OBJ_TYPE_FIELDS = "obj_fields"
|
||||
const val ARG_SPACE = "arg.object.type.space"
|
||||
const val ARG_OBJECT_ID = "arg.object.type.object_id"
|
||||
|
||||
fun args(space: Id, objectId: Id) = bundleOf(
|
||||
ARG_SPACE to space,
|
||||
ARG_OBJECT_ID to objectId
|
||||
)
|
||||
}
|
||||
}
|
|
@ -41,6 +41,7 @@ import com.anytypeio.anytype.ui.relations.value.TagOrStatusValueFragment
|
|||
import javax.inject.Inject
|
||||
import timber.log.Timber
|
||||
|
||||
@Deprecated("Legacy, epic Primitives, use ObjectFieldsFragment instead")
|
||||
open class ObjectRelationListFragment : BaseBottomSheetFragment<FragmentRelationListBinding>(),
|
||||
RelationTextValueFragment.TextValueEditReceiver,
|
||||
RelationDateValueFragment.DateValueEditReceiver {
|
||||
|
@ -194,6 +195,10 @@ open class ObjectRelationListFragment : BaseBottomSheetFragment<FragmentRelation
|
|||
Timber.e(it, "Error while opening date object from relation list")
|
||||
}
|
||||
}
|
||||
|
||||
is Command.NavigateToObjectType -> {
|
||||
//do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -28,24 +28,28 @@
|
|||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.anytypeio.anytype.core_ui.widgets.ObjectMenuItemWidget
|
||||
android:id="@+id/optionIcon"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:background="@drawable/default_ripple"
|
||||
app:icon="@drawable/ic_object_menu_icon"
|
||||
app:subtitle="@string/icon_description"
|
||||
app:title="@string/icon" />
|
||||
<com.anytypeio.anytype.core_ui.widgets.ObjectMenuItemWidget
|
||||
android:id="@+id/optionIcon"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:background="@drawable/default_ripple"
|
||||
app:icon="@drawable/ic_obj_settings_icon_24"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/dragger"
|
||||
app:title="@string/icon" />
|
||||
|
||||
<View
|
||||
android:id="@+id/iconDivider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0.5dp"
|
||||
android:layout_marginStart="72dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:background="@color/shape_primary" />
|
||||
<View
|
||||
android:id="@+id/iconDivider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0.5dp"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:background="@color/shape_primary"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/optionIcon" />
|
||||
|
||||
<com.anytypeio.anytype.core_ui.widgets.ObjectMenuItemWidget
|
||||
android:id="@+id/optionCover"
|
||||
|
@ -53,7 +57,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@drawable/default_ripple"
|
||||
app:icon="@drawable/ic_object_menu_cover"
|
||||
app:icon="@drawable/ic_obj_settings_cover_24"
|
||||
app:subtitle="@string/cover_description"
|
||||
app:title="@string/cover" />
|
||||
|
||||
|
@ -61,28 +65,28 @@
|
|||
android:id="@+id/coverDivider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0.5dp"
|
||||
android:layout_marginStart="72dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:background="@color/shape_primary" />
|
||||
|
||||
<com.anytypeio.anytype.core_ui.widgets.ObjectMenuItemWidget
|
||||
android:id="@+id/optionLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@drawable/default_ripple"
|
||||
app:icon="@drawable/ic_object_menu_layout"
|
||||
app:subtitle="@string/layout_description"
|
||||
app:title="@string/layout" />
|
||||
<com.anytypeio.anytype.core_ui.widgets.ObjectMenuDescriptionItem
|
||||
android:id="@+id/optionDescription"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@drawable/default_ripple"
|
||||
app:icon="@drawable/ic_obj_settings_description_24"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/coverDivider"
|
||||
app:title="@string/description" />
|
||||
|
||||
<View
|
||||
android:id="@+id/layoutDivider"
|
||||
android:id="@+id/descriptionDivider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0.5dp"
|
||||
android:layout_marginStart="72dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:background="@color/shape_primary" />
|
||||
|
||||
<com.anytypeio.anytype.core_ui.widgets.ObjectMenuItemWidget
|
||||
|
@ -91,35 +95,35 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@drawable/default_ripple"
|
||||
app:icon="@drawable/ic_object_menu_relations"
|
||||
app:subtitle="@string/relations_description"
|
||||
app:title="@string/relations" />
|
||||
app:icon="@drawable/ic_obj_settings_fields_24"
|
||||
app:title="@string/fields" />
|
||||
|
||||
<View
|
||||
android:id="@+id/relationsDivider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0.5dp"
|
||||
android:layout_marginStart="72dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:background="@color/shape_primary" />
|
||||
|
||||
<com.anytypeio.anytype.core_ui.widgets.ObjectMenuItemWidget
|
||||
android:id="@+id/optionHistory"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@drawable/default_ripple"
|
||||
app:icon="@drawable/ic_object_menu_history"
|
||||
app:subtitle="@string/history_description"
|
||||
app:title="@string/history" />
|
||||
<com.anytypeio.anytype.core_ui.widgets.ObjectMenuItemWidget
|
||||
android:id="@+id/optionHistory"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@drawable/default_ripple"
|
||||
app:icon="@drawable/ic_obj_settings_history_24"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/relationsDivider"
|
||||
app:title="@string/history" />
|
||||
|
||||
<View
|
||||
android:id="@+id/historyDivider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0.5dp"
|
||||
android:layout_marginStart="72dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:background="@color/shape_primary" />
|
||||
|
||||
<com.anytypeio.anytype.core_ui.widgets.ObjectMenuItemWidget
|
||||
|
@ -129,15 +133,14 @@
|
|||
android:layout_marginTop="8dp"
|
||||
android:background="@drawable/default_ripple"
|
||||
app:icon="@drawable/ic_object_menu_diagnostics"
|
||||
app:subtitle="@string/object_debug"
|
||||
app:title="@string/object_diagnostics" />
|
||||
|
||||
<View
|
||||
android:id="@+id/objectDiagnosticsDivider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0.5dp"
|
||||
android:layout_marginStart="72dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:background="@color/shape_primary" />
|
||||
|
||||
<com.anytypeio.anytype.core_ui.widgets.ObjectMenuItemWidget
|
||||
|
@ -148,16 +151,15 @@
|
|||
android:background="@drawable/default_ripple"
|
||||
android:visibility="gone"
|
||||
app:icon="@drawable/ic_object_menu_debug_goroutines"
|
||||
app:subtitle="Command Debug.StackGoroutines"
|
||||
app:title="Debug Goroutines"
|
||||
tools:visibility="visible" />
|
||||
tools:visibility="visible" />
|
||||
|
||||
<View
|
||||
android:id="@+id/debugGoroutinesDivider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0.5dp"
|
||||
android:layout_marginStart="72dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:background="@color/shape_primary"
|
||||
android:visibility="gone" />
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
android:label="Object-Menu-Screen" />
|
||||
<dialog
|
||||
android:id="@+id/objectRelationListScreen"
|
||||
android:name="com.anytypeio.anytype.ui.relations.ObjectRelationListFragment"
|
||||
android:name="com.anytypeio.anytype.ui.primitives.ObjectFieldsFragment"
|
||||
android:label="Object-Relation-List-Screen" />
|
||||
<dialog
|
||||
android:id="@+id/objectIconPickerScreen"
|
||||
|
@ -81,6 +81,11 @@
|
|||
android:id="@+id/actionExitToSpaceWidgets"
|
||||
app:popUpTo="@+id/homeScreen"
|
||||
app:popUpToInclusive="false" />
|
||||
|
||||
<dialog android:id="@+id/objectTypeFieldsScreen"
|
||||
android:name="com.anytypeio.anytype.ui.primitives.ObjectTypeFieldsFragment"
|
||||
android:label="ObjectTypeFieldsScreen" />
|
||||
|
||||
</navigation>
|
||||
|
||||
<include app:graph="@navigation/nav_editor_modal" />
|
||||
|
@ -109,9 +114,10 @@
|
|||
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
|
||||
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
|
||||
</fragment>
|
||||
|
||||
<dialog
|
||||
android:id="@+id/objectRelationListScreen"
|
||||
android:name="com.anytypeio.anytype.ui.relations.ObjectRelationListFragment"
|
||||
android:name="com.anytypeio.anytype.ui.primitives.ObjectFieldsFragment"
|
||||
android:label="Object-Relation-List-Screen" />
|
||||
<dialog
|
||||
android:id="@+id/objectSetMainMenuScreen"
|
||||
|
@ -227,13 +233,22 @@
|
|||
<fragment
|
||||
android:id="@+id/dateObjectScreen"
|
||||
android:name="com.anytypeio.anytype.ui.date.DateObjectFragment"
|
||||
android:label="Date Object"> {
|
||||
android:label="Date Object">
|
||||
<action
|
||||
android:id="@+id/actionExitToSpaceWidgets"
|
||||
app:popUpTo="@+id/homeScreen"
|
||||
app:popUpToInclusive="false" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/objectTypeScreen"
|
||||
android:name="com.anytypeio.anytype.ui.primitives.ObjectTypeFragment"
|
||||
android:label=" Object">
|
||||
<action
|
||||
android:id="@+id/openEmojiPicker"
|
||||
app:destination="@id/typeSetIconPickerScreen" />
|
||||
</fragment>
|
||||
|
||||
<dialog
|
||||
android:id="@+id/selectWidgetSourceScreen"
|
||||
android:name="com.anytypeio.anytype.ui.widgets.SelectWidgetSourceFragment" />
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
</fragment>
|
||||
<dialog
|
||||
android:id="@+id/objectRelationListScreen"
|
||||
android:name="com.anytypeio.anytype.ui.relations.ObjectRelationListFragment"
|
||||
android:name="com.anytypeio.anytype.ui.primitives.ObjectFieldsFragment"
|
||||
android:label="Object-Relation-List-Screen" />
|
||||
<dialog
|
||||
android:id="@+id/objectIconPickerScreen"
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
</fragment>
|
||||
<dialog
|
||||
android:id="@+id/objectRelationListScreen"
|
||||
android:name="com.anytypeio.anytype.ui.relations.ObjectRelationListFragment"
|
||||
android:name="com.anytypeio.anytype.ui.primitives.ObjectFieldsFragment"
|
||||
android:label="Object-Relation-List-Screen" />
|
||||
<dialog
|
||||
android:id="@+id/objectIconPickerScreen"
|
||||
|
|
|
@ -653,4 +653,18 @@ sealed class Command {
|
|||
val dataViewId: Id,
|
||||
val viewerId: Id
|
||||
)
|
||||
data class ObjectTypeConflictingFields(
|
||||
val spaceId: String,
|
||||
val objectTypeId: String
|
||||
) : Command()
|
||||
|
||||
data class ObjectTypeSetRecommendedHeaderFields(
|
||||
val objectTypeId: String,
|
||||
val fields: List<Id>
|
||||
) : Command()
|
||||
|
||||
data class ObjectTypeSetRecommendedFields(
|
||||
val objectTypeId: String,
|
||||
val fields: List<Id>
|
||||
) : Command()
|
||||
}
|
|
@ -91,10 +91,10 @@ sealed class ObjectWrapper {
|
|||
val restrictions: List<ObjectRestriction>
|
||||
get() = when (val value = map[Relations.RESTRICTIONS]) {
|
||||
is Double -> buildList {
|
||||
ObjectRestriction.values().firstOrNull { it.code == value.toInt() }
|
||||
ObjectRestriction.entries.firstOrNull { it.code == value.toInt() }
|
||||
}
|
||||
is List<*> -> value.typeOf<Double>().mapNotNull { code ->
|
||||
ObjectRestriction.values().firstOrNull { it.code == code.toInt() }
|
||||
ObjectRestriction.entries.firstOrNull { it.code == code.toInt() }
|
||||
}
|
||||
else -> emptyList()
|
||||
}
|
||||
|
@ -168,14 +168,37 @@ sealed class ObjectWrapper {
|
|||
val iconEmoji: String? by default
|
||||
val isDeleted: Boolean? by default
|
||||
val recommendedRelations: List<Id> get() = getValues(Relations.RECOMMENDED_RELATIONS)
|
||||
val recommendedFeaturedRelations: List<Id> get() = getValues(Relations.RECOMMENDED_FEATURED_RELATIONS)
|
||||
val recommendedHiddenRelations: List<Id> get() = getValues(Relations.RECOMMENDED_HIDDEN_RELATIONS)
|
||||
val recommendedFileRelations: List<Id> get() = getValues(Relations.RECOMMENDED_FILE_RELATIONS)
|
||||
val recommendedLayout: ObjectType.Layout?
|
||||
get() = when (val value = map[Relations.RECOMMENDED_LAYOUT]) {
|
||||
is Double -> ObjectType.Layout.values().singleOrNull { layout ->
|
||||
is Double -> ObjectType.Layout.entries.singleOrNull { layout ->
|
||||
layout.code == value.toInt()
|
||||
}
|
||||
else -> ObjectType.Layout.BASIC
|
||||
}
|
||||
val layout: ObjectType.Layout?
|
||||
get() = when (val value = map[Relations.LAYOUT]) {
|
||||
is Double -> ObjectType.Layout.entries.singleOrNull { layout ->
|
||||
layout.code == value.toInt()
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
val defaultTemplateId: Id? by default
|
||||
|
||||
val restrictions: List<ObjectRestriction>
|
||||
get() = when (val value = map[Relations.RESTRICTIONS]) {
|
||||
is Double -> buildList {
|
||||
ObjectRestriction.entries.firstOrNull { it.code == value.toInt() }
|
||||
}
|
||||
|
||||
is List<*> -> value.typeOf<Double>().mapNotNull { code ->
|
||||
ObjectRestriction.entries.firstOrNull { it.code == code.toInt() }
|
||||
}
|
||||
|
||||
else -> emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
data class Relation(override val map: Struct) : ObjectWrapper() {
|
||||
|
@ -188,7 +211,7 @@ sealed class ObjectWrapper {
|
|||
get() {
|
||||
val value = map[Relations.RELATION_FORMAT]
|
||||
return if (value is Double) {
|
||||
RelationFormat.values().firstOrNull { f ->
|
||||
RelationFormat.entries.firstOrNull { f ->
|
||||
f.code == value.toInt()
|
||||
} ?: RelationFormat.UNDEFINED
|
||||
} else {
|
||||
|
@ -214,10 +237,10 @@ sealed class ObjectWrapper {
|
|||
val restrictions: List<ObjectRestriction>
|
||||
get() = when (val value = map[Relations.RESTRICTIONS]) {
|
||||
is Double -> buildList {
|
||||
ObjectRestriction.values().firstOrNull { it.code == value.toInt() }
|
||||
ObjectRestriction.entries.firstOrNull { it.code == value.toInt() }
|
||||
}
|
||||
is List<*> -> value.typeOf<Double>().mapNotNull { code ->
|
||||
ObjectRestriction.values().firstOrNull { it.code == code.toInt() }
|
||||
ObjectRestriction.entries.firstOrNull { it.code == code.toInt() }
|
||||
}
|
||||
else -> emptyList()
|
||||
}
|
||||
|
@ -226,7 +249,11 @@ sealed class ObjectWrapper {
|
|||
|
||||
val type: List<Id> get() = getValues(Relations.TYPE)
|
||||
|
||||
val isValid get() = map.containsKey(Relations.RELATION_KEY) && map.containsKey(Relations.ID)
|
||||
val isValid get() =
|
||||
map.containsKey(Relations.RELATION_KEY) && map.containsKey(Relations.ID)
|
||||
|
||||
val isValidToUse get() = isValid && isDeleted != true && isArchived != true && isHidden != true
|
||||
|
||||
}
|
||||
|
||||
data class Option(override val map: Struct) : ObjectWrapper() {
|
||||
|
|
|
@ -12,7 +12,8 @@ object Relations {
|
|||
const val COVER_TYPE = "coverType"
|
||||
const val COVER_ID = "coverId"
|
||||
const val DESCRIPTION = "description"
|
||||
const val LAYOUT = "layout"
|
||||
const val LAYOUT = "resolvedLayout"
|
||||
const val LEGACY_LAYOUT = "layout"
|
||||
const val NAME = "name"
|
||||
const val ICON_EMOJI = "iconEmoji"
|
||||
const val ICON_OPTION = "iconOption"
|
||||
|
@ -69,6 +70,9 @@ object Relations {
|
|||
|
||||
const val RECOMMENDED_LAYOUT = "recommendedLayout"
|
||||
const val RECOMMENDED_RELATIONS = "recommendedRelations"
|
||||
const val RECOMMENDED_FEATURED_RELATIONS = "recommendedFeaturedRelations"
|
||||
const val RECOMMENDED_HIDDEN_RELATIONS = "recommendedHiddenRelations"
|
||||
const val RECOMMENDED_FILE_RELATIONS = "recommendedFileRelations"
|
||||
const val DEFAULT_TEMPLATE_ID = "defaultTemplateId"
|
||||
|
||||
const val UNIQUE_KEY = "uniqueKey"
|
||||
|
@ -103,59 +107,98 @@ object Relations {
|
|||
"name",
|
||||
"description",
|
||||
"snippet",
|
||||
"iconEmoji",
|
||||
"iconImage",
|
||||
"type",
|
||||
"layout",
|
||||
"layoutAlign",
|
||||
"coverId",
|
||||
"coverScale",
|
||||
"coverType",
|
||||
"coverX",
|
||||
"coverY",
|
||||
"createdDate",
|
||||
"creator",
|
||||
"lastModifiedDate",
|
||||
"lastModifiedBy",
|
||||
"lastOpenedDate",
|
||||
"featuredRelations",
|
||||
"isFavorite",
|
||||
"workspaceId",
|
||||
"done",
|
||||
"spaceId",
|
||||
"links",
|
||||
"internalFlags",
|
||||
"restrictions",
|
||||
|
||||
"addedDate",
|
||||
"source",
|
||||
"sourceObject",
|
||||
|
||||
"setOf",
|
||||
"relationFormat",
|
||||
"relationKey",
|
||||
"relationReadonlyValue",
|
||||
"relationDefaultValue",
|
||||
"relationMaxCount",
|
||||
"relationOptionColor",
|
||||
"relationFormatObjectTypes",
|
||||
"isReadonly",
|
||||
"isDeleted",
|
||||
"isHidden",
|
||||
"spaceShareableStatus",
|
||||
"isAclShared",
|
||||
"isHiddenDiscovery",
|
||||
"done",
|
||||
"isArchived",
|
||||
"templateIsBundled",
|
||||
"smartblockTypes",
|
||||
"targetObjectType",
|
||||
"recommendedRelations",
|
||||
"recommendedLayout",
|
||||
"templateIsBundled",
|
||||
|
||||
"layout",
|
||||
"layoutAlign",
|
||||
|
||||
"creator",
|
||||
"createdDate",
|
||||
"lastOpenedDate",
|
||||
"lastModifiedBy",
|
||||
"lastModifiedDate",
|
||||
"addedDate",
|
||||
|
||||
"iconEmoji",
|
||||
"iconImage",
|
||||
|
||||
"coverId",
|
||||
"coverType",
|
||||
"coverScale",
|
||||
"coverX",
|
||||
"coverY",
|
||||
|
||||
"fileExt",
|
||||
"fileMimeType",
|
||||
"sizeInBytes",
|
||||
|
||||
"isHidden",
|
||||
"isArchived",
|
||||
"isFavorite",
|
||||
"isReadonly",
|
||||
|
||||
"relationKey",
|
||||
"relationFormat",
|
||||
"relationMaxCount",
|
||||
"relationReadonlyValue",
|
||||
"relationDefaultValue",
|
||||
"relationFormatObjectTypes",
|
||||
"relationOptionColor",
|
||||
"sharedSpacesLimit"
|
||||
"oldAnytypeID",
|
||||
"spaceDashboardId",
|
||||
"recommendedRelations",
|
||||
"iconOption",
|
||||
"widthInPixels",
|
||||
"heightInPixels",
|
||||
"sourceFilePath",
|
||||
"fileSyncStatus",
|
||||
"defaultTemplateId",
|
||||
"uniqueKey",
|
||||
"backlinks",
|
||||
"profileOwnerIdentity",
|
||||
"fileBackupStatus",
|
||||
"fileId",
|
||||
"fileIndexingStatus",
|
||||
"origin",
|
||||
"revision",
|
||||
"imageKind",
|
||||
"importType",
|
||||
"spaceAccessType",
|
||||
"spaceInviteFileCid",
|
||||
"spaceInviteFileKey",
|
||||
"readersLimit",
|
||||
"writersLimit",
|
||||
"sharedSpacesLimit",
|
||||
"participantPermissions",
|
||||
"participantStatus",
|
||||
"latestAclHeadId",
|
||||
"identity",
|
||||
"globalName",
|
||||
"syncDate",
|
||||
"syncStatus",
|
||||
"syncError",
|
||||
"lastUsedDate",
|
||||
"mentions",
|
||||
"chatId",
|
||||
"hasChat",
|
||||
"timestamp",
|
||||
"recommendedFeaturedRelations",
|
||||
"recommendedHiddenRelations",
|
||||
"recommendedFileRelations",
|
||||
"layoutWidth",
|
||||
"defaultViewType",
|
||||
"defaultTypeId",
|
||||
"resolvedLayout"
|
||||
)
|
||||
}
|
|
@ -38,10 +38,9 @@ sealed class Response {
|
|||
|
||||
sealed class Set : Response() {
|
||||
data class Create(
|
||||
@Deprecated("legacy param")
|
||||
val blockId: Id?,
|
||||
val targetId: Id,
|
||||
val payload: Payload
|
||||
val objectId: Id,
|
||||
val payload: Payload,
|
||||
val details: Struct
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,8 @@ object SupportedLayouts {
|
|||
ObjectType.Layout.NOTE,
|
||||
ObjectType.Layout.BOOKMARK,
|
||||
ObjectType.Layout.AUDIO,
|
||||
ObjectType.Layout.PDF
|
||||
ObjectType.Layout.PDF,
|
||||
ObjectType.Layout.OBJECT_TYPE,
|
||||
)
|
||||
val editorLayouts = listOf(
|
||||
ObjectType.Layout.BASIC,
|
||||
|
|
|
@ -4,10 +4,13 @@ import com.anytypeio.anytype.core_models.Id
|
|||
import com.anytypeio.anytype.core_models.ObjectType
|
||||
import com.anytypeio.anytype.core_models.ObjectTypeIds
|
||||
import com.anytypeio.anytype.core_models.ObjectView
|
||||
import com.anytypeio.anytype.core_models.ObjectWrapper
|
||||
import com.anytypeio.anytype.core_models.Relations
|
||||
import com.anytypeio.anytype.core_models.SupportedLayouts
|
||||
import com.anytypeio.anytype.core_models.getSingleValue
|
||||
import com.anytypeio.anytype.core_models.restrictions.ObjectRestriction
|
||||
import kotlin.collections.contains
|
||||
import kotlin.collections.get
|
||||
|
||||
/**
|
||||
* Represents a set of user permissions for a given object.
|
||||
|
@ -52,8 +55,11 @@ data class ObjectPermissions(
|
|||
val canEditRelationsList: Boolean = false,
|
||||
val canEditBlocks: Boolean = false,
|
||||
val canEditDetails: Boolean = false,
|
||||
val editBlocks: EditBlocksPermission,
|
||||
val canCreateObjectThisType: Boolean = false
|
||||
val editBlocks: EditBlocksPermission = EditBlocksPermission.ReadOnly,
|
||||
val canCreateObjectThisType: Boolean = false,
|
||||
val canChangeRecommendedLayoutForThisType: Boolean = false,
|
||||
val canCreateTemplatesForThisType: Boolean = false,
|
||||
val participantCanEdit: Boolean = false,
|
||||
)
|
||||
|
||||
/**
|
||||
|
@ -104,7 +110,7 @@ fun ObjectView.toObjectPermissions(
|
|||
|
||||
return ObjectPermissions(
|
||||
canArchive = participantCanEdit && !objectRestrictions.contains(ObjectRestriction.DELETE) && !isArchived,
|
||||
canDelete = participantCanEdit && !objectRestrictions.contains(ObjectRestriction.DELETE),
|
||||
canDelete = participantCanEdit && !objectRestrictions.contains(ObjectRestriction.DELETE),
|
||||
canChangeType = canEdit &&
|
||||
!isTemplateObject &&
|
||||
!objectRestrictions.contains(ObjectRestriction.TYPE_CHANGE),
|
||||
|
@ -134,7 +140,34 @@ fun ObjectView.toObjectPermissions(
|
|||
canEditBlocks = (editBlocksPermission == EditBlocksPermission.Edit),
|
||||
canEditDetails = canEditDetails && canEdit,
|
||||
editBlocks = editBlocksPermission,
|
||||
canCreateObjectThisType = !objectRestrictions.contains(ObjectRestriction.CREATE_OBJECT_OF_THIS_TYPE) && canApplyUneditableActions
|
||||
canCreateObjectThisType = !objectRestrictions.contains(ObjectRestriction.CREATE_OBJECT_OF_THIS_TYPE) && canApplyUneditableActions,
|
||||
participantCanEdit = participantCanEdit
|
||||
)
|
||||
}
|
||||
|
||||
fun ObjectWrapper.Type.toObjectPermissionsForTypes(
|
||||
participantCanEdit: Boolean
|
||||
): ObjectPermissions {
|
||||
|
||||
val isArchived = getSingleValue<Boolean>(Relations.IS_ARCHIVED) == true
|
||||
val canEdit = !isArchived && participantCanEdit
|
||||
|
||||
val canEditDetails = !restrictions.contains(ObjectRestriction.DETAILS)
|
||||
|
||||
val canCreateTemplatesForObjectsThisType = layoutsWithTemplates.contains(recommendedLayout)
|
||||
&& uniqueKey != ObjectTypeIds.TEMPLATE
|
||||
|
||||
val canChangeRecommendedLayoutForObjectsThisType = participantCanEdit
|
||||
&& possibleToChangeLayoutLayouts.contains(recommendedLayout)
|
||||
&& uniqueKey != ObjectTypeIds.TEMPLATE
|
||||
|
||||
return ObjectPermissions(
|
||||
canDelete = participantCanEdit && !restrictions.contains(ObjectRestriction.DELETE),
|
||||
canEditDetails = canEditDetails && canEdit,
|
||||
canCreateTemplatesForThisType = canCreateTemplatesForObjectsThisType,
|
||||
canCreateObjectThisType = !restrictions.contains(ObjectRestriction.CREATE_OBJECT_OF_THIS_TYPE) && participantCanEdit,
|
||||
canChangeRecommendedLayoutForThisType = canChangeRecommendedLayoutForObjectsThisType,
|
||||
participantCanEdit = canEdit
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -185,3 +218,10 @@ private val possibleToChangeLayoutLayouts = listOf(
|
|||
ObjectType.Layout.TODO,
|
||||
ObjectType.Layout.NOTE
|
||||
)
|
||||
|
||||
private val layoutsWithTemplates = listOf(
|
||||
ObjectType.Layout.BASIC,
|
||||
ObjectType.Layout.NOTE,
|
||||
ObjectType.Layout.PROFILE,
|
||||
ObjectType.Layout.TODO,
|
||||
)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.anytypeio.anytype.core_models.primitives
|
||||
|
||||
import com.anytypeio.anytype.core_models.ObjectWrapper
|
||||
import com.anytypeio.anytype.core_models.RelativeDate
|
||||
|
||||
sealed class Value<T> {
|
||||
|
@ -20,4 +21,13 @@ sealed class Field<T>(open val value: Value<T>) {
|
|||
data class FieldDateValue(
|
||||
val timestamp: TimestampInSeconds,
|
||||
val relativeDate: RelativeDate
|
||||
)
|
||||
|
||||
data class ParsedFields(
|
||||
val header: List<ObjectWrapper.Relation> = emptyList(),
|
||||
val sidebar: List<ObjectWrapper.Relation> = emptyList(),
|
||||
val hidden: List<ObjectWrapper.Relation> = emptyList(),
|
||||
val localWithoutSystem: List<ObjectWrapper.Relation> = emptyList(),
|
||||
val localSystem: List<ObjectWrapper.Relation> = emptyList(),
|
||||
val file: List<ObjectWrapper.Relation> = emptyList(),
|
||||
)
|
|
@ -57,7 +57,7 @@ dependencies {
|
|||
debugImplementation libs.composeTooling
|
||||
implementation libs.coilCompose
|
||||
implementation libs.composeConstraintLayout
|
||||
implementation libs.composeReorderable
|
||||
implementation libs.composeReorderableLegacy
|
||||
|
||||
testImplementation libs.fragmentTesting
|
||||
testImplementation project(':test:android-utils')
|
||||
|
|
|
@ -2,6 +2,7 @@ package com.anytypeio.anytype.core_ui.common
|
|||
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_NO
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
||||
import androidx.compose.ui.tooling.preview.Devices
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
|
||||
@Preview(
|
||||
|
@ -20,4 +21,15 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||
apiLevel = 34,
|
||||
showSystemUi = true
|
||||
)
|
||||
annotation class DefaultPreviews
|
||||
annotation class DefaultPreviews
|
||||
|
||||
@Preview(
|
||||
backgroundColor = 0xFFFFFFFF,
|
||||
showBackground = true,
|
||||
uiMode = UI_MODE_NIGHT_NO,
|
||||
name = "Light Mode",
|
||||
apiLevel = 28,
|
||||
showSystemUi = true,
|
||||
device = Devices.NEXUS_5
|
||||
)
|
||||
annotation class OldDevicesPreview
|
|
@ -0,0 +1,52 @@
|
|||
package com.anytypeio.anytype.core_ui.common
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.core.view.HapticFeedbackConstantsCompat
|
||||
import androidx.core.view.ViewCompat
|
||||
|
||||
@Composable
|
||||
fun rememberReorderHapticFeedback(): ReorderHapticFeedback {
|
||||
val view = LocalView.current
|
||||
|
||||
val reorderHapticFeedback = remember {
|
||||
object : ReorderHapticFeedback() {
|
||||
override fun performHapticFeedback(type: ReorderHapticFeedbackType) {
|
||||
when (type) {
|
||||
ReorderHapticFeedbackType.START ->
|
||||
ViewCompat.performHapticFeedback(
|
||||
view,
|
||||
HapticFeedbackConstantsCompat.GESTURE_START
|
||||
)
|
||||
|
||||
ReorderHapticFeedbackType.MOVE ->
|
||||
ViewCompat.performHapticFeedback(
|
||||
view,
|
||||
HapticFeedbackConstantsCompat.SEGMENT_FREQUENT_TICK
|
||||
)
|
||||
|
||||
ReorderHapticFeedbackType.END ->
|
||||
ViewCompat.performHapticFeedback(
|
||||
view,
|
||||
HapticFeedbackConstantsCompat.GESTURE_END
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return reorderHapticFeedback
|
||||
}
|
||||
|
||||
enum class ReorderHapticFeedbackType {
|
||||
START,
|
||||
MOVE,
|
||||
END,
|
||||
}
|
||||
|
||||
open class ReorderHapticFeedback {
|
||||
open fun performHapticFeedback(type: ReorderHapticFeedbackType) {
|
||||
// no-op
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
package com.anytypeio.anytype.core_ui.features.fields
|
||||
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.DpOffset
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.anytypeio.anytype.core_ui.views.BodyCalloutRegular
|
||||
import com.anytypeio.anytype.core_ui.R
|
||||
|
||||
@Composable
|
||||
fun FieldItemDropDownMenu(
|
||||
showMenu: Boolean,
|
||||
onDismissRequest: () -> Unit,
|
||||
onAddToCurrentTypeClick: () -> Unit,
|
||||
onRemoveFromObjectClick: () -> Unit,
|
||||
) {
|
||||
DropdownMenu(
|
||||
modifier = Modifier
|
||||
.width(244.dp),
|
||||
expanded = showMenu,
|
||||
offset = DpOffset(x = 0.dp, y = 0.dp),
|
||||
onDismissRequest = {
|
||||
onDismissRequest()
|
||||
},
|
||||
shape = RoundedCornerShape(10.dp),
|
||||
containerColor = colorResource(id = R.color.background_secondary),
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
text = {
|
||||
Text(
|
||||
text = stringResource(R.string.field_menu_add_to_type),
|
||||
style = BodyCalloutRegular,
|
||||
color = colorResource(id = R.color.text_primary)
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
onAddToCurrentTypeClick()
|
||||
},
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = {
|
||||
Text(
|
||||
text = stringResource(R.string.field_menu_remove_from_object),
|
||||
style = BodyCalloutRegular,
|
||||
color = colorResource(id = R.color.palette_system_red)
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
onRemoveFromObjectClick()
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
package com.anytypeio.anytype.core_ui.features.fields
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
|
@ -14,6 +16,8 @@ import androidx.compose.foundation.lazy.LazyColumn
|
|||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.res.colorResource
|
||||
|
@ -26,8 +30,17 @@ import com.anytypeio.anytype.core_ui.R
|
|||
import com.anytypeio.anytype.core_ui.common.DefaultPreviews
|
||||
import com.anytypeio.anytype.core_ui.views.Relations1
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun FieldEmpty(modifier: Modifier = Modifier, title: String, fieldFormat: RelationFormat) {
|
||||
fun FieldEmpty(
|
||||
modifier: Modifier = Modifier,
|
||||
title: String,
|
||||
fieldFormat: RelationFormat,
|
||||
isLocal: Boolean,
|
||||
onFieldClick: () -> Unit,
|
||||
onAddToCurrentTypeClick: () -> Unit,
|
||||
onRemoveFromObjectClick: () -> Unit,
|
||||
) {
|
||||
val defaultModifier = modifier
|
||||
.fillMaxWidth()
|
||||
.border(
|
||||
|
@ -43,7 +56,11 @@ fun FieldEmpty(modifier: Modifier = Modifier, title: String, fieldFormat: Relati
|
|||
FieldVerticalEmpty(
|
||||
modifier = defaultModifier,
|
||||
title = title,
|
||||
emptyState = emptyState
|
||||
emptyState = emptyState,
|
||||
isLocal = isLocal,
|
||||
onFieldClick = onFieldClick,
|
||||
onAddToCurrentTypeClick = onAddToCurrentTypeClick,
|
||||
onRemoveFromObjectClick = onRemoveFromObjectClick
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -52,20 +69,37 @@ fun FieldEmpty(modifier: Modifier = Modifier, title: String, fieldFormat: Relati
|
|||
FieldHorizontalEmpty(
|
||||
modifier = defaultModifier,
|
||||
title = title,
|
||||
emptyState = emptyState
|
||||
emptyState = emptyState,
|
||||
isLocal = isLocal,
|
||||
onFieldClick = onFieldClick,
|
||||
onAddToCurrentTypeClick = onAddToCurrentTypeClick,
|
||||
onRemoveFromObjectClick = onRemoveFromObjectClick
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
private fun FieldVerticalEmpty(
|
||||
modifier: Modifier = Modifier,
|
||||
title: String,
|
||||
emptyState: String,
|
||||
isLocal: Boolean,
|
||||
onFieldClick: () -> Unit,
|
||||
onAddToCurrentTypeClick: () -> Unit,
|
||||
onRemoveFromObjectClick: () -> Unit,
|
||||
) {
|
||||
val isMenuExpanded = remember { mutableStateOf(false) }
|
||||
Column(
|
||||
modifier = modifier.padding(horizontal = 16.dp, vertical = 16.dp)
|
||||
modifier = modifier
|
||||
.combinedClickable(
|
||||
onClick = { onFieldClick()},
|
||||
onLongClick = {
|
||||
if (isLocal) isMenuExpanded.value = true
|
||||
}
|
||||
)
|
||||
.padding(horizontal = 16.dp, vertical = 16.dp)
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
|
@ -84,21 +118,47 @@ private fun FieldVerticalEmpty(
|
|||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
FieldItemDropDownMenu(
|
||||
showMenu = isMenuExpanded.value,
|
||||
onDismissRequest = {
|
||||
isMenuExpanded.value = false
|
||||
},
|
||||
onAddToCurrentTypeClick = {
|
||||
isMenuExpanded.value = false
|
||||
onAddToCurrentTypeClick()
|
||||
},
|
||||
onRemoveFromObjectClick = {
|
||||
isMenuExpanded.value = false
|
||||
onRemoveFromObjectClick()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
private fun FieldHorizontalEmpty(
|
||||
modifier: Modifier = Modifier,
|
||||
title: String,
|
||||
emptyState: String,
|
||||
isLocal: Boolean,
|
||||
onFieldClick: () -> Unit,
|
||||
onAddToCurrentTypeClick: () -> Unit,
|
||||
onRemoveFromObjectClick: () -> Unit,
|
||||
) {
|
||||
val isMenuExpanded = remember { mutableStateOf(false) }
|
||||
val configuration = LocalConfiguration.current
|
||||
val screenWidth = configuration.screenWidthDp.dp
|
||||
val halfScreenWidth = screenWidth / 2 - 32.dp
|
||||
|
||||
Row(
|
||||
modifier = modifier
|
||||
.combinedClickable(
|
||||
onClick = onFieldClick,
|
||||
onLongClick = {
|
||||
if (isLocal) isMenuExpanded.value = true
|
||||
}
|
||||
)
|
||||
.padding(horizontal = 16.dp, vertical = 16.dp)
|
||||
) {
|
||||
Text(
|
||||
|
@ -116,6 +176,20 @@ private fun FieldHorizontalEmpty(
|
|||
style = Relations1,
|
||||
color = colorResource(id = R.color.text_tertiary)
|
||||
)
|
||||
FieldItemDropDownMenu(
|
||||
showMenu = isMenuExpanded.value,
|
||||
onDismissRequest = {
|
||||
isMenuExpanded.value = false
|
||||
},
|
||||
onAddToCurrentTypeClick = {
|
||||
isMenuExpanded.value = false
|
||||
onAddToCurrentTypeClick()
|
||||
},
|
||||
onRemoveFromObjectClick = {
|
||||
isMenuExpanded.value = false
|
||||
onRemoveFromObjectClick()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -149,13 +223,21 @@ fun PreviewField() {
|
|||
item {
|
||||
FieldEmpty(
|
||||
title = "Description",
|
||||
fieldFormat = Relation.Format.LONG_TEXT
|
||||
fieldFormat = Relation.Format.LONG_TEXT,
|
||||
isLocal = true,
|
||||
onFieldClick = {},
|
||||
onAddToCurrentTypeClick = {},
|
||||
onRemoveFromObjectClick = {}
|
||||
)
|
||||
}
|
||||
item {
|
||||
FieldEmpty(
|
||||
title = "Some Number, very long long long long long fields name",
|
||||
fieldFormat = Relation.Format.NUMBER
|
||||
fieldFormat = Relation.Format.NUMBER,
|
||||
isLocal = true,
|
||||
onFieldClick = {},
|
||||
onAddToCurrentTypeClick = {},
|
||||
onRemoveFromObjectClick = {}
|
||||
)
|
||||
}
|
||||
item {
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package com.anytypeio.anytype.core_ui.features.fields
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
|
@ -14,6 +16,8 @@ import androidx.compose.foundation.layout.wrapContentHeight
|
|||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.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.platform.LocalConfiguration
|
||||
|
@ -25,13 +29,25 @@ import com.anytypeio.anytype.core_ui.R
|
|||
import com.anytypeio.anytype.core_ui.common.DefaultPreviews
|
||||
import com.anytypeio.anytype.core_ui.views.Relations1
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun FieldTypeCheckbox(
|
||||
modifier: Modifier = Modifier,
|
||||
title: String,
|
||||
isCheck: Boolean,
|
||||
isLocal: Boolean,
|
||||
onFieldClick: () -> Unit,
|
||||
onAddToCurrentTypeClick: () -> Unit,
|
||||
onRemoveFromObjectClick: () -> Unit,
|
||||
) {
|
||||
val isMenuExpanded = remember { mutableStateOf(false) }
|
||||
val defaultModifier = modifier
|
||||
.combinedClickable(
|
||||
onClick = { onFieldClick()},
|
||||
onLongClick = {
|
||||
if (isLocal) isMenuExpanded.value = true
|
||||
}
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.border(
|
||||
width = 1.dp,
|
||||
|
@ -81,6 +97,20 @@ fun FieldTypeCheckbox(
|
|||
)
|
||||
}
|
||||
}
|
||||
FieldItemDropDownMenu(
|
||||
showMenu = isMenuExpanded.value,
|
||||
onDismissRequest = {
|
||||
isMenuExpanded.value = false
|
||||
},
|
||||
onAddToCurrentTypeClick = {
|
||||
isMenuExpanded.value = false
|
||||
onAddToCurrentTypeClick()
|
||||
},
|
||||
onRemoveFromObjectClick = {
|
||||
isMenuExpanded.value = false
|
||||
onRemoveFromObjectClick()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,6 +120,10 @@ fun FieldTypeCheckbox(
|
|||
fun FieldTypeCheckboxPreview() {
|
||||
FieldTypeCheckbox(
|
||||
title = "Creation date",
|
||||
isCheck = false
|
||||
isCheck = false,
|
||||
isLocal = true,
|
||||
onRemoveFromObjectClick = {},
|
||||
onAddToCurrentTypeClick = {},
|
||||
onFieldClick = {}
|
||||
)
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
package com.anytypeio.anytype.core_ui.features.fields
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
|
@ -12,6 +14,8 @@ import androidx.compose.foundation.layout.wrapContentHeight
|
|||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.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.platform.LocalConfiguration
|
||||
|
@ -26,13 +30,25 @@ import com.anytypeio.anytype.core_ui.extensions.getPrettyName
|
|||
import com.anytypeio.anytype.core_ui.views.BodyCallout
|
||||
import com.anytypeio.anytype.core_ui.views.Relations1
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun FieldTypeDate(
|
||||
modifier: Modifier = Modifier,
|
||||
title: String,
|
||||
relativeDate: RelativeDate
|
||||
relativeDate: RelativeDate,
|
||||
isLocal: Boolean,
|
||||
onFieldClick: () -> Unit,
|
||||
onAddToCurrentTypeClick: () -> Unit,
|
||||
onRemoveFromObjectClick: () -> Unit,
|
||||
) {
|
||||
val isMenuExpanded = remember { mutableStateOf(false) }
|
||||
val defaultModifier = modifier
|
||||
.combinedClickable(
|
||||
onClick = onFieldClick,
|
||||
onLongClick = {
|
||||
if (isLocal) isMenuExpanded.value = true
|
||||
}
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.border(
|
||||
width = 1.dp,
|
||||
|
@ -77,6 +93,20 @@ fun FieldTypeDate(
|
|||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
FieldItemDropDownMenu(
|
||||
showMenu = isMenuExpanded.value,
|
||||
onDismissRequest = {
|
||||
isMenuExpanded.value = false
|
||||
},
|
||||
onAddToCurrentTypeClick = {
|
||||
isMenuExpanded.value = false
|
||||
onAddToCurrentTypeClick()
|
||||
},
|
||||
onRemoveFromObjectClick = {
|
||||
isMenuExpanded.value = false
|
||||
onRemoveFromObjectClick()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,5 +120,9 @@ fun FieldTypeDatePreview() {
|
|||
initialTimeInMillis = System.currentTimeMillis(),
|
||||
dayOfWeek = DayOfWeekCustom.THURSDAY
|
||||
),
|
||||
isLocal = true,
|
||||
onRemoveFromObjectClick = {},
|
||||
onAddToCurrentTypeClick = {},
|
||||
onFieldClick = {}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package com.anytypeio.anytype.core_ui.features.fields
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
|
@ -16,6 +18,8 @@ import androidx.compose.foundation.layout.wrapContentWidth
|
|||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.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.layout.SubcomposeLayout
|
||||
|
@ -34,16 +38,28 @@ import com.anytypeio.anytype.core_ui.widgets.ListWidgetObjectIcon
|
|||
import com.anytypeio.anytype.presentation.relations.ObjectRelationView
|
||||
import com.anytypeio.anytype.presentation.sets.model.FileView
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun FieldTypeFile(
|
||||
modifier: Modifier = Modifier,
|
||||
fieldObject: ObjectRelationView.File
|
||||
fieldObject: ObjectRelationView.File,
|
||||
isLocal: Boolean,
|
||||
onFieldClick: () -> Unit,
|
||||
onAddToCurrentTypeClick: () -> Unit,
|
||||
onRemoveFromObjectClick: () -> Unit,
|
||||
) {
|
||||
val isMenuExpanded = remember { mutableStateOf(false) }
|
||||
val configuration = LocalConfiguration.current
|
||||
val screenWidth = configuration.screenWidthDp.dp
|
||||
val halfScreenWidth = screenWidth / 2 - 32.dp
|
||||
|
||||
val defaultModifier = modifier
|
||||
.combinedClickable(
|
||||
onClick = onFieldClick,
|
||||
onLongClick = {
|
||||
if (isLocal) isMenuExpanded.value = true
|
||||
}
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.border(
|
||||
width = 1.dp,
|
||||
|
@ -52,6 +68,7 @@ fun FieldTypeFile(
|
|||
)
|
||||
.padding(vertical = 16.dp)
|
||||
.padding(horizontal = 16.dp)
|
||||
|
||||
if (fieldObject.files.size == 1) {
|
||||
// If there is only one item, display the title and the item in one row.
|
||||
val singleItem = fieldObject.files.first()
|
||||
|
@ -82,6 +99,20 @@ fun FieldTypeFile(
|
|||
objView = singleItem
|
||||
)
|
||||
}
|
||||
FieldItemDropDownMenu(
|
||||
showMenu = isMenuExpanded.value,
|
||||
onDismissRequest = {
|
||||
isMenuExpanded.value = false
|
||||
},
|
||||
onAddToCurrentTypeClick = {
|
||||
isMenuExpanded.value = false
|
||||
onAddToCurrentTypeClick()
|
||||
},
|
||||
onRemoveFromObjectClick = {
|
||||
isMenuExpanded.value = false
|
||||
onRemoveFromObjectClick()
|
||||
}
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Column(
|
||||
|
@ -156,6 +187,20 @@ fun FieldTypeFile(
|
|||
}
|
||||
}
|
||||
}
|
||||
FieldItemDropDownMenu(
|
||||
showMenu = isMenuExpanded.value,
|
||||
onDismissRequest = {
|
||||
isMenuExpanded.value = false
|
||||
},
|
||||
onAddToCurrentTypeClick = {
|
||||
isMenuExpanded.value = false
|
||||
onAddToCurrentTypeClick()
|
||||
},
|
||||
onRemoveFromObjectClick = {
|
||||
isMenuExpanded.value = false
|
||||
onRemoveFromObjectClick()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package com.anytypeio.anytype.core_ui.features.fields
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
|
@ -15,6 +17,8 @@ import androidx.compose.foundation.layout.wrapContentWidth
|
|||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.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.graphics.Color
|
||||
|
@ -35,13 +39,25 @@ import com.anytypeio.anytype.core_ui.views.Relations1
|
|||
import com.anytypeio.anytype.core_ui.views.Relations2
|
||||
import com.anytypeio.anytype.presentation.sets.model.TagView
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun FieldTypeMultiSelect(
|
||||
modifier: Modifier = Modifier,
|
||||
title: String,
|
||||
tags: List<TagView>
|
||||
tags: List<TagView>,
|
||||
isLocal: Boolean,
|
||||
onFieldClick: () -> Unit,
|
||||
onAddToCurrentTypeClick: () -> Unit,
|
||||
onRemoveFromObjectClick: () -> Unit,
|
||||
) {
|
||||
val isMenuExpanded = remember { mutableStateOf(false) }
|
||||
val defaultModifier = modifier
|
||||
.combinedClickable(
|
||||
onClick = onFieldClick,
|
||||
onLongClick = {
|
||||
if (isLocal) isMenuExpanded.value = true
|
||||
}
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.border(
|
||||
width = 1.dp,
|
||||
|
@ -79,6 +95,20 @@ fun FieldTypeMultiSelect(
|
|||
modifier = Modifier.fillMaxWidth(),
|
||||
textStyle = Relations1
|
||||
)
|
||||
FieldItemDropDownMenu(
|
||||
showMenu = isMenuExpanded.value,
|
||||
onDismissRequest = {
|
||||
isMenuExpanded.value = false
|
||||
},
|
||||
onAddToCurrentTypeClick = {
|
||||
isMenuExpanded.value = false
|
||||
onAddToCurrentTypeClick()
|
||||
},
|
||||
onRemoveFromObjectClick = {
|
||||
isMenuExpanded.value = false
|
||||
onRemoveFromObjectClick()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
package com.anytypeio.anytype.core_ui.features.fields
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.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.layout.SubcomposeLayout
|
||||
|
@ -35,16 +39,28 @@ import com.anytypeio.anytype.presentation.sets.model.ObjectView
|
|||
* (where n = total number of items minus two) immediately following its text.
|
||||
* If the text of the second item is long, it is truncated so that the suffix is always visible.
|
||||
*/
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun FieldTypeObject(
|
||||
modifier: Modifier = Modifier,
|
||||
fieldObject: ObjectRelationView.Object
|
||||
fieldObject: ObjectRelationView.Object,
|
||||
isLocal: Boolean,
|
||||
onFieldClick: () -> Unit,
|
||||
onAddToCurrentTypeClick: () -> Unit,
|
||||
onRemoveFromObjectClick: () -> Unit,
|
||||
) {
|
||||
val isMenuExpanded = remember { mutableStateOf(false) }
|
||||
val configuration = LocalConfiguration.current
|
||||
val screenWidth = configuration.screenWidthDp.dp
|
||||
val halfScreenWidth = screenWidth / 2 - 32.dp
|
||||
|
||||
val defaultModifier = modifier
|
||||
.combinedClickable(
|
||||
onClick = onFieldClick,
|
||||
onLongClick = {
|
||||
if (isLocal) isMenuExpanded.value = true
|
||||
}
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.border(
|
||||
width = 1.dp,
|
||||
|
@ -83,6 +99,20 @@ fun FieldTypeObject(
|
|||
objView = singleItem
|
||||
)
|
||||
}
|
||||
FieldItemDropDownMenu(
|
||||
showMenu = isMenuExpanded.value,
|
||||
onDismissRequest = {
|
||||
isMenuExpanded.value = false
|
||||
},
|
||||
onAddToCurrentTypeClick = {
|
||||
isMenuExpanded.value = false
|
||||
onAddToCurrentTypeClick()
|
||||
},
|
||||
onRemoveFromObjectClick = {
|
||||
isMenuExpanded.value = false
|
||||
onRemoveFromObjectClick()
|
||||
}
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Column(
|
||||
|
@ -156,6 +186,20 @@ fun FieldTypeObject(
|
|||
}
|
||||
}
|
||||
}
|
||||
FieldItemDropDownMenu(
|
||||
showMenu = isMenuExpanded.value,
|
||||
onDismissRequest = {
|
||||
isMenuExpanded.value = false
|
||||
},
|
||||
onAddToCurrentTypeClick = {
|
||||
isMenuExpanded.value = false
|
||||
onAddToCurrentTypeClick()
|
||||
},
|
||||
onRemoveFromObjectClick = {
|
||||
isMenuExpanded.value = false
|
||||
onRemoveFromObjectClick()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package com.anytypeio.anytype.core_ui.features.fields
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
|
@ -12,6 +14,8 @@ import androidx.compose.foundation.layout.wrapContentHeight
|
|||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.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.platform.LocalConfiguration
|
||||
|
@ -25,13 +29,26 @@ import com.anytypeio.anytype.core_ui.extensions.dark
|
|||
import com.anytypeio.anytype.core_ui.views.Relations1
|
||||
import com.anytypeio.anytype.presentation.sets.model.StatusView
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun FieldTypeSelect(
|
||||
modifier: Modifier = Modifier,
|
||||
title: String,
|
||||
status: StatusView
|
||||
status: StatusView,
|
||||
isLocal: Boolean,
|
||||
onFieldClick: () -> Unit,
|
||||
onAddToCurrentTypeClick: () -> Unit,
|
||||
onRemoveFromObjectClick: () -> Unit,
|
||||
) {
|
||||
val isMenuExpanded = remember { mutableStateOf(false) }
|
||||
|
||||
val defaultModifier = modifier
|
||||
.combinedClickable(
|
||||
onClick = onFieldClick,
|
||||
onLongClick = {
|
||||
if (isLocal) isMenuExpanded.value = true
|
||||
}
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.border(
|
||||
width = 1.dp,
|
||||
|
@ -71,6 +88,20 @@ fun FieldTypeSelect(
|
|||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
FieldItemDropDownMenu(
|
||||
showMenu = isMenuExpanded.value,
|
||||
onDismissRequest = {
|
||||
isMenuExpanded.value = false
|
||||
},
|
||||
onAddToCurrentTypeClick = {
|
||||
isMenuExpanded.value = false
|
||||
onAddToCurrentTypeClick()
|
||||
},
|
||||
onRemoveFromObjectClick = {
|
||||
isMenuExpanded.value = false
|
||||
onRemoveFromObjectClick()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -83,6 +114,10 @@ fun FieldTypeSelectPreview() {
|
|||
id = "1",
|
||||
status = "In Progress",
|
||||
color = ThemeColor.TEAL.code
|
||||
)
|
||||
),
|
||||
isLocal = true,
|
||||
onRemoveFromObjectClick = {},
|
||||
onAddToCurrentTypeClick = {},
|
||||
onFieldClick = {}
|
||||
)
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
package com.anytypeio.anytype.core_ui.features.fields
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
|
@ -9,6 +11,8 @@ import androidx.compose.foundation.layout.padding
|
|||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
|
@ -17,13 +21,26 @@ import com.anytypeio.anytype.core_ui.R
|
|||
import com.anytypeio.anytype.core_ui.common.DefaultPreviews
|
||||
import com.anytypeio.anytype.core_ui.views.Relations1
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun FieldTypeText(
|
||||
modifier: Modifier = Modifier,
|
||||
title: String,
|
||||
text: String
|
||||
text: String,
|
||||
isLocal: Boolean,
|
||||
onFieldClick: () -> Unit,
|
||||
onAddToCurrentTypeClick: () -> Unit,
|
||||
onRemoveFromObjectClick: () -> Unit,
|
||||
) {
|
||||
val isMenuExpanded = remember { mutableStateOf(false) }
|
||||
|
||||
val defaultModifier = modifier
|
||||
.combinedClickable(
|
||||
onClick = onFieldClick,
|
||||
onLongClick = {
|
||||
if (isLocal) isMenuExpanded.value = true
|
||||
}
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.border(
|
||||
width = 1.dp,
|
||||
|
@ -55,6 +72,20 @@ fun FieldTypeText(
|
|||
maxLines = 3,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
FieldItemDropDownMenu(
|
||||
showMenu = isMenuExpanded.value,
|
||||
onDismissRequest = {
|
||||
isMenuExpanded.value = false
|
||||
},
|
||||
onAddToCurrentTypeClick = {
|
||||
isMenuExpanded.value = false
|
||||
onAddToCurrentTypeClick()
|
||||
},
|
||||
onRemoveFromObjectClick = {
|
||||
isMenuExpanded.value = false
|
||||
onRemoveFromObjectClick()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -65,6 +96,10 @@ fun FieldTypeTextPreview() {
|
|||
title = "Description",
|
||||
text = "Upon creating your profile, you’ll receive your very own 12 word mnemonic ‘Recovery’ phrase to protect your account. This phrase is generated on-device and represents your master key generated upon signup, similar to a Bitcoin wallet. It also prevents anyone - including Anytype - from accessing your account and decrypting your data.\n" +
|
||||
"\n" +
|
||||
"All data you create will be stored locally (on-device) first. We use zero-knowledge encryption, meaning that your data is encrypted before it leaves your device to sync with other devices or backup nodes."
|
||||
"All data you create will be stored locally (on-device) first. We use zero-knowledge encryption, meaning that your data is encrypted before it leaves your device to sync with other devices or backup nodes.",
|
||||
isLocal = true,
|
||||
onRemoveFromObjectClick = {},
|
||||
onAddToCurrentTypeClick = {},
|
||||
onFieldClick = {}
|
||||
)
|
||||
}
|
|
@ -1,12 +1,19 @@
|
|||
package com.anytypeio.anytype.core_ui.features.fields
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
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.layout.width
|
||||
import androidx.compose.foundation.layout.wrapContentSize
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
|
@ -14,27 +21,39 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.rememberNestedScrollInteropConnection
|
||||
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.core_models.RelationFormat
|
||||
import com.anytypeio.anytype.core_models.Relations
|
||||
import com.anytypeio.anytype.core_ui.R
|
||||
import com.anytypeio.anytype.core_ui.common.DefaultPreviews
|
||||
import com.anytypeio.anytype.core_ui.features.editor.holders.relations.resRelationOrigin
|
||||
import com.anytypeio.anytype.core_ui.foundation.Dragger
|
||||
import com.anytypeio.anytype.core_ui.foundation.noRippleThrottledClickable
|
||||
import com.anytypeio.anytype.core_ui.views.BodyCalloutMedium
|
||||
import com.anytypeio.anytype.core_ui.views.Title1
|
||||
import com.anytypeio.anytype.presentation.relations.ObjectRelationView
|
||||
import com.anytypeio.anytype.presentation.relations.RelationListViewModel.Model
|
||||
import timber.log.Timber
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun FieldListScreen(
|
||||
state: List<Model>,
|
||||
onRelationClicked: (Model.Item) -> Unit
|
||||
onRelationClicked: (Model.Item) -> Unit,
|
||||
onTypeIconClicked: () -> Unit,
|
||||
onLocalInfoIconClicked: () -> Unit,
|
||||
onAddToTypeClicked: (Model.Item) -> Unit,
|
||||
onRemoveFromObjectClicked: (Model.Item) -> Unit
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(
|
||||
color = colorResource(id = R.color.widget_background),
|
||||
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp)
|
||||
)
|
||||
.nestedScroll(rememberNestedScrollInteropConnection())
|
||||
.padding(horizontal = 16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
|
@ -47,7 +66,9 @@ fun FieldListScreen(
|
|||
}
|
||||
item {
|
||||
Box(
|
||||
modifier = Modifier.height(48.dp)
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(48.dp)
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
|
@ -55,6 +76,23 @@ fun FieldListScreen(
|
|||
style = Title1,
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
)
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterEnd)
|
||||
.width(56.dp)
|
||||
.height(48.dp)
|
||||
.noRippleThrottledClickable {
|
||||
onTypeIconClicked()
|
||||
},
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Image(
|
||||
modifier = Modifier.wrapContentSize(),
|
||||
painter = painterResource(R.drawable.ic_settings_24),
|
||||
contentDescription = "Open object's type"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
items(
|
||||
|
@ -68,11 +106,13 @@ fun FieldListScreen(
|
|||
when (field) {
|
||||
is ObjectRelationView.Checkbox -> {
|
||||
FieldTypeCheckbox(
|
||||
modifier = Modifier.noRippleThrottledClickable {
|
||||
onRelationClicked(item)
|
||||
},
|
||||
modifier = Modifier,
|
||||
title = field.name,
|
||||
isCheck = field.isChecked
|
||||
isCheck = field.isChecked,
|
||||
isLocal = item.isLocal,
|
||||
onFieldClick = { onRelationClicked(item) },
|
||||
onAddToCurrentTypeClick = { onAddToTypeClicked(item) },
|
||||
onRemoveFromObjectClick = { onRemoveFromObjectClicked(item) }
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -80,19 +120,23 @@ fun FieldListScreen(
|
|||
val relativeDate = field.relativeDate
|
||||
if (relativeDate != null) {
|
||||
FieldTypeDate(
|
||||
modifier = Modifier.noRippleThrottledClickable {
|
||||
onRelationClicked(item)
|
||||
},
|
||||
modifier = Modifier,
|
||||
title = field.name,
|
||||
relativeDate = relativeDate
|
||||
relativeDate = relativeDate,
|
||||
isLocal = item.isLocal,
|
||||
onFieldClick = { onRelationClicked(item) },
|
||||
onAddToCurrentTypeClick = { onAddToTypeClicked(item) },
|
||||
onRemoveFromObjectClick = { onRemoveFromObjectClicked(item) }
|
||||
)
|
||||
} else {
|
||||
FieldEmpty(
|
||||
modifier = Modifier.noRippleThrottledClickable {
|
||||
onRelationClicked(item)
|
||||
},
|
||||
modifier = Modifier,
|
||||
title = field.name,
|
||||
fieldFormat = RelationFormat.DATE
|
||||
fieldFormat = RelationFormat.DATE,
|
||||
isLocal = item.isLocal,
|
||||
onFieldClick = { onRelationClicked(item) },
|
||||
onAddToCurrentTypeClick = { onAddToTypeClicked(item) },
|
||||
onRemoveFromObjectClick = { onRemoveFromObjectClicked(item) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -102,28 +146,34 @@ fun FieldListScreen(
|
|||
if (field.key == Relations.ORIGIN) {
|
||||
val code = textValue?.toInt() ?: -1
|
||||
FieldTypeText(
|
||||
modifier = Modifier.noRippleThrottledClickable {
|
||||
onRelationClicked(item)
|
||||
},
|
||||
modifier = Modifier,
|
||||
title = field.name,
|
||||
text = stringResource(code.resRelationOrigin())
|
||||
text = stringResource(code.resRelationOrigin()),
|
||||
isLocal = item.isLocal,
|
||||
onFieldClick = { onRelationClicked(item) },
|
||||
onAddToCurrentTypeClick = { onAddToTypeClicked(item) },
|
||||
onRemoveFromObjectClick = { onRemoveFromObjectClicked(item) }
|
||||
)
|
||||
} else {
|
||||
if (textValue.isNullOrEmpty() == true) {
|
||||
FieldEmpty(
|
||||
modifier = Modifier.noRippleThrottledClickable {
|
||||
onRelationClicked(item)
|
||||
},
|
||||
modifier = Modifier,
|
||||
title = field.name,
|
||||
fieldFormat = RelationFormat.LONG_TEXT
|
||||
fieldFormat = RelationFormat.LONG_TEXT,
|
||||
isLocal = item.isLocal,
|
||||
onFieldClick = { onRelationClicked(item) },
|
||||
onAddToCurrentTypeClick = { onAddToTypeClicked(item) },
|
||||
onRemoveFromObjectClick = { onRemoveFromObjectClicked(item) }
|
||||
)
|
||||
} else {
|
||||
FieldTypeText(
|
||||
modifier = Modifier.noRippleThrottledClickable {
|
||||
onRelationClicked(item)
|
||||
},
|
||||
modifier = Modifier,
|
||||
title = field.name,
|
||||
text = textValue
|
||||
text = textValue,
|
||||
isLocal = item.isLocal,
|
||||
onFieldClick = { onRelationClicked(item) },
|
||||
onAddToCurrentTypeClick = { onAddToTypeClicked(item) },
|
||||
onRemoveFromObjectClick = { onRemoveFromObjectClicked(item) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -132,18 +182,22 @@ fun FieldListScreen(
|
|||
is ObjectRelationView.File -> {
|
||||
if (field.files.isEmpty()) {
|
||||
FieldEmpty(
|
||||
modifier = Modifier.noRippleThrottledClickable {
|
||||
onRelationClicked(item)
|
||||
},
|
||||
modifier = Modifier,
|
||||
title = field.name,
|
||||
fieldFormat = RelationFormat.FILE
|
||||
fieldFormat = RelationFormat.FILE,
|
||||
isLocal = item.isLocal,
|
||||
onFieldClick = { onRelationClicked(item) },
|
||||
onAddToCurrentTypeClick = { onAddToTypeClicked(item) },
|
||||
onRemoveFromObjectClick = { onRemoveFromObjectClicked(item) }
|
||||
)
|
||||
} else {
|
||||
FieldTypeFile(
|
||||
modifier = Modifier.noRippleThrottledClickable {
|
||||
onRelationClicked(item)
|
||||
},
|
||||
fieldObject = field
|
||||
modifier = Modifier,
|
||||
fieldObject = field,
|
||||
isLocal = item.isLocal,
|
||||
onFieldClick = { onRelationClicked(item) },
|
||||
onAddToCurrentTypeClick = { onAddToTypeClicked(item) },
|
||||
onRemoveFromObjectClick = { onRemoveFromObjectClicked(item) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -151,18 +205,22 @@ fun FieldListScreen(
|
|||
is ObjectRelationView.Object -> {
|
||||
if (field.objects.isEmpty()) {
|
||||
FieldEmpty(
|
||||
modifier = Modifier.noRippleThrottledClickable {
|
||||
onRelationClicked(item)
|
||||
},
|
||||
modifier = Modifier,
|
||||
title = field.name,
|
||||
fieldFormat = RelationFormat.OBJECT
|
||||
fieldFormat = RelationFormat.OBJECT,
|
||||
isLocal = item.isLocal,
|
||||
onFieldClick = { onRelationClicked(item) },
|
||||
onAddToCurrentTypeClick = { onAddToTypeClicked(item) },
|
||||
onRemoveFromObjectClick = { onRemoveFromObjectClicked(item) }
|
||||
)
|
||||
} else {
|
||||
FieldTypeObject(
|
||||
modifier = Modifier.noRippleThrottledClickable {
|
||||
onRelationClicked(item)
|
||||
},
|
||||
fieldObject = field
|
||||
modifier = Modifier,
|
||||
fieldObject = field,
|
||||
isLocal = item.isLocal,
|
||||
onFieldClick = { onRelationClicked(item) },
|
||||
onAddToCurrentTypeClick = { onAddToTypeClicked(item) },
|
||||
onRemoveFromObjectClick = { onRemoveFromObjectClicked(item) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -170,19 +228,23 @@ fun FieldListScreen(
|
|||
is ObjectRelationView.Status -> {
|
||||
if (field.status.isEmpty()) {
|
||||
FieldEmpty(
|
||||
modifier = Modifier.noRippleThrottledClickable {
|
||||
onRelationClicked(item)
|
||||
},
|
||||
modifier = Modifier,
|
||||
title = field.name,
|
||||
fieldFormat = RelationFormat.STATUS
|
||||
fieldFormat = RelationFormat.STATUS,
|
||||
isLocal = item.isLocal,
|
||||
onFieldClick = { onRelationClicked(item) },
|
||||
onAddToCurrentTypeClick = { onAddToTypeClicked(item) },
|
||||
onRemoveFromObjectClick = { onRemoveFromObjectClicked(item) }
|
||||
)
|
||||
} else {
|
||||
FieldTypeSelect(
|
||||
modifier = Modifier.noRippleThrottledClickable {
|
||||
onRelationClicked(item)
|
||||
},
|
||||
modifier = Modifier,
|
||||
title = field.name,
|
||||
status = field.status.first()
|
||||
status = field.status.first(),
|
||||
isLocal = item.isLocal,
|
||||
onFieldClick = { onRelationClicked(item) },
|
||||
onAddToCurrentTypeClick = { onAddToTypeClicked(item) },
|
||||
onRemoveFromObjectClick = { onRemoveFromObjectClicked(item) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -190,22 +252,27 @@ fun FieldListScreen(
|
|||
is ObjectRelationView.Tags -> {
|
||||
if (field.tags.isEmpty()) {
|
||||
FieldEmpty(
|
||||
modifier = Modifier.noRippleThrottledClickable {
|
||||
onRelationClicked(item)
|
||||
},
|
||||
modifier = Modifier,
|
||||
title = field.name,
|
||||
fieldFormat = RelationFormat.TAG
|
||||
fieldFormat = RelationFormat.TAG,
|
||||
isLocal = item.isLocal,
|
||||
onFieldClick = { onRelationClicked(item) },
|
||||
onAddToCurrentTypeClick = { onAddToTypeClicked(item) },
|
||||
onRemoveFromObjectClick = { onRemoveFromObjectClicked(item) }
|
||||
)
|
||||
} else {
|
||||
FieldTypeMultiSelect(
|
||||
modifier = Modifier.noRippleThrottledClickable {
|
||||
onRelationClicked(item)
|
||||
},
|
||||
modifier = Modifier,
|
||||
title = field.name,
|
||||
tags = field.tags
|
||||
tags = field.tags,
|
||||
isLocal = item.isLocal,
|
||||
onFieldClick = { onRelationClicked(item) },
|
||||
onAddToCurrentTypeClick = { onAddToTypeClicked(item) },
|
||||
onRemoveFromObjectClick = { onRemoveFromObjectClicked(item) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
is ObjectRelationView.Links.Backlinks,
|
||||
is ObjectRelationView.Links.From,
|
||||
is ObjectRelationView.ObjectType.Base,
|
||||
|
@ -216,16 +283,14 @@ fun FieldListScreen(
|
|||
}
|
||||
}
|
||||
|
||||
Model.Section.Featured -> {
|
||||
//TODO: Implement
|
||||
is Model.Section.Header -> {
|
||||
Section(item)
|
||||
}
|
||||
|
||||
Model.Section.Other -> {
|
||||
//TODO: Implement
|
||||
is Model.Section.SideBar -> {
|
||||
Section(item)
|
||||
}
|
||||
|
||||
is Model.Section.TypeFrom -> {
|
||||
//TODO: Implement
|
||||
Model.Section.Local -> {
|
||||
SectionLocal(onLocalInfoIconClicked)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -234,4 +299,77 @@ fun FieldListScreen(
|
|||
Spacer(modifier = Modifier.height(64.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SectionLocal(
|
||||
onLocalInfoIconClicked: () -> Unit = {}
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(52.dp)
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.padding(bottom = 7.dp, start = 20.dp)
|
||||
.align(Alignment.BottomStart),
|
||||
text = stringResource(id = R.string.object_type_fields_section_local_fields),
|
||||
style = BodyCalloutMedium,
|
||||
color = colorResource(R.color.text_primary),
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomEnd)
|
||||
.height(37.dp)
|
||||
.width(44.dp)
|
||||
.noRippleThrottledClickable {
|
||||
onLocalInfoIconClicked()
|
||||
}
|
||||
) {
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.padding(bottom = 9.dp, end = 20.dp)
|
||||
.wrapContentSize()
|
||||
.align(Alignment.BottomEnd),
|
||||
painter = painterResource(R.drawable.ic_section_local_fields),
|
||||
contentDescription = "Section local fields info"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Section(item: Model.Section) {
|
||||
val text = when (item) {
|
||||
Model.Section.Header -> stringResource(id = R.string.object_type_fields_section_header)
|
||||
Model.Section.SideBar -> stringResource(id = R.string.object_type_fields_section_fields_menu)
|
||||
else -> ""
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
contentAlignment = Alignment.CenterStart
|
||||
) {
|
||||
Text(
|
||||
text = text,
|
||||
style = BodyCalloutMedium,
|
||||
color = colorResource(id = R.color.text_secondary),
|
||||
modifier = Modifier
|
||||
.padding(vertical = 17.dp)
|
||||
.padding(start = 16.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@DefaultPreviews
|
||||
@Composable
|
||||
fun FieldListScreenPreview() {
|
||||
FieldListScreen(
|
||||
state = listOf(Model.Section.Local),
|
||||
onRelationClicked = {},
|
||||
onLocalInfoIconClicked = {},
|
||||
onTypeIconClicked = {},
|
||||
onAddToTypeClicked = {},
|
||||
onRemoveFromObjectClicked = {}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -54,7 +54,11 @@ fun TagsPreview() {
|
|||
color = ThemeColor.PINK.code,
|
||||
)
|
||||
),
|
||||
title = "Tag"
|
||||
title = "Tag",
|
||||
isLocal = true,
|
||||
onRemoveFromObjectClick = {},
|
||||
onAddToCurrentTypeClick = {},
|
||||
onFieldClick = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -76,7 +80,11 @@ fun SingleLongTagPreview() {
|
|||
color = ThemeColor.RED.code,
|
||||
),
|
||||
),
|
||||
title = "Tag"
|
||||
title = "Tag",
|
||||
isLocal = true,
|
||||
onRemoveFromObjectClick = {},
|
||||
onAddToCurrentTypeClick = {},
|
||||
onFieldClick = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -98,7 +106,11 @@ fun SingleShortTagPreview() {
|
|||
color = ThemeColor.RED.code,
|
||||
),
|
||||
),
|
||||
title = "Tag"
|
||||
title = "Tag",
|
||||
isLocal = true,
|
||||
onRemoveFromObjectClick = {},
|
||||
onAddToCurrentTypeClick = {},
|
||||
onFieldClick = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -125,7 +137,11 @@ fun TwoTagsFirstShortSecondLongPreview() {
|
|||
color = ThemeColor.ORANGE.code,
|
||||
)
|
||||
),
|
||||
title = "Tag"
|
||||
title = "Tag",
|
||||
isLocal = true,
|
||||
onRemoveFromObjectClick = {},
|
||||
onAddToCurrentTypeClick = {},
|
||||
onFieldClick = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -152,7 +168,11 @@ fun TwoShortTagsPreview() {
|
|||
color = ThemeColor.ORANGE.code,
|
||||
)
|
||||
),
|
||||
title = "Tag"
|
||||
title = "Tag",
|
||||
isLocal = true,
|
||||
onRemoveFromObjectClick = {},
|
||||
onAddToCurrentTypeClick = {},
|
||||
onFieldClick = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -184,7 +204,11 @@ fun ThreeShortTagsPreview() {
|
|||
color = ThemeColor.LIME.code,
|
||||
),
|
||||
),
|
||||
title = "Tag"
|
||||
title = "Tag",
|
||||
isLocal = true,
|
||||
onRemoveFromObjectClick = {},
|
||||
onAddToCurrentTypeClick = {},
|
||||
onFieldClick = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -221,7 +245,11 @@ fun FourTagsWithOverflowPreview() {
|
|||
color = ThemeColor.BLUE.code,
|
||||
)
|
||||
),
|
||||
title = "Tag"
|
||||
title = "Tag",
|
||||
isLocal = true,
|
||||
onRemoveFromObjectClick = {},
|
||||
onAddToCurrentTypeClick = {},
|
||||
onFieldClick = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import com.anytypeio.anytype.presentation.relations.ObjectRelationView
|
|||
import com.anytypeio.anytype.presentation.relations.RelationListViewModel
|
||||
import timber.log.Timber
|
||||
|
||||
@Deprecated("Use ListRelationViewHolder instead")
|
||||
class DocumentRelationAdapter(
|
||||
private var items: List<RelationListViewModel.Model>,
|
||||
private val onRelationClicked: (RelationListViewModel.Model.Item) -> Unit,
|
||||
|
@ -189,8 +190,8 @@ class DocumentRelationAdapter(
|
|||
if (payload is GranularChange) {
|
||||
if (payload.isModeChanged) {
|
||||
val item = items[position]
|
||||
check(item is RelationListViewModel.Model.Item)
|
||||
holder.setIsRemovable(item.isRemovable)
|
||||
// check(item is RelationListViewModel.Model.Item)
|
||||
// holder.setIsRemovable(item.isRemovable)
|
||||
} else {
|
||||
super.onBindViewHolder(holder, position, payloads)
|
||||
}
|
||||
|
@ -258,7 +259,7 @@ class DocumentRelationAdapter(
|
|||
if (holder is ListRelationViewHolder) {
|
||||
check(item is RelationListViewModel.Model.Item)
|
||||
holder.setIsFeatured(item.view.featured)
|
||||
holder.setIsRemovable(item.isRemovable)
|
||||
//holder.setIsRemovable(item.isRemovable)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -276,9 +277,9 @@ class DocumentRelationAdapter(
|
|||
else -> R.layout.item_relation_list_relation_default
|
||||
}
|
||||
}
|
||||
RelationListViewModel.Model.Section.Featured -> R.layout.item_relation_list_section
|
||||
RelationListViewModel.Model.Section.Other -> R.layout.item_relation_list_section
|
||||
is RelationListViewModel.Model.Section.TypeFrom -> R.layout.item_relation_list_section
|
||||
// RelationListViewModel.Model.Section.Featured -> R.layout.item_relation_list_section
|
||||
// RelationListViewModel.Model.Section.Other -> R.layout.item_relation_list_section
|
||||
// is RelationListViewModel.Model.Section.TypeFrom -> R.layout.item_relation_list_section
|
||||
else -> throw IllegalStateException("Unexpected item type: $item")
|
||||
}
|
||||
|
||||
|
@ -292,18 +293,18 @@ class DocumentRelationAdapter(
|
|||
class SectionViewHolder(view: View) : RecyclerView.ViewHolder(view) {
|
||||
fun bind(section: RelationListViewModel.Model.Section) {
|
||||
when (section) {
|
||||
RelationListViewModel.Model.Section.Featured -> {
|
||||
itemView.findViewById<TextView>(R.id.tvSectionName)
|
||||
.setText(R.string.featured_relations)
|
||||
}
|
||||
RelationListViewModel.Model.Section.Other -> {
|
||||
itemView.findViewById<TextView>(R.id.tvSectionName)
|
||||
.setText(R.string.other_relations)
|
||||
}
|
||||
is RelationListViewModel.Model.Section.TypeFrom -> {
|
||||
val text = itemView.resources.getString(R.string.from_type, section.typeName)
|
||||
itemView.findViewById<TextView>(R.id.tvSectionName).text = text
|
||||
}
|
||||
// RelationListViewModel.Model.Section.Featured -> {
|
||||
// itemView.findViewById<TextView>(R.id.tvSectionName)
|
||||
// .setText(R.string.featured_relations)
|
||||
// }
|
||||
// RelationListViewModel.Model.Section.Other -> {
|
||||
// itemView.findViewById<TextView>(R.id.tvSectionName)
|
||||
// .setText(R.string.other_relations)
|
||||
// }
|
||||
// is RelationListViewModel.Model.Section.TypeFrom -> {
|
||||
// val text = itemView.resources.getString(R.string.from_type, section.typeName)
|
||||
// itemView.findViewById<TextView>(R.id.tvSectionName).text = text
|
||||
// }
|
||||
else -> throw IllegalStateException("Unexpected item type: $section")
|
||||
}
|
||||
}
|
||||
|
@ -316,13 +317,14 @@ class DocumentRelationAdapter(
|
|||
override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int): Any? {
|
||||
val oldItem = old[oldItemPosition]
|
||||
val newItem = new[newItemPosition]
|
||||
return if (oldItem is RelationListViewModel.Model.Item && newItem is RelationListViewModel.Model.Item) {
|
||||
if (newItem.isRemovable != oldItem.isRemovable)
|
||||
GranularChange(isModeChanged = true)
|
||||
else
|
||||
null
|
||||
} else
|
||||
null
|
||||
return null
|
||||
// return if (oldItem is RelationListViewModel.Model.Item && newItem is RelationListViewModel.Model.Item) {
|
||||
// if (newItem.isRemovable != oldItem.isRemovable)
|
||||
// GranularChange(isModeChanged = true)
|
||||
// else
|
||||
// null
|
||||
// } else
|
||||
// null
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -78,16 +78,18 @@ fun Dragger(
|
|||
@Composable
|
||||
fun Divider(
|
||||
modifier: Modifier = Modifier,
|
||||
height: Dp = 0.5.dp,
|
||||
paddingStart: Dp = 20.dp,
|
||||
paddingEnd: Dp = 20.dp,
|
||||
visible: Boolean = true
|
||||
visible: Boolean = true,
|
||||
color: Color = colorResource(R.color.shape_primary)
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.alpha(if (visible) 1f else 0f)
|
||||
.padding(start = paddingStart, end = paddingEnd)
|
||||
.background(color = colorResource(R.color.shape_primary))
|
||||
.height(0.5.dp)
|
||||
.background(color = color)
|
||||
.height(height)
|
||||
.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,162 @@
|
|||
package com.anytypeio.anytype.core_ui.foundation
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.selection.LocalTextSelectionColors
|
||||
import androidx.compose.foundation.text.selection.TextSelectionColors
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.TextFieldDefaults
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.text.input.VisualTransformation
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.anytypeio.anytype.core_ui.common.DefaultPreviews
|
||||
import com.anytypeio.anytype.core_ui.views.BodyRegular
|
||||
import com.anytypeio.anytype.core_ui.R
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
fun DefaultSearchBar(
|
||||
modifier: Modifier = Modifier,
|
||||
onQueryChanged: (String) -> Unit
|
||||
) {
|
||||
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
val focus = LocalFocusManager.current
|
||||
val focusRequester = FocusRequester()
|
||||
|
||||
val selectionColors = TextSelectionColors(
|
||||
backgroundColor = colorResource(id = R.color.cursor_color).copy(
|
||||
alpha = 0.2f
|
||||
),
|
||||
handleColor = colorResource(id = R.color.cursor_color),
|
||||
)
|
||||
|
||||
var query by remember { mutableStateOf(TextFieldValue()) }
|
||||
|
||||
Row(
|
||||
modifier = modifier
|
||||
.background(
|
||||
color = colorResource(id = R.color.shape_transparent),
|
||||
shape = RoundedCornerShape(10.dp)
|
||||
)
|
||||
.height(40.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_search_18),
|
||||
contentDescription = "Search icon",
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterVertically)
|
||||
.padding(
|
||||
start = 11.dp
|
||||
)
|
||||
)
|
||||
CompositionLocalProvider(value = LocalTextSelectionColors provides selectionColors) {
|
||||
|
||||
BasicTextField(
|
||||
value = query,
|
||||
modifier = Modifier
|
||||
.weight(1.0f)
|
||||
.padding(start = 6.dp)
|
||||
.align(Alignment.CenterVertically)
|
||||
.focusRequester(focusRequester),
|
||||
textStyle = BodyRegular.copy(
|
||||
color = colorResource(id = R.color.text_primary)
|
||||
),
|
||||
onValueChange = { input ->
|
||||
query = input.also {
|
||||
onQueryChanged(input.text)
|
||||
}
|
||||
},
|
||||
singleLine = true,
|
||||
maxLines = 1,
|
||||
keyboardActions = KeyboardActions(
|
||||
onDone = {
|
||||
focus.clearFocus(true)
|
||||
}
|
||||
),
|
||||
decorationBox = @Composable { innerTextField ->
|
||||
TextFieldDefaults.OutlinedTextFieldDecorationBox(
|
||||
value = query.text,
|
||||
innerTextField = innerTextField,
|
||||
enabled = true,
|
||||
singleLine = true,
|
||||
visualTransformation = VisualTransformation.None,
|
||||
interactionSource = interactionSource,
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringResource(id = R.string.search),
|
||||
style = BodyRegular.copy(
|
||||
color = colorResource(id = R.color.text_tertiary)
|
||||
)
|
||||
)
|
||||
},
|
||||
colors = TextFieldDefaults.textFieldColors(
|
||||
backgroundColor = Color.Transparent,
|
||||
cursorColor = colorResource(id = R.color.cursor_color),
|
||||
),
|
||||
border = {},
|
||||
contentPadding = PaddingValues()
|
||||
)
|
||||
},
|
||||
cursorBrush = SolidColor(colorResource(id = R.color.palette_system_blue)),
|
||||
)
|
||||
}
|
||||
Spacer(Modifier.width(9.dp))
|
||||
AnimatedVisibility(
|
||||
visible = query.text.isNotEmpty(),
|
||||
enter = fadeIn(tween(100)),
|
||||
exit = fadeOut(tween(100))
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_clear_18),
|
||||
contentDescription = "Clear icon",
|
||||
modifier = Modifier
|
||||
.padding(end = 9.dp)
|
||||
.noRippleClickable {
|
||||
query = TextFieldValue().also {
|
||||
onQueryChanged("")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DefaultPreviews
|
||||
@Composable
|
||||
private fun AllContentSearchBarPreview() {
|
||||
DefaultSearchBar {}
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
package com.anytypeio.anytype.core_ui.lists.objects
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.ListItemDefaults
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.anytypeio.anytype.core_models.ObjectType
|
||||
import com.anytypeio.anytype.core_models.primitives.SpaceId
|
||||
import com.anytypeio.anytype.core_ui.R
|
||||
import com.anytypeio.anytype.core_ui.common.DefaultPreviews
|
||||
import com.anytypeio.anytype.core_ui.common.ShimmerEffect
|
||||
import com.anytypeio.anytype.core_ui.views.PreviewTitle2Regular
|
||||
import com.anytypeio.anytype.core_ui.views.Relations3
|
||||
import com.anytypeio.anytype.core_ui.widgets.ListWidgetObjectIcon
|
||||
import com.anytypeio.anytype.presentation.objects.ObjectIcon
|
||||
import com.anytypeio.anytype.presentation.objects.UiObjectsListItem
|
||||
|
||||
/**
|
||||
* A reusable composable for displaying a single UiObjectsListItem.Item
|
||||
*/
|
||||
@Composable
|
||||
fun ObjectsListItem(
|
||||
item: UiObjectsListItem.Item,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val createdBy = item.createdBy
|
||||
val typeName = item.typeName
|
||||
|
||||
ListItem(
|
||||
colors = ListItemDefaults.colors(
|
||||
containerColor = colorResource(id = R.color.background_primary),
|
||||
),
|
||||
modifier = modifier
|
||||
.height(72.dp)
|
||||
.fillMaxWidth(),
|
||||
headlineContent = {
|
||||
Text(
|
||||
text = item.name,
|
||||
style = PreviewTitle2Regular,
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
},
|
||||
supportingContent = {
|
||||
Row {
|
||||
if (!typeName.isNullOrBlank()) {
|
||||
Text(
|
||||
text = typeName,
|
||||
style = Relations3,
|
||||
color = colorResource(id = R.color.text_secondary),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
if (!createdBy.isNullOrBlank()) {
|
||||
Text(
|
||||
text = "${stringResource(R.string.date_layout_item_created_by)} • $createdBy",
|
||||
style = Relations3,
|
||||
color = colorResource(id = R.color.text_secondary),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
leadingContent = {
|
||||
ListWidgetObjectIcon(
|
||||
icon = item.icon,
|
||||
modifier = Modifier,
|
||||
iconSize = 48.dp
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ListItemLoading(
|
||||
modifier: Modifier
|
||||
) {
|
||||
ListItem(
|
||||
colors = ListItemDefaults.colors(
|
||||
containerColor = colorResource(id = R.color.background_primary),
|
||||
),
|
||||
modifier = modifier
|
||||
.height(72.dp)
|
||||
.fillMaxWidth(),
|
||||
headlineContent = {
|
||||
ShimmerEffect(
|
||||
modifier = Modifier
|
||||
.width(164.dp)
|
||||
.height(18.dp)
|
||||
)
|
||||
},
|
||||
supportingContent = {
|
||||
ShimmerEffect(
|
||||
modifier = Modifier
|
||||
.width(64.dp)
|
||||
.height(13.dp)
|
||||
)
|
||||
},
|
||||
leadingContent = {
|
||||
ShimmerEffect(
|
||||
modifier = Modifier
|
||||
.size(48.dp)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@DefaultPreviews
|
||||
@Composable
|
||||
fun PreviewObjectListItem() {
|
||||
ObjectsListItem(
|
||||
item = UiObjectsListItem.Item(
|
||||
id = "123",
|
||||
name = "Some name",
|
||||
space = SpaceId("123"),
|
||||
type = "123",
|
||||
typeName = "Some type",
|
||||
createdBy = "Some user",
|
||||
layout = ObjectType.Layout.BASIC,
|
||||
icon = ObjectIcon.Empty.Page,
|
||||
isPossibleToDelete = true
|
||||
)
|
||||
)
|
||||
}
|
|
@ -0,0 +1,165 @@
|
|||
package com.anytypeio.anytype.core_ui.lists.objects
|
||||
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxScope
|
||||
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.rememberLazyListState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.anytypeio.anytype.core_ui.R
|
||||
import com.anytypeio.anytype.core_ui.common.DefaultPreviews
|
||||
import com.anytypeio.anytype.core_ui.extensions.swapList
|
||||
import com.anytypeio.anytype.core_ui.foundation.Divider
|
||||
import com.anytypeio.anytype.core_ui.foundation.noRippleThrottledClickable
|
||||
import com.anytypeio.anytype.core_ui.lists.objects.stubs.StubVerticalItems
|
||||
import com.anytypeio.anytype.core_ui.views.ButtonSize
|
||||
import com.anytypeio.anytype.core_ui.views.animations.DotsLoadingIndicator
|
||||
import com.anytypeio.anytype.core_ui.views.animations.FadeAnimationSpecs
|
||||
import com.anytypeio.anytype.presentation.objects.UiObjectsListItem
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun PaginatedObjectList(
|
||||
state: UiObjectsListState,
|
||||
uiState: UiContentState,
|
||||
canPaginate: Boolean,
|
||||
onLoadMore: () -> Unit,
|
||||
onObjectClicked: (UiObjectsListItem.Item) -> Unit,
|
||||
onMoveToBin: (UiObjectsListItem.Item) -> Unit,
|
||||
) {
|
||||
val items = remember { mutableStateListOf<UiObjectsListItem>() }
|
||||
items.swapList(state.items)
|
||||
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
val lazyListState = rememberLazyListState()
|
||||
|
||||
val canPaginateState = remember { mutableStateOf(false) }
|
||||
LaunchedEffect(key1 = canPaginate) {
|
||||
canPaginateState.value = canPaginate
|
||||
}
|
||||
|
||||
val shouldStartPaging = remember {
|
||||
derivedStateOf {
|
||||
canPaginateState.value && (lazyListState.layoutInfo.visibleItemsInfo.lastOrNull()?.index
|
||||
?: -9) >= (lazyListState.layoutInfo.totalItemsCount - 2)
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(key1 = shouldStartPaging.value) {
|
||||
if (shouldStartPaging.value && uiState is UiContentState.Idle) {
|
||||
onLoadMore()
|
||||
}
|
||||
}
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.padding(top = 8.dp)
|
||||
.fillMaxSize(),
|
||||
state = lazyListState
|
||||
) {
|
||||
items(
|
||||
count = items.size,
|
||||
key = { index -> items[index].id },
|
||||
contentType = { index ->
|
||||
when (items[index]) {
|
||||
is UiObjectsListItem.Loading -> "loading"
|
||||
is UiObjectsListItem.Item -> "item"
|
||||
}
|
||||
}
|
||||
) { index ->
|
||||
val item = items[index]
|
||||
when (item) {
|
||||
is UiObjectsListItem.Item -> {
|
||||
SwipeToDismissItem(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.animateItem()
|
||||
.noRippleThrottledClickable {
|
||||
onObjectClicked(item)
|
||||
},
|
||||
item = item,
|
||||
onObjectClicked = onObjectClicked,
|
||||
onMoveToBin = onMoveToBin
|
||||
)
|
||||
Divider(paddingStart = 16.dp, paddingEnd = 16.dp)
|
||||
}
|
||||
is UiObjectsListItem.Loading -> {
|
||||
ListItemLoading(modifier = Modifier)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (uiState is UiContentState.Paging) {
|
||||
item {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillParentMaxWidth()
|
||||
.height(52.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
LoadingState()
|
||||
}
|
||||
}
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(200.dp))
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(key1 = uiState) {
|
||||
if (uiState is UiContentState.Idle) {
|
||||
if (uiState.scrollToTop) {
|
||||
scope.launch {
|
||||
lazyListState.scrollToItem(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BoxScope.LoadingState() {
|
||||
val loadingAlpha by animateFloatAsState(targetValue = 1f, label = "")
|
||||
DotsLoadingIndicator(
|
||||
animating = true,
|
||||
modifier = Modifier
|
||||
.graphicsLayer { alpha = loadingAlpha }
|
||||
.align(Alignment.Center),
|
||||
animationSpecs = FadeAnimationSpecs(itemCount = 3),
|
||||
color = colorResource(id = R.color.glyph_active),
|
||||
size = ButtonSize.Small
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@DefaultPreviews
|
||||
fun ObjectsListScreenPreview() {
|
||||
val contentListState = UiObjectsListState(
|
||||
items = StubVerticalItems
|
||||
)
|
||||
PaginatedObjectList(
|
||||
state = contentListState,
|
||||
uiState = UiContentState.Idle(scrollToTop = false),
|
||||
canPaginate = true,
|
||||
onObjectClicked = {},
|
||||
onLoadMore = {},
|
||||
onMoveToBin = {},
|
||||
)
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package com.anytypeio.anytype.core_ui.lists.objects
|
||||
|
||||
import com.anytypeio.anytype.presentation.objects.UiObjectsListItem
|
||||
|
||||
sealed class UiContentState {
|
||||
data class Idle(val scrollToTop: Boolean = false) : UiContentState()
|
||||
data object InitLoading : UiContentState()
|
||||
data object Paging : UiContentState()
|
||||
data object Empty : UiContentState()
|
||||
}
|
||||
|
||||
data class UiObjectsListState(
|
||||
val items: List<UiObjectsListItem>
|
||||
) {
|
||||
companion object {
|
||||
|
||||
val Empty = UiObjectsListState(items = emptyList())
|
||||
val LoadingState = UiObjectsListState(
|
||||
items = listOf(
|
||||
UiObjectsListItem.Loading("Loading-Item-1"),
|
||||
UiObjectsListItem.Loading("Loading-Item-2"),
|
||||
UiObjectsListItem.Loading("Loading-Item-3"),
|
||||
UiObjectsListItem.Loading("Loading-Item-4"),
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
package com.anytypeio.anytype.core_ui.lists.objects
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.shrinkVertically
|
||||
import androidx.compose.material3.SwipeToDismissBox
|
||||
import androidx.compose.material3.SwipeToDismissBoxValue
|
||||
import androidx.compose.material3.rememberSwipeToDismissBoxState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.anytypeio.anytype.core_ui.R
|
||||
import com.anytypeio.anytype.core_ui.foundation.DismissBackground
|
||||
import com.anytypeio.anytype.core_ui.foundation.noRippleThrottledClickable
|
||||
import com.anytypeio.anytype.presentation.objects.UiObjectsListItem
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
@Composable
|
||||
fun SwipeToDismissItem(
|
||||
item: UiObjectsListItem.Item,
|
||||
modifier: Modifier,
|
||||
animationDuration: Int = 500,
|
||||
onObjectClicked: (UiObjectsListItem.Item) -> Unit,
|
||||
onMoveToBin: (UiObjectsListItem.Item) -> Unit,
|
||||
) {
|
||||
var isRemoved by remember { mutableStateOf(false) }
|
||||
val dismissState = rememberSwipeToDismissBoxState(
|
||||
initialValue = SwipeToDismissBoxValue.Settled,
|
||||
confirmValueChange = { value ->
|
||||
if (value == SwipeToDismissBoxValue.EndToStart) {
|
||||
isRemoved = true
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
return@rememberSwipeToDismissBoxState true
|
||||
},
|
||||
positionalThreshold = { it * .5f }
|
||||
)
|
||||
|
||||
if (dismissState.currentValue != SwipeToDismissBoxValue.Settled) {
|
||||
LaunchedEffect(Unit) {
|
||||
dismissState.snapTo(SwipeToDismissBoxValue.Settled)
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(key1 = isRemoved) {
|
||||
if (isRemoved) {
|
||||
delay(animationDuration.toLong())
|
||||
onMoveToBin(item)
|
||||
}
|
||||
}
|
||||
AnimatedVisibility(
|
||||
visible = !isRemoved,
|
||||
exit = shrinkVertically(
|
||||
animationSpec = tween(durationMillis = animationDuration),
|
||||
shrinkTowards = Alignment.Top
|
||||
) + fadeOut()
|
||||
) {
|
||||
SwipeToDismissBox(
|
||||
modifier = modifier,
|
||||
state = dismissState,
|
||||
enableDismissFromEndToStart = item.isPossibleToDelete,
|
||||
enableDismissFromStartToEnd = false,
|
||||
backgroundContent = {
|
||||
DismissBackground(
|
||||
actionText = stringResource(R.string.move_to_bin),
|
||||
dismissState = dismissState
|
||||
)
|
||||
},
|
||||
content = {
|
||||
ObjectsListItem(
|
||||
modifier = Modifier
|
||||
.noRippleThrottledClickable {
|
||||
onObjectClicked(item)
|
||||
},
|
||||
item = item
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,212 @@
|
|||
package com.anytypeio.anytype.core_ui.lists.objects.menu
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.foundation.layout.wrapContentSize
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment.Companion.CenterVertically
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.rotate
|
||||
import androidx.compose.ui.graphics.ColorFilter.Companion.tint
|
||||
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.core_ui.R
|
||||
import com.anytypeio.anytype.core_models.DVSortType
|
||||
import com.anytypeio.anytype.core_ui.foundation.Divider
|
||||
import com.anytypeio.anytype.core_ui.views.BodyCalloutRegular
|
||||
import com.anytypeio.anytype.core_ui.views.UXBody
|
||||
import com.anytypeio.anytype.presentation.objects.MenuSortsItem
|
||||
import com.anytypeio.anytype.presentation.objects.ObjectsListSort
|
||||
|
||||
@Composable
|
||||
fun ObjectsListSortingMenuContainer(
|
||||
container: MenuSortsItem.Container,
|
||||
sorts: List<MenuSortsItem.Sort>,
|
||||
types: List<MenuSortsItem.SortType>,
|
||||
sortingExpanded: Boolean,
|
||||
onChangeSortExpandedState: (Boolean) -> Unit,
|
||||
onSortClick: (ObjectsListSort) -> Unit
|
||||
) {
|
||||
|
||||
SortingBox(
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
onChangeSortExpandedState(!sortingExpanded)
|
||||
},
|
||||
subtitle = container.sort.title(),
|
||||
isExpanded = sortingExpanded
|
||||
)
|
||||
Divider(
|
||||
paddingStart = 0.dp,
|
||||
paddingEnd = 0.dp,
|
||||
color = colorResource(R.color.shape_secondary)
|
||||
)
|
||||
if (sortingExpanded) {
|
||||
sorts.forEach { item ->
|
||||
ObjectsListMenuItem(
|
||||
title = item.sort.title(),
|
||||
isSelected = item.sort.isSelected,
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
onSortClick(item.sort)
|
||||
}
|
||||
)
|
||||
Divider(
|
||||
paddingStart = 0.dp,
|
||||
paddingEnd = 0.dp,
|
||||
color = colorResource(R.color.shape_secondary)
|
||||
)
|
||||
}
|
||||
Divider(
|
||||
height = 7.5.dp,
|
||||
paddingStart = 0.dp,
|
||||
paddingEnd = 0.dp,
|
||||
color = colorResource(R.color.shape_secondary)
|
||||
)
|
||||
val size = types.size
|
||||
types.forEachIndexed { index, item ->
|
||||
val s = item.sort
|
||||
ObjectsListMenuItem(
|
||||
title = item.sortType.title(item.sort),
|
||||
isSelected = item.isSelected,
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
val updatedSort = when (val sort = item.sort) {
|
||||
is ObjectsListSort.ByName -> sort.copy(sortType = item.sortType)
|
||||
is ObjectsListSort.ByDateCreated -> sort.copy(sortType = item.sortType)
|
||||
is ObjectsListSort.ByDateUpdated -> sort.copy(sortType = item.sortType)
|
||||
is ObjectsListSort.ByDateUsed -> sort.copy(sortType = item.sortType)
|
||||
}
|
||||
onSortClick(updatedSort)
|
||||
}
|
||||
)
|
||||
if (index < size - 1) {
|
||||
Divider(
|
||||
paddingStart = 0.dp,
|
||||
paddingEnd = 0.dp,
|
||||
color = colorResource(R.color.shape_secondary)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SortingBox(modifier: Modifier, subtitle: String, isExpanded: Boolean) {
|
||||
val rotationAngle = if (isExpanded) 90f else 0f
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.background(colorResource(id = R.color.background_secondary)),
|
||||
verticalAlignment = CenterVertically
|
||||
) {
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.padding(start = 10.dp)
|
||||
.size(18.dp)
|
||||
.rotate(rotationAngle),
|
||||
painter = painterResource(R.drawable.ic_arrow_disclosure_18),
|
||||
contentDescription = "",
|
||||
colorFilter = tint(colorResource(id = R.color.glyph_selected))
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.wrapContentHeight()
|
||||
.padding(top = 11.dp, bottom = 10.dp, start = 6.dp)
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.all_content_sort_by),
|
||||
modifier = Modifier.wrapContentSize(),
|
||||
style = UXBody,
|
||||
color = colorResource(id = R.color.text_primary)
|
||||
)
|
||||
Text(
|
||||
text = subtitle,
|
||||
modifier = Modifier.wrapContentSize(),
|
||||
style = BodyCalloutRegular,
|
||||
color = colorResource(id = R.color.text_secondary)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ObjectsListMenuItem(
|
||||
modifier: Modifier,
|
||||
title: String,
|
||||
isSelected: Boolean,
|
||||
contentDescription: String? = null
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.height(44.dp)
|
||||
.background(colorResource(id = R.color.background_secondary)),
|
||||
verticalAlignment = CenterVertically,
|
||||
) {
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.wrapContentSize()
|
||||
.padding(start = 12.dp),
|
||||
painter = painterResource(R.drawable.ic_check_16),
|
||||
contentDescription = contentDescription,
|
||||
alpha = if (isSelected) 1f else 0f
|
||||
)
|
||||
Text(
|
||||
text = title,
|
||||
modifier = Modifier
|
||||
.wrapContentSize()
|
||||
.padding(start = 8.dp),
|
||||
style = UXBody,
|
||||
color = colorResource(id = R.color.text_primary)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ObjectsListSort.title(): String = stringResource(
|
||||
when (this) {
|
||||
is ObjectsListSort.ByDateCreated -> R.string.all_content_sort_date_created
|
||||
is ObjectsListSort.ByDateUpdated -> R.string.all_content_sort_date_updated
|
||||
is ObjectsListSort.ByName -> R.string.all_content_sort_name
|
||||
is ObjectsListSort.ByDateUsed -> R.string.all_content_sort_date_used
|
||||
}
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun DVSortType.title(sort: ObjectsListSort): String = when (this) {
|
||||
DVSortType.ASC -> {
|
||||
when (sort) {
|
||||
is ObjectsListSort.ByDateCreated, is ObjectsListSort.ByDateUpdated, is ObjectsListSort.ByDateUsed -> stringResource(
|
||||
id = R.string.all_content_sort_date_asc
|
||||
)
|
||||
|
||||
is ObjectsListSort.ByName -> stringResource(id = R.string.all_content_sort_name_asc)
|
||||
}
|
||||
}
|
||||
|
||||
DVSortType.DESC -> {
|
||||
when (sort) {
|
||||
is ObjectsListSort.ByDateCreated,
|
||||
is ObjectsListSort.ByDateUpdated,
|
||||
is ObjectsListSort.ByDateUsed -> stringResource(
|
||||
id = R.string.all_content_sort_date_desc
|
||||
)
|
||||
|
||||
is ObjectsListSort.ByName -> stringResource(id = R.string.all_content_sort_name_desc)
|
||||
}
|
||||
}
|
||||
|
||||
DVSortType.CUSTOM -> ""
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package com.anytypeio.anytype.core_ui.lists.objects.stubs
|
||||
|
||||
import com.anytypeio.anytype.core_models.ObjectType
|
||||
import com.anytypeio.anytype.core_models.primitives.SpaceId
|
||||
import com.anytypeio.anytype.presentation.objects.ObjectIcon
|
||||
import com.anytypeio.anytype.presentation.objects.UiObjectsListItem
|
||||
|
||||
val StubVerticalItems = listOf(
|
||||
UiObjectsListItem.Item(
|
||||
id = "1",
|
||||
name = "Task Object",
|
||||
space = SpaceId("space1"),
|
||||
type = "type1",
|
||||
typeName = "Task",
|
||||
createdBy = "by Joseph Wolf",
|
||||
layout = ObjectType.Layout.TODO,
|
||||
icon = ObjectIcon.Task(isChecked = true)
|
||||
),
|
||||
UiObjectsListItem.Item(
|
||||
id = "2",
|
||||
name = "Page Object",
|
||||
space = SpaceId("space2"),
|
||||
type = "type2",
|
||||
typeName = "Page",
|
||||
createdBy = "by Mike Long",
|
||||
layout = ObjectType.Layout.BASIC,
|
||||
icon = ObjectIcon.Empty.Page
|
||||
),
|
||||
UiObjectsListItem.Item(
|
||||
id = "3",
|
||||
name = "File Object",
|
||||
space = SpaceId("space3"),
|
||||
type = "type3",
|
||||
typeName = "File",
|
||||
createdBy = "by John Doe",
|
||||
layout = ObjectType.Layout.FILE,
|
||||
icon = ObjectIcon.File(
|
||||
mime = "image/png",
|
||||
fileName = "test_image.png"
|
||||
)
|
||||
)
|
||||
)
|
|
@ -32,7 +32,8 @@ fun ListWidgetObjectIcon(
|
|||
icon: ObjectIcon,
|
||||
modifier: Modifier,
|
||||
iconSize: Dp = 48.dp,
|
||||
onTaskIconClicked: (Boolean) -> Unit = {}
|
||||
onTaskIconClicked: (Boolean) -> Unit = {},
|
||||
backgroundColor: Int = R.color.shape_tertiary
|
||||
) {
|
||||
when (icon) {
|
||||
is ObjectIcon.Profile.Avatar -> {
|
||||
|
@ -46,7 +47,7 @@ fun ListWidgetObjectIcon(
|
|||
DefaultProfileIconImage(icon, modifier, iconSize)
|
||||
}
|
||||
is ObjectIcon.Basic.Emoji -> {
|
||||
EmojiIconView(icon = icon, backgroundSize = iconSize, modifier = modifier)
|
||||
EmojiIconView(icon = icon, backgroundSize = iconSize, modifier = modifier, backgroundColor = backgroundColor)
|
||||
}
|
||||
is ObjectIcon.Basic.Image -> {
|
||||
DefaultObjectImageIcon(icon.hash, modifier, iconSize, fallback = icon.emptyState)
|
||||
|
@ -77,7 +78,8 @@ fun ListWidgetObjectIcon(
|
|||
EmptyIconView(
|
||||
modifier = modifier,
|
||||
emptyType = icon,
|
||||
backgroundSize = iconSize
|
||||
backgroundSize = iconSize,
|
||||
backgroundColor = backgroundColor
|
||||
)
|
||||
}
|
||||
ObjectIcon.None -> {}
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
package com.anytypeio.anytype.core_ui.widgets
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.LinearLayout
|
||||
import com.anytypeio.anytype.core_ui.databinding.WidgetObjectMenuDescriptionBinding
|
||||
import com.anytypeio.anytype.core_ui.R
|
||||
|
||||
class ObjectMenuDescriptionItem @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null
|
||||
) : LinearLayout(context, attrs) {
|
||||
|
||||
|
||||
val binding = WidgetObjectMenuDescriptionBinding.inflate(
|
||||
LayoutInflater.from(context), this
|
||||
)
|
||||
|
||||
fun setAction(setAsHide: Boolean) {
|
||||
binding.descriptionAction.text =
|
||||
if (setAsHide) context.getString(R.string.modal_hide) else context.getString(R.string.modal_show)
|
||||
}
|
||||
}
|
|
@ -25,7 +25,6 @@ class ObjectMenuItemWidget @JvmOverloads constructor(
|
|||
if (set == null) return
|
||||
val attrs = context.obtainStyledAttributes(set, R.styleable.ObjectMenuItemWidget, 0, 0)
|
||||
tvTitle.text = attrs.getString(R.styleable.ObjectMenuItemWidget_title)
|
||||
tvSubtitle.text = attrs.getString(R.styleable.ObjectMenuItemWidget_subtitle)
|
||||
ivIcon.setImageResource(attrs.getResourceId(R.styleable.ObjectMenuItemWidget_icon, -1))
|
||||
attrs.recycle()
|
||||
}
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
package com.anytypeio.anytype.core_ui.widgets
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
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.core_models.primitives.TypeId
|
||||
import com.anytypeio.anytype.core_models.primitives.TypeKey
|
||||
import com.anytypeio.anytype.core_ui.common.DefaultPreviews
|
||||
import com.anytypeio.anytype.presentation.editor.cover.CoverColor
|
||||
import com.anytypeio.anytype.presentation.templates.TemplateObjectTypeView
|
||||
import com.anytypeio.anytype.presentation.templates.TemplateView
|
||||
import com.anytypeio.anytype.presentation.templates.TemplateView.Companion.DEFAULT_TEMPLATE_ID_BLANK
|
||||
import com.anytypeio.anytype.presentation.widgets.TypeTemplatesWidgetUI
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
||||
@DefaultPreviews
|
||||
@Composable
|
||||
fun TypeTemplatesWidgetPreview() {
|
||||
val items = listOf(
|
||||
TemplateView.Blank(
|
||||
id = DEFAULT_TEMPLATE_ID_BLANK,
|
||||
targetTypeId = TypeId("page"),
|
||||
targetTypeKey = TypeKey("ot-page"),
|
||||
typeName = "Page",
|
||||
layout = ObjectType.Layout.BASIC.code
|
||||
),
|
||||
TemplateView.Template(
|
||||
id = "1",
|
||||
name = "Template Title",
|
||||
targetTypeId = TypeId("page"),
|
||||
targetTypeKey = TypeKey("ot-page"),
|
||||
layout = ObjectType.Layout.PROFILE,
|
||||
image = null,
|
||||
emoji = "📄",
|
||||
coverColor = CoverColor.RED,
|
||||
coverGradient = null,
|
||||
coverImage = null,
|
||||
isDefault = true
|
||||
),
|
||||
)
|
||||
val state = TypeTemplatesWidgetUI.Data(
|
||||
templates = items,
|
||||
showWidget = true,
|
||||
isEditing = true,
|
||||
moreMenuItem = null,
|
||||
objectTypes = listOf(
|
||||
TemplateObjectTypeView.Search,
|
||||
TemplateObjectTypeView.Item(
|
||||
type = ObjectWrapper.Type(
|
||||
map = mapOf(Relations.ID to "123", Relations.NAME to "Page"),
|
||||
)
|
||||
)
|
||||
),
|
||||
viewerId = "",
|
||||
isPossibleToChangeType = true,
|
||||
isPossibleToChangeTemplate = false
|
||||
)
|
||||
TypeTemplatesWidget(
|
||||
state = state,
|
||||
onDismiss = {},
|
||||
editClick = {},
|
||||
doneClick = {},
|
||||
moreClick = {},
|
||||
scope = CoroutineScope(
|
||||
Dispatchers.Main
|
||||
),
|
||||
menuClick = {},
|
||||
action = {}
|
||||
)
|
||||
}
|
|
@ -14,7 +14,9 @@ import androidx.compose.foundation.clickable
|
|||
import androidx.compose.foundation.gestures.Orientation
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxScope
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
|
@ -71,7 +73,6 @@ import androidx.compose.ui.text.TextStyle
|
|||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.IntOffset
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
@ -80,19 +81,17 @@ import androidx.compose.ui.unit.sp
|
|||
import coil.compose.rememberAsyncImagePainter
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
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.core_models.primitives.TypeId
|
||||
import com.anytypeio.anytype.core_models.primitives.TypeKey
|
||||
import com.anytypeio.anytype.core_ui.R
|
||||
import com.anytypeio.anytype.core_ui.foundation.Divider
|
||||
import com.anytypeio.anytype.core_ui.foundation.Dragger
|
||||
import com.anytypeio.anytype.core_ui.foundation.noRippleClickable
|
||||
import com.anytypeio.anytype.core_ui.foundation.noRippleThrottledClickable
|
||||
import com.anytypeio.anytype.core_ui.views.AvatarTitle
|
||||
import com.anytypeio.anytype.core_ui.views.BodyCalloutMedium
|
||||
import com.anytypeio.anytype.core_ui.views.BodyCalloutRegular
|
||||
import com.anytypeio.anytype.core_ui.views.BodyRegular
|
||||
import com.anytypeio.anytype.core_ui.views.Caption1Medium
|
||||
import com.anytypeio.anytype.core_ui.views.Caption2Medium
|
||||
import com.anytypeio.anytype.core_ui.views.Caption2Semibold
|
||||
import com.anytypeio.anytype.core_ui.views.Title1
|
||||
import com.anytypeio.anytype.core_ui.views.fontInterRegular
|
||||
|
@ -101,14 +100,15 @@ import com.anytypeio.anytype.presentation.editor.cover.CoverGradient
|
|||
import com.anytypeio.anytype.presentation.templates.TemplateMenuClick
|
||||
import com.anytypeio.anytype.presentation.templates.TemplateObjectTypeView
|
||||
import com.anytypeio.anytype.presentation.templates.TemplateView
|
||||
import com.anytypeio.anytype.presentation.templates.TemplateView.Companion.DEFAULT_TEMPLATE_ID_BLANK
|
||||
import com.anytypeio.anytype.presentation.widgets.TypeTemplatesWidgetUI
|
||||
import com.anytypeio.anytype.presentation.widgets.TypeTemplatesWidgetUIAction
|
||||
import com.anytypeio.anytype.presentation.widgets.TypeTemplatesWidgetUIAction.TemplateClick
|
||||
import com.anytypeio.anytype.presentation.widgets.TypeTemplatesWidgetUIAction.TypeClick
|
||||
import com.bumptech.glide.integration.compose.ExperimentalGlideComposeApi
|
||||
import com.bumptech.glide.integration.compose.GlideImage
|
||||
import com.bumptech.glide.integration.compose.placeholder
|
||||
import kotlin.math.roundToInt
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
|
||||
|
@ -414,7 +414,7 @@ private fun TemplatesList(
|
|||
scrollState: LazyListState,
|
||||
state: TypeTemplatesWidgetUI.Data,
|
||||
action: (TypeTemplatesWidgetUIAction) -> Unit,
|
||||
moreClick: (TemplateView, IntOffset) -> Unit
|
||||
moreClick: (TemplateView, IntOffset) -> Unit,
|
||||
) {
|
||||
LazyRow(
|
||||
state = scrollState,
|
||||
|
@ -497,7 +497,10 @@ private fun TemplatesList(
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun TemplateItemContent(item: TemplateView) {
|
||||
fun BoxScope.TemplateItemContent(
|
||||
item: TemplateView,
|
||||
showDefaultIcon: Boolean = false
|
||||
) {
|
||||
Column {
|
||||
when (item) {
|
||||
is TemplateView.Blank -> {
|
||||
|
@ -506,63 +509,20 @@ private fun TemplateItemContent(item: TemplateView) {
|
|||
}
|
||||
|
||||
is TemplateView.Template -> {
|
||||
if (item.isCoverPresent()) {
|
||||
TemplateItemCoverAndIcon(item)
|
||||
if (item.layout == ObjectType.Layout.TODO) {
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
TemplateItemTodoTitle(text = item.name)
|
||||
} else {
|
||||
if (!item.isImageOrEmojiPresent()) {
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
} else {
|
||||
Spacer(modifier = Modifier.height(6.dp))
|
||||
}
|
||||
TemplateItemTitle(
|
||||
text = item.name,
|
||||
textAlign = getProperTextAlign(item.layout)
|
||||
)
|
||||
when (item.layout) {
|
||||
ObjectType.Layout.BASIC -> {
|
||||
TemplateHeaderBasic(item)
|
||||
}
|
||||
} else {
|
||||
if (item.layout == ObjectType.Layout.TODO) {
|
||||
ObjectType.Layout.PROFILE -> {
|
||||
TemplateHeaderProfile(item)
|
||||
}
|
||||
ObjectType.Layout.TODO -> {
|
||||
TemplateHeaderTask(item)
|
||||
}
|
||||
else -> {
|
||||
Spacer(modifier = Modifier.height(28.dp))
|
||||
TemplateItemTodoTitle(text = item.name)
|
||||
} else {
|
||||
if (item.isImageOrEmojiPresent()) {
|
||||
if (item.layout.isProfileOrParticipant()) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.wrapContentWidth()
|
||||
.height(68.dp)
|
||||
.padding(top = 28.dp)
|
||||
.align(Alignment.CenterHorizontally)
|
||||
) {
|
||||
val modifier = Modifier.clip(CircleShape)
|
||||
TemplateItemIconOrImage(item = item, modifier = modifier)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(6.dp))
|
||||
TemplateItemTitle(
|
||||
text = item.name,
|
||||
textAlign = getProperTextAlign(item.layout)
|
||||
)
|
||||
} else {
|
||||
val modifier = Modifier
|
||||
.padding(start = 14.dp, top = 26.dp)
|
||||
TemplateItemIconOrImage(item = item, modifier = modifier)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
TemplateItemTitle(
|
||||
text = item.name, textAlign = getProperTextAlign(item.layout)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Spacer(modifier = Modifier.height(28.dp))
|
||||
TemplateItemTitle(
|
||||
text = item.name,
|
||||
textAlign = getProperTextAlign(item.layout)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
TemplateItemRectangles()
|
||||
}
|
||||
|
||||
|
@ -581,8 +541,104 @@ private fun TemplateItemContent(item: TemplateView) {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (showDefaultIcon && item.isDefault) {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.padding(bottom = 8.dp)
|
||||
.wrapContentSize()
|
||||
.background(
|
||||
shape = RoundedCornerShape(4.dp),
|
||||
color = colorResource(R.color.shape_tertiary)
|
||||
)
|
||||
.padding(start = 6.dp, end = 6.dp, top = 2.dp, bottom = 2.dp)
|
||||
.align(Alignment.BottomCenter),
|
||||
text = stringResource(R.string.default_template_icon),
|
||||
textAlign = TextAlign.Center,
|
||||
color = colorResource(R.color.text_secondary),
|
||||
style = Caption2Medium
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ColumnScope.TemplateHeaderBasic(item: TemplateView.Template) {
|
||||
if (item.isCoverPresent()) {
|
||||
TemplateItemCoverAndIcon(item)
|
||||
} else {
|
||||
Spacer(modifier = Modifier.height(28.dp))
|
||||
TemplateItemIconOrImage(item = item)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
TemplateItemTitle(
|
||||
text = item.name,
|
||||
textAlign = getProperTextAlign(item.layout)
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalGlideComposeApi::class)
|
||||
@Composable
|
||||
private fun ColumnScope.TemplateHeaderProfile(item: TemplateView.Template) {
|
||||
if (item.isCoverPresent()) {
|
||||
TemplateItemCoverAndIcon(item)
|
||||
} else {
|
||||
ProfileIcon(
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally).padding(top = 28.dp),
|
||||
item = item
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
TemplateItemTitle(
|
||||
text = item.name,
|
||||
textAlign = getProperTextAlign(item.layout)
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalGlideComposeApi::class)
|
||||
@Composable
|
||||
private fun ProfileIcon(
|
||||
modifier: Modifier = Modifier,
|
||||
item: TemplateView.Template
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.size(40.dp)
|
||||
.background(
|
||||
color = colorResource(id = R.color.shape_tertiary),
|
||||
shape = CircleShape
|
||||
)
|
||||
) {
|
||||
if (item.image != null) {
|
||||
GlideImage(
|
||||
model = item.image,
|
||||
contentDescription = "Custom image template's icon",
|
||||
modifier = Modifier
|
||||
.size(40.dp)
|
||||
.align(Alignment.Center),
|
||||
contentScale = ContentScale.Crop
|
||||
)
|
||||
} else {
|
||||
Text(
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
text = item.name.firstOrNull()?.toString() ?: "U",
|
||||
style = AvatarTitle.copy(
|
||||
fontSize = 24.sp,
|
||||
),
|
||||
color = colorResource(id = R.color.glyph_active)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ColumnScope.TemplateHeaderTask(item: TemplateView.Template) {
|
||||
TemplateItemCoverColor(item = item)
|
||||
TemplateItemCoverImage(item = item)
|
||||
TemplateItemCoverGradient(item = item)
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
TemplateItemTodoTitle(text = item.name)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalGlideComposeApi::class)
|
||||
@Composable
|
||||
private fun TemplateItemIconOrImage(
|
||||
item: TemplateView.Template,
|
||||
|
@ -591,22 +647,16 @@ private fun TemplateItemIconOrImage(
|
|||
item.image?.let {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.wrapContentSize()
|
||||
.border(
|
||||
width = 2.dp,
|
||||
color = colorResource(id = R.color.background_primary),
|
||||
shape = RoundedCornerShape(2.dp)
|
||||
)
|
||||
.clip(RoundedCornerShape(2.dp))
|
||||
.padding(start = 16.dp)
|
||||
.size(40.dp)
|
||||
.background(
|
||||
color = colorResource(id = R.color.shape_tertiary)
|
||||
color = colorResource(id = R.color.shape_tertiary),
|
||||
shape = RoundedCornerShape(5.dp)
|
||||
)
|
||||
) {
|
||||
Image(
|
||||
painter = rememberAsyncImagePainter(
|
||||
model = it,
|
||||
error = painterResource(id = R.drawable.ic_home_widget_space)
|
||||
),
|
||||
GlideImage(
|
||||
model = it,
|
||||
failure = placeholder(painterResource(id = R.drawable.ic_home_widget_space)),
|
||||
contentDescription = "Custom image template's icon",
|
||||
modifier = Modifier
|
||||
.size(40.dp)
|
||||
|
@ -618,17 +668,12 @@ private fun TemplateItemIconOrImage(
|
|||
item.emoji?.let {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.wrapContentSize()
|
||||
.border(
|
||||
width = 2.dp,
|
||||
color = colorResource(id = R.color.background_primary),
|
||||
shape = RoundedCornerShape(8.dp)
|
||||
)
|
||||
.clip(RoundedCornerShape(8.dp))
|
||||
.padding(start = 16.dp)
|
||||
.size(40.dp)
|
||||
.background(
|
||||
color = colorResource(id = R.color.shape_tertiary)
|
||||
color = colorResource(id = R.color.text_tertiary),
|
||||
shape = RoundedCornerShape(5.dp)
|
||||
)
|
||||
.padding(8.dp)
|
||||
) {
|
||||
Image(
|
||||
painter = rememberAsyncImagePainter(
|
||||
|
@ -645,6 +690,7 @@ private fun TemplateItemIconOrImage(
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalGlideComposeApi::class)
|
||||
@Composable
|
||||
private fun TemplateItemCoverAndIcon(item: TemplateView.Template) {
|
||||
Box(
|
||||
|
@ -658,20 +704,11 @@ private fun TemplateItemCoverAndIcon(item: TemplateView.Template) {
|
|||
when (item.layout) {
|
||||
ObjectType.Layout.TODO -> {}
|
||||
ObjectType.Layout.PROFILE, ObjectType.Layout.PARTICIPANT -> {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.wrapContentWidth()
|
||||
.height(82.dp)
|
||||
.padding(top = 50.dp)
|
||||
.align(Alignment.TopCenter)
|
||||
) {
|
||||
val modifier = Modifier
|
||||
.clip(CircleShape)
|
||||
.align(Alignment.Center)
|
||||
TemplateItemIconOrImage(item = item, modifier = modifier)
|
||||
}
|
||||
ProfileIcon(
|
||||
modifier = Modifier.align(Alignment.BottomCenter),
|
||||
item = item
|
||||
)
|
||||
}
|
||||
|
||||
else -> {
|
||||
val modifier = Modifier
|
||||
.padding(start = 14.dp, top = 44.dp)
|
||||
|
@ -991,65 +1028,4 @@ fun ObjectTypesList(
|
|||
enum class DragStates {
|
||||
VISIBLE,
|
||||
DISMISSED
|
||||
}
|
||||
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun ComposablePreview() {
|
||||
val items = listOf(
|
||||
TemplateView.Blank(
|
||||
id = DEFAULT_TEMPLATE_ID_BLANK,
|
||||
targetTypeId = TypeId("page"),
|
||||
targetTypeKey = TypeKey("ot-page"),
|
||||
typeName = "Page",
|
||||
layout = ObjectType.Layout.BASIC.code
|
||||
),
|
||||
TemplateView.Template(
|
||||
id = "1",
|
||||
name = "Template 1",
|
||||
targetTypeId = TypeId("page"),
|
||||
targetTypeKey = TypeKey("ot-page"),
|
||||
layout = ObjectType.Layout.BASIC,
|
||||
image = null,
|
||||
emoji = null,
|
||||
coverColor = null,
|
||||
coverGradient = null,
|
||||
coverImage = null,
|
||||
),
|
||||
)
|
||||
val state = TypeTemplatesWidgetUI.Data(
|
||||
templates = items,
|
||||
showWidget = true,
|
||||
isEditing = true,
|
||||
moreMenuItem = TemplateView.Template(
|
||||
id = "123",
|
||||
name = "Template 1",
|
||||
targetTypeId = TypeId("page"),
|
||||
targetTypeKey = TypeKey("ot-page"),
|
||||
),
|
||||
objectTypes = listOf(
|
||||
TemplateObjectTypeView.Search,
|
||||
TemplateObjectTypeView.Item(
|
||||
type = ObjectWrapper.Type(
|
||||
map = mapOf(Relations.ID to "123", Relations.NAME to "Page"),
|
||||
)
|
||||
)
|
||||
),
|
||||
viewerId = "",
|
||||
isPossibleToChangeType = true,
|
||||
isPossibleToChangeTemplate = false
|
||||
)
|
||||
TypeTemplatesWidget(
|
||||
state = state,
|
||||
onDismiss = {},
|
||||
editClick = {},
|
||||
doneClick = {},
|
||||
moreClick = {},
|
||||
scope = CoroutineScope(
|
||||
Dispatchers.Main
|
||||
),
|
||||
menuClick = {},
|
||||
action = {}
|
||||
)
|
||||
}
|
10
core-ui/src/main/res/drawable/ic_obj_settings_cover_24.xml
Normal file
10
core-ui/src/main/res/drawable/ic_obj_settings_cover_24.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M5,4H19C20.381,4 21.5,5.119 21.5,6.5V17.5L14.53,10.53C14.237,10.237 13.763,10.237 13.47,10.53L10.03,13.97C9.737,14.262 9.263,14.262 8.97,13.97L8.03,13.03C7.737,12.737 7.263,12.737 6.97,13.03L2.5,17.5V6.5C2.5,5.119 3.619,4 5,4ZM1,6.5C1,4.291 2.791,2.5 5,2.5H19C21.209,2.5 23,4.291 23,6.5V17.5C23,19.709 21.209,21.5 19,21.5H5C2.791,21.5 1,19.709 1,17.5V6.5ZM7.5,10.5C6.395,10.5 5.5,9.605 5.5,8.5C5.5,7.395 6.395,6.5 7.5,6.5C8.605,6.5 9.5,7.395 9.5,8.5C9.5,9.605 8.605,10.5 7.5,10.5Z"
|
||||
android:fillColor="@color/palette_system_green"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
|
@ -0,0 +1,17 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M12,12m-10.25,0a10.25,10.25 0,1 1,20.5 0a10.25,10.25 0,1 1,-20.5 0"
|
||||
android:strokeWidth="1.5"
|
||||
android:fillColor="@color/transparent_black"
|
||||
android:strokeColor="@color/palette_system_sky"/>
|
||||
<path
|
||||
android:pathData="M11.25,10.063h1.5v8h-1.5z"
|
||||
android:fillColor="@color/palette_system_sky"/>
|
||||
<path
|
||||
android:pathData="M12,6.063L12,6.063A1,1 0,0 1,13 7.063L13,7.063A1,1 0,0 1,12 8.063L12,8.063A1,1 0,0 1,11 7.063L11,7.063A1,1 0,0 1,12 6.063z"
|
||||
android:fillColor="@color/palette_system_sky"/>
|
||||
</vector>
|
33
core-ui/src/main/res/drawable/ic_obj_settings_fields_24.xml
Normal file
33
core-ui/src/main/res/drawable/ic_obj_settings_fields_24.xml
Normal file
|
@ -0,0 +1,33 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M6,18.5C6,19.881 4.881,21 3.5,21C2.119,21 1,19.881 1,18.5C1,17.119 2.119,16 3.5,16C4.881,16 6,17.119 6,18.5Z"
|
||||
android:fillColor="#AB50CC"/>
|
||||
<path
|
||||
android:pathData="M8,18.5H16"
|
||||
android:strokeWidth="1.5"
|
||||
android:fillColor="@color/transparent_black"
|
||||
android:strokeColor="@color/palette_system_purple"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M5.75,14.839L9.75,7.911"
|
||||
android:strokeWidth="1.5"
|
||||
android:fillColor="@color/transparent_black"
|
||||
android:strokeColor="@color/palette_system_purple"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M18.25,14.839L14.25,7.911"
|
||||
android:strokeWidth="1.5"
|
||||
android:fillColor="@color/transparent_black"
|
||||
android:strokeColor="@color/palette_system_purple"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M23,18.5C23,19.881 21.881,21 20.5,21C19.119,21 18,19.881 18,18.5C18,17.119 19.119,16 20.5,16C21.881,16 23,17.119 23,18.5Z"
|
||||
android:fillColor="@color/palette_system_purple"/>
|
||||
<path
|
||||
android:pathData="M14.5,4C14.5,5.381 13.381,6.5 12,6.5C10.619,6.5 9.5,5.381 9.5,4C9.5,2.619 10.619,1.5 12,1.5C13.381,1.5 14.5,2.619 14.5,4Z"
|
||||
android:fillColor="@color/palette_system_purple"/>
|
||||
</vector>
|
23
core-ui/src/main/res/drawable/ic_obj_settings_history_24.xml
Normal file
23
core-ui/src/main/res/drawable/ic_obj_settings_history_24.xml
Normal file
|
@ -0,0 +1,23 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M2.031,12C2.031,17.523 6.508,22 12.031,22C17.554,22 22.031,17.523 22.031,12C22.031,6.477 17.554,2 12.031,2C8.125,2 4.741,4.24 3.096,7.506"
|
||||
android:strokeWidth="1.5"
|
||||
android:fillColor="@color/transparent_black"
|
||||
android:strokeColor="@color/palette_system_pink"/>
|
||||
<path
|
||||
android:pathData="M12,6V11.906L15.063,15"
|
||||
android:strokeWidth="1.5"
|
||||
android:fillColor="@color/transparent_black"
|
||||
android:strokeColor="@color/palette_system_pink"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M2.031,2.5h1.5v5.5h-1.5z"
|
||||
android:fillColor="@color/palette_system_pink"/>
|
||||
<path
|
||||
android:pathData="M2.031,8l0,-1.5l5.5,-0l0,1.5z"
|
||||
android:fillColor="@color/palette_system_pink"/>
|
||||
</vector>
|
20
core-ui/src/main/res/drawable/ic_obj_settings_icon_24.xml
Normal file
20
core-ui/src/main/res/drawable/ic_obj_settings_icon_24.xml
Normal file
|
@ -0,0 +1,20 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M9.25,7.188C8.491,7.188 7.875,8.111 7.875,9.25C7.875,10.389 8.491,11.313 9.25,11.313C10.009,11.313 10.625,10.389 10.625,9.25C10.625,8.111 10.009,7.188 9.25,7.188ZM14.75,7.188C13.991,7.188 13.375,8.111 13.375,9.25C13.375,10.389 13.991,11.313 14.75,11.313C15.509,11.313 16.125,10.389 16.125,9.25C16.125,8.111 15.509,7.188 14.75,7.188Z"
|
||||
android:fillColor="@color/palette_system_amber_100"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M12,12m-10.25,0a10.25,10.25 0,1 1,20.5 0a10.25,10.25 0,1 1,-20.5 0"
|
||||
android:strokeWidth="1.5"
|
||||
android:fillColor="@color/transparent_black"
|
||||
android:strokeColor="@color/palette_system_amber_100"/>
|
||||
<path
|
||||
android:pathData="M17.48,13.453C17.731,13.391 17.975,13.47 18.121,13.605C18.251,13.725 18.282,13.855 18.221,14.005C17.035,16.933 14.643,18.816 12,18.816C9.367,18.816 6.984,16.948 5.793,14.042C5.731,13.891 5.762,13.761 5.895,13.639C6.046,13.501 6.295,13.421 6.55,13.484C7.929,13.83 9.798,14.141 12.005,14.113C14.224,14.113 16.101,13.795 17.48,13.453Z"
|
||||
android:strokeWidth="1.5"
|
||||
android:fillColor="@color/transparent_black"
|
||||
android:strokeColor="@color/palette_system_amber_100"/>
|
||||
</vector>
|
20
core-ui/src/main/res/drawable/ic_obj_settings_layout_24.xml
Normal file
20
core-ui/src/main/res/drawable/ic_obj_settings_layout_24.xml
Normal file
|
@ -0,0 +1,20 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M6,2.719L18,2.719A3.25,3.25 0,0 1,21.25 5.969L21.25,17.969A3.25,3.25 0,0 1,18 21.219L6,21.219A3.25,3.25 0,0 1,2.75 17.969L2.75,5.969A3.25,3.25 0,0 1,6 2.719z"
|
||||
android:strokeWidth="1.5"
|
||||
android:fillColor="@color/transparent_black"
|
||||
android:strokeColor="@color/palette_system_blue"/>
|
||||
<path
|
||||
android:pathData="M6.75,16.5L13.25,16.5A0.75,0.75 0,0 1,14 17.25L14,17.25A0.75,0.75 0,0 1,13.25 18L6.75,18A0.75,0.75 0,0 1,6 17.25L6,17.25A0.75,0.75 0,0 1,6.75 16.5z"
|
||||
android:fillColor="@color/palette_system_blue"/>
|
||||
<path
|
||||
android:pathData="M6.75,13.563L17.25,13.563A0.75,0.75 0,0 1,18 14.313L18,14.313A0.75,0.75 0,0 1,17.25 15.063L6.75,15.063A0.75,0.75 0,0 1,6 14.313L6,14.313A0.75,0.75 0,0 1,6.75 13.563z"
|
||||
android:fillColor="@color/palette_system_blue"/>
|
||||
<path
|
||||
android:pathData="M8,5.969L10,5.969A2,2 0,0 1,12 7.969L12,9.969A2,2 0,0 1,10 11.969L8,11.969A2,2 0,0 1,6 9.969L6,7.969A2,2 0,0 1,8 5.969z"
|
||||
android:fillColor="@color/palette_system_blue"/>
|
||||
</vector>
|
16
core-ui/src/main/res/drawable/ic_section_local_fields.xml
Normal file
16
core-ui/src/main/res/drawable/ic_section_local_fields.xml
Normal file
|
@ -0,0 +1,16 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="18dp"
|
||||
android:height="18dp"
|
||||
android:viewportWidth="18"
|
||||
android:viewportHeight="18">
|
||||
<path
|
||||
android:pathData="M9,0L9,0A9,9 0,0 1,18 9L18,9A9,9 0,0 1,9 18L9,18A9,9 0,0 1,0 9L0,9A9,9 0,0 1,9 0z"
|
||||
android:strokeAlpha="0.6"
|
||||
android:fillColor="@color/glyph_button"
|
||||
android:fillAlpha="0.6"/>
|
||||
<path
|
||||
android:pathData="M8.023,11.281H9.344V11.193C9.362,10.131 9.667,9.656 10.452,9.171C11.278,8.672 11.786,7.971 11.786,6.932C11.786,5.432 10.65,4.416 8.956,4.416C7.4,4.416 6.154,5.325 6.094,6.987H7.497C7.553,6.009 8.25,5.579 8.956,5.579C9.741,5.579 10.378,6.101 10.378,6.918C10.378,7.606 9.949,8.091 9.399,8.432C8.54,8.959 8.033,9.48 8.023,11.193V11.281ZM8.721,14.087C9.224,14.087 9.644,13.677 9.644,13.164C9.644,12.661 9.224,12.246 8.721,12.246C8.213,12.246 7.797,12.661 7.797,13.164C7.797,13.677 8.213,14.087 8.721,14.087Z"
|
||||
android:strokeAlpha="0.6"
|
||||
android:fillColor="@color/text_button_label"
|
||||
android:fillAlpha="0.6"/>
|
||||
</vector>
|
10
core-ui/src/main/res/drawable/ic_settings_24.xml
Normal file
10
core-ui/src/main/res/drawable/ic_settings_24.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M16.5,7C16.5,7.828 15.828,8.5 15,8.5C14.172,8.5 13.5,7.828 13.5,7C13.5,6.172 14.172,5.5 15,5.5C15.828,5.5 16.5,6.172 16.5,7ZM20.25,6.25H17.906C17.573,4.956 16.398,4 15,4C13.602,4 12.427,4.956 12.094,6.25H3.75C3.336,6.25 3,6.586 3,7C3,7.414 3.336,7.75 3.75,7.75H12.094C12.427,9.044 13.602,10 15,10C16.398,10 17.573,9.044 17.906,7.75H20.25C20.664,7.75 21,7.414 21,7C21,6.586 20.664,6.25 20.25,6.25ZM7.5,17C7.5,17.828 8.172,18.5 9,18.5C9.828,18.5 10.5,17.828 10.5,17C10.5,16.172 9.828,15.5 9,15.5C8.172,15.5 7.5,16.172 7.5,17ZM6.095,17.75C6.428,19.044 7.602,20 9,20C10.398,20 11.573,19.044 11.906,17.75H20.25C20.664,17.75 21,17.414 21,17C21,16.586 20.664,16.25 20.25,16.25H11.906C11.573,14.956 10.398,14 9,14C7.602,14 6.428,14.956 6.095,16.25H3.75C3.336,16.25 3,16.586 3,17C3,17.414 3.336,17.75 3.75,17.75H6.095Z"
|
||||
android:fillColor="@color/glyph_active"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
20
core-ui/src/main/res/drawable/ic_type_layout_basic_icon.xml
Normal file
20
core-ui/src/main/res/drawable/ic_type_layout_basic_icon.xml
Normal file
|
@ -0,0 +1,20 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M9,6.75C8.172,6.75 7.5,7.757 7.5,9C7.5,10.243 8.172,11.25 9,11.25C9.828,11.25 10.5,10.243 10.5,9C10.5,7.757 9.828,6.75 9,6.75ZM15,6.75C14.172,6.75 13.5,7.757 13.5,9C13.5,10.243 14.172,11.25 15,11.25C15.828,11.25 16.5,10.243 16.5,9C16.5,7.757 15.828,6.75 15,6.75Z"
|
||||
android:fillColor="@color/glyph_active"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M12,12m-11.25,0a11.25,11.25 0,1 1,22.5 0a11.25,11.25 0,1 1,-22.5 0"
|
||||
android:strokeWidth="1.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="@color/glyph_active"/>
|
||||
<path
|
||||
android:pathData="M18.178,13.464C18.426,13.4 18.668,13.476 18.816,13.611C18.949,13.733 18.984,13.869 18.924,14.025C17.652,17.342 14.975,19.503 12,19.503C9.036,19.503 6.368,17.359 5.089,14.062C5.029,13.906 5.064,13.768 5.2,13.645C5.352,13.508 5.599,13.431 5.85,13.496C7.368,13.894 9.484,14.268 12.005,14.236C14.537,14.236 16.661,13.856 18.178,13.464Z"
|
||||
android:strokeWidth="1.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="@color/glyph_active"/>
|
||||
</vector>
|
14
core-ui/src/main/res/drawable/ic_type_layout_todo_icon.xml
Normal file
14
core-ui/src/main/res/drawable/ic_type_layout_todo_icon.xml
Normal file
|
@ -0,0 +1,14 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="12dp"
|
||||
android:height="12dp"
|
||||
android:viewportWidth="12"
|
||||
android:viewportHeight="12">
|
||||
<path
|
||||
android:pathData="M3,0L9,0A3,3 0,0 1,12 3L12,9A3,3 0,0 1,9 12L3,12A3,3 0,0 1,0 9L0,3A3,3 0,0 1,3 0z"
|
||||
android:fillColor="@color/glyph_active"/>
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M3,6L5.5,8.5L8.499,3"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="@color/glyph_white"/>
|
||||
</vector>
|
|
@ -0,0 +1,44 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivIcon"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginTop="14dp"
|
||||
android:layout_marginBottom="14dp"
|
||||
android:layout_marginStart="20dp"
|
||||
android:src="@drawable/ic_obj_settings_description_24" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/textContainer"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvTitle"
|
||||
style="@style/TextView.ContentStyle.PreviewTitles.1.Regular"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/description"
|
||||
tools:text="Icon" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/descriptionAction"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
style="@style/TextView.ContentStyle.PreviewTitles.1.Regular"
|
||||
android:textColor="@color/text_secondary"
|
||||
android:layout_marginEnd="20dp"
|
||||
/>
|
||||
|
||||
</merge>
|
|
@ -1,13 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--typography, buttons 05.04-->
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivIcon"
|
||||
android:layout_width="44dp"
|
||||
android:layout_height="44dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginTop="14dp"
|
||||
android:layout_marginBottom="14dp"
|
||||
android:layout_marginStart="20dp"
|
||||
tools:src="@drawable/ic_object_menu_icon" />
|
||||
|
||||
<LinearLayout
|
||||
|
@ -22,19 +23,11 @@
|
|||
|
||||
<TextView
|
||||
android:id="@+id/tvTitle"
|
||||
style="@style/TextView.UXStyle.Titles.2.Medium"
|
||||
style="@style/TextView.ContentStyle.PreviewTitles.1.Regular"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
tools:text="Icon" />
|
||||
|
||||
<TextView
|
||||
style="@style/TextView.UXStyle.Captions.1.Regular"
|
||||
android:textColor="@color/text_secondary"
|
||||
android:id="@+id/tvSubtitle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
tools:text="Emoji or image for object" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
|
@ -42,7 +35,7 @@
|
|||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:src="@drawable/ic_object_menu_arrow_forward" />
|
||||
android:layout_marginEnd="12dp"
|
||||
android:src="@drawable/ic_arrow_forward_24" />
|
||||
|
||||
</merge>
|
|
@ -41,6 +41,9 @@
|
|||
<color name="dashboard_card_background">#99000000</color>
|
||||
<color name="dashboard_background">#99252525</color>
|
||||
|
||||
<!-- Background -->
|
||||
<color name="widget_background">#BF252525</color>
|
||||
|
||||
<!-- Colors at Figma https://www.figma.com/file/vgXV7x2v20vJajc7clYJ7a/Typography-and-Colors%2FMobile?node-id=1031%3A115 -->
|
||||
<color name="palette_dark_default">#F3F2EC</color>
|
||||
<color name="palette_dark_yellow">#E0D56C</color>
|
||||
|
|
|
@ -79,6 +79,9 @@
|
|||
<color name="dashboard_card_background">#FFFFFF</color>
|
||||
<color name="dashboard_background">#99F5F5F5</color>
|
||||
|
||||
<!-- Background -->
|
||||
<color name="widget_background">#FFFFFF</color>
|
||||
|
||||
<!-- Colors at Figma https://www.figma.com/file/vgXV7x2v20vJajc7clYJ7a/Typography-and-Colors%2FMobile?node-id=1031%3A160 -->
|
||||
|
||||
<color name="palette_dark_default">#252525</color>
|
||||
|
|
|
@ -2,6 +2,7 @@ package com.anytypeio.anytype.data.auth.repo.block
|
|||
|
||||
import com.anytypeio.anytype.core_models.Block
|
||||
import com.anytypeio.anytype.core_models.Command
|
||||
import com.anytypeio.anytype.core_models.Command.ObjectTypeConflictingFields
|
||||
import com.anytypeio.anytype.core_models.Config
|
||||
import com.anytypeio.anytype.core_models.CreateBlockLinkWithObjectResult
|
||||
import com.anytypeio.anytype.core_models.CreateObjectResult
|
||||
|
@ -307,12 +308,18 @@ class BlockDataRepository(
|
|||
|
||||
override suspend fun createSet(
|
||||
space: Id,
|
||||
objectType: String?
|
||||
objectType: String?,
|
||||
details: Struct?
|
||||
): CreateObjectSet.Response {
|
||||
val result = remote.createSet(space = space, objectType = objectType)
|
||||
val result = remote.createSet(
|
||||
space = space,
|
||||
objectType = objectType,
|
||||
details = details
|
||||
)
|
||||
return CreateObjectSet.Response(
|
||||
target = result.targetId,
|
||||
payload = result.payload
|
||||
target = result.objectId,
|
||||
payload = result.payload,
|
||||
details = result.details
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1101,4 +1108,16 @@ class BlockDataRepository(
|
|||
override suspend fun setDeviceNetworkState(type: DeviceNetworkType) {
|
||||
remote.setDeviceNetworkState(type)
|
||||
}
|
||||
|
||||
override suspend fun objectTypeListConflictingRelations(command: ObjectTypeConflictingFields): List<Id> {
|
||||
return remote.objectTypeListConflictingRelations(command)
|
||||
}
|
||||
|
||||
override suspend fun objectTypeSetRecommendedHeaderFields(command: Command.ObjectTypeSetRecommendedHeaderFields) {
|
||||
remote.objectTypeSetRecommendedHeaderFields(command)
|
||||
}
|
||||
|
||||
override suspend fun objectTypeSetRecommendedFields(command: Command.ObjectTypeSetRecommendedFields) {
|
||||
remote.objectTypeSetRecommendedFields(command)
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ package com.anytypeio.anytype.data.auth.repo.block
|
|||
|
||||
import com.anytypeio.anytype.core_models.Block
|
||||
import com.anytypeio.anytype.core_models.Command
|
||||
import com.anytypeio.anytype.core_models.Command.ObjectTypeConflictingFields
|
||||
import com.anytypeio.anytype.core_models.Config
|
||||
import com.anytypeio.anytype.core_models.CreateBlockLinkWithObjectResult
|
||||
import com.anytypeio.anytype.core_models.CreateObjectResult
|
||||
|
@ -106,7 +107,8 @@ interface BlockRemote {
|
|||
|
||||
suspend fun createSet(
|
||||
space: Id,
|
||||
objectType: String?
|
||||
objectType: String?,
|
||||
details: Struct?
|
||||
): Response.Set.Create
|
||||
|
||||
suspend fun setDataViewViewerPosition(
|
||||
|
@ -468,4 +470,9 @@ interface BlockRemote {
|
|||
suspend fun debugAccountSelectTrace(dir: String): String
|
||||
|
||||
suspend fun setDeviceNetworkState(type: DeviceNetworkType)
|
||||
|
||||
suspend fun objectTypeListConflictingRelations(command: ObjectTypeConflictingFields): List<Id>
|
||||
|
||||
suspend fun objectTypeSetRecommendedHeaderFields(command: Command.ObjectTypeSetRecommendedHeaderFields)
|
||||
suspend fun objectTypeSetRecommendedFields(command: Command.ObjectTypeSetRecommendedFields)
|
||||
}
|
|
@ -2,6 +2,7 @@ package com.anytypeio.anytype.domain.block.interactor.sets
|
|||
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.Payload
|
||||
import com.anytypeio.anytype.core_models.Struct
|
||||
import com.anytypeio.anytype.domain.base.BaseUseCase
|
||||
import com.anytypeio.anytype.domain.block.repo.BlockRepository
|
||||
|
||||
|
@ -10,12 +11,17 @@ class CreateObjectSet(
|
|||
) : BaseUseCase<CreateObjectSet.Response, CreateObjectSet.Params>() {
|
||||
|
||||
override suspend fun run(params: Params) = safe {
|
||||
repo.createSet(space = params.space, objectType = params.type)
|
||||
repo.createSet(
|
||||
space = params.space,
|
||||
objectType = params.type,
|
||||
details = params.details
|
||||
)
|
||||
}
|
||||
|
||||
data class Params(
|
||||
val space: Id,
|
||||
val type: Id? = null
|
||||
val type: Id? = null,
|
||||
val details: Struct? = null
|
||||
)
|
||||
|
||||
/**
|
||||
|
@ -23,6 +29,7 @@ class CreateObjectSet(
|
|||
*/
|
||||
data class Response(
|
||||
val target: Id,
|
||||
val payload: Payload
|
||||
val payload: Payload,
|
||||
val details: Struct
|
||||
)
|
||||
}
|
|
@ -2,6 +2,7 @@ package com.anytypeio.anytype.domain.block.repo
|
|||
|
||||
import com.anytypeio.anytype.core_models.Block
|
||||
import com.anytypeio.anytype.core_models.Command
|
||||
import com.anytypeio.anytype.core_models.Command.ObjectTypeConflictingFields
|
||||
import com.anytypeio.anytype.core_models.Config
|
||||
import com.anytypeio.anytype.core_models.CreateBlockLinkWithObjectResult
|
||||
import com.anytypeio.anytype.core_models.CreateObjectResult
|
||||
|
@ -159,7 +160,8 @@ interface BlockRepository {
|
|||
|
||||
suspend fun createSet(
|
||||
space: Id,
|
||||
objectType: String? = null
|
||||
objectType: String? = null,
|
||||
details: Struct? = null
|
||||
): CreateObjectSet.Response
|
||||
|
||||
suspend fun addRelationToDataView(ctx: Id, dv: Id, relation: Key): Payload
|
||||
|
@ -511,4 +513,9 @@ interface BlockRepository {
|
|||
suspend fun objectDateByTimestamp(command: Command.ObjectDateByTimestamp): Struct?
|
||||
|
||||
suspend fun setDeviceNetworkState(type: DeviceNetworkType)
|
||||
|
||||
suspend fun objectTypeListConflictingRelations(command: ObjectTypeConflictingFields): List<Id>
|
||||
|
||||
suspend fun objectTypeSetRecommendedHeaderFields(command: Command.ObjectTypeSetRecommendedHeaderFields)
|
||||
suspend fun objectTypeSetRecommendedFields(command: Command.ObjectTypeSetRecommendedFields)
|
||||
}
|
|
@ -8,6 +8,7 @@ import com.anytypeio.anytype.domain.block.repo.BlockRepository
|
|||
import com.anytypeio.anytype.domain.event.interactor.EventChannel
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.emitAll
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.scan
|
||||
|
@ -17,17 +18,38 @@ class ObjectWatcher @Inject constructor(
|
|||
private val events: EventChannel,
|
||||
private val reducer: Reducer
|
||||
) {
|
||||
/**
|
||||
* Watches an object and returns a flow of ObjectView.
|
||||
* If opening the object or observing events fails, the flow will throw an exception.
|
||||
*/
|
||||
fun watch(target: Id, space: SpaceId): Flow<ObjectView> = flow {
|
||||
emitAll(
|
||||
events.observeEvents(context = target).scan(
|
||||
initial = repo.openObject(id = target, space = space),
|
||||
operation = reducer
|
||||
)
|
||||
)
|
||||
// Attempt to open the object. If it fails, throw.
|
||||
val initialObjectView = try {
|
||||
repo.openObject(id = target, space = space)
|
||||
} catch (e: Exception) {
|
||||
throw RuntimeException("Failed to open object with id=$target in space=$space", e)
|
||||
}
|
||||
|
||||
// Start observing events. If events flow fails at any point, it will throw.
|
||||
val eventFlow = events.observeEvents(context = target)
|
||||
.catch { e -> throw RuntimeException("Failed to observe events for object=$target", e) }
|
||||
|
||||
emitAll(eventFlow.scan(initialObjectView, reducer))
|
||||
}.catch { e ->
|
||||
// Optional: If you want a centralized place to rethrow or log, you can do it here.
|
||||
throw e
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops watching an object.
|
||||
* If closing the page fails, it will throw an exception.
|
||||
*/
|
||||
suspend fun unwatch(target: Id, space: SpaceId) {
|
||||
repo.closePage(id = target, space = space)
|
||||
try {
|
||||
repo.closePage(id = target, space = space)
|
||||
} catch (e: Exception) {
|
||||
throw RuntimeException("Failed to unwatch object with id=$target in space=$space", e)
|
||||
}
|
||||
}
|
||||
|
||||
interface Reducer : (ObjectView, List<Event>) -> ObjectView
|
||||
|
|
|
@ -6,6 +6,10 @@ import com.anytypeio.anytype.core_models.ObjectWrapper
|
|||
import com.anytypeio.anytype.core_models.Struct
|
||||
import com.anytypeio.anytype.domain.`object`.amend
|
||||
import com.anytypeio.anytype.domain.`object`.unset
|
||||
import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes.TrackedEvent
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
|
||||
|
@ -20,6 +24,13 @@ interface StoreOfObjectTypes {
|
|||
suspend fun set(target: Id, data: Struct)
|
||||
suspend fun remove(target: Id)
|
||||
suspend fun clear()
|
||||
|
||||
fun trackChanges() : Flow<TrackedEvent>
|
||||
|
||||
sealed class TrackedEvent {
|
||||
object Init : TrackedEvent()
|
||||
object Change: TrackedEvent()
|
||||
}
|
||||
}
|
||||
|
||||
class DefaultStoreOfObjectTypes : StoreOfObjectTypes {
|
||||
|
@ -27,6 +38,8 @@ class DefaultStoreOfObjectTypes : StoreOfObjectTypes {
|
|||
private val mutex = Mutex()
|
||||
private val store = mutableMapOf<Id, ObjectWrapper.Type>()
|
||||
|
||||
private val updates = MutableSharedFlow<TrackedEvent>()
|
||||
|
||||
override val size: Int get() = store.size
|
||||
|
||||
override suspend fun get(id: Id): ObjectWrapper.Type? = mutex.withLock {
|
||||
|
@ -50,6 +63,7 @@ class DefaultStoreOfObjectTypes : StoreOfObjectTypes {
|
|||
store[o.id] = current.amend(o.map)
|
||||
}
|
||||
}
|
||||
updates.emit(TrackedEvent.Change)
|
||||
}
|
||||
|
||||
override suspend fun amend(target: Id, diff: Map<Id, Any?>): Unit = mutex.withLock {
|
||||
|
@ -59,6 +73,7 @@ class DefaultStoreOfObjectTypes : StoreOfObjectTypes {
|
|||
} else {
|
||||
store[target] = ObjectWrapper.Type(diff)
|
||||
}
|
||||
updates.emit(TrackedEvent.Change)
|
||||
}
|
||||
|
||||
override suspend fun set(
|
||||
|
@ -66,6 +81,7 @@ class DefaultStoreOfObjectTypes : StoreOfObjectTypes {
|
|||
data: Map<String, Any?>
|
||||
): Unit = mutex.withLock {
|
||||
store[target] = ObjectWrapper.Type(data)
|
||||
updates.emit(TrackedEvent.Change)
|
||||
}
|
||||
|
||||
override suspend fun unset(
|
||||
|
@ -76,6 +92,7 @@ class DefaultStoreOfObjectTypes : StoreOfObjectTypes {
|
|||
if (current != null) {
|
||||
store[target] = current.unset(keys)
|
||||
}
|
||||
updates.emit(TrackedEvent.Change)
|
||||
}
|
||||
|
||||
override suspend fun remove(target: Id) : Unit = mutex.withLock {
|
||||
|
@ -83,9 +100,14 @@ class DefaultStoreOfObjectTypes : StoreOfObjectTypes {
|
|||
if (current != null) {
|
||||
store.remove(target)
|
||||
}
|
||||
updates.emit(TrackedEvent.Change)
|
||||
}
|
||||
|
||||
override suspend fun clear(): Unit = mutex.withLock {
|
||||
store.clear()
|
||||
}
|
||||
|
||||
override fun trackChanges(): Flow<TrackedEvent> = updates.onStart {
|
||||
emit(TrackedEvent.Init)
|
||||
}
|
||||
}
|
|
@ -132,4 +132,10 @@ class DefaultStoreOfRelations : StoreOfRelations {
|
|||
override suspend fun observe(): Flow<Map<Id, ObjectWrapper.Relation>> {
|
||||
return trackChanges().map { store }
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun StoreOfRelations.getValidRelations(ids: List<Id>): List<ObjectWrapper.Relation> {
|
||||
return ids.mapNotNull { id ->
|
||||
getById(id)?.takeIf { it.isValidToUse }
|
||||
}
|
||||
}
|
|
@ -1,10 +1,10 @@
|
|||
package com.anytypeio.anytype.domain.primitives
|
||||
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.Key
|
||||
import com.anytypeio.anytype.core_models.MAX_SNIPPET_SIZE
|
||||
import com.anytypeio.anytype.core_models.ObjectType
|
||||
import com.anytypeio.anytype.core_models.ObjectTypeIds
|
||||
import com.anytypeio.anytype.core_models.ObjectView
|
||||
import com.anytypeio.anytype.core_models.ObjectWrapper
|
||||
import com.anytypeio.anytype.core_models.Relations
|
||||
import com.anytypeio.anytype.core_models.RelativeDate
|
||||
|
@ -12,6 +12,7 @@ import com.anytypeio.anytype.core_models.SupportedLayouts
|
|||
import com.anytypeio.anytype.core_models.TimeInSeconds
|
||||
import com.anytypeio.anytype.core_models.primitives.Field
|
||||
import com.anytypeio.anytype.core_models.primitives.FieldDateValue
|
||||
import com.anytypeio.anytype.core_models.primitives.ParsedFields
|
||||
import com.anytypeio.anytype.core_models.primitives.SpaceId
|
||||
import com.anytypeio.anytype.core_models.primitives.TimestampInSeconds
|
||||
import com.anytypeio.anytype.core_models.primitives.Value
|
||||
|
@ -19,9 +20,12 @@ import com.anytypeio.anytype.domain.base.fold
|
|||
import com.anytypeio.anytype.domain.debugging.Logger
|
||||
import com.anytypeio.anytype.domain.misc.DateProvider
|
||||
import com.anytypeio.anytype.domain.objects.GetDateObjectByTimestamp
|
||||
import com.anytypeio.anytype.domain.objects.StoreOfRelations
|
||||
import com.anytypeio.anytype.domain.objects.getValidRelations
|
||||
import com.anytypeio.anytype.domain.resources.StringResourceProvider
|
||||
import javax.inject.Inject
|
||||
import kotlin.collections.contains
|
||||
import kotlin.collections.plus
|
||||
|
||||
interface FieldParser {
|
||||
fun toDate(any: Any?): Field.Date?
|
||||
|
@ -31,11 +35,29 @@ interface FieldParser {
|
|||
actionSuccess: suspend (ObjectWrapper.Basic) -> Unit,
|
||||
actionFailure: suspend (Throwable) -> Unit
|
||||
)
|
||||
|
||||
fun getObjectName(objectWrapper: ObjectWrapper.Basic): String
|
||||
fun getObjectName(objectWrapper: ObjectWrapper.Type): String
|
||||
fun getObjectTypeIdAndName(
|
||||
objectWrapper: ObjectWrapper.Basic,
|
||||
types: List<ObjectWrapper.Type>
|
||||
): Pair<Id?, String?>
|
||||
|
||||
suspend fun getObjectParsedFields(
|
||||
objectType: ObjectWrapper.Type,
|
||||
objFieldKeys: List<Key>,
|
||||
storeOfRelations: StoreOfRelations
|
||||
): ParsedFields
|
||||
|
||||
suspend fun getObjectTypeParsedFields(
|
||||
objectType: ObjectWrapper.Type,
|
||||
objectTypeConflictingFieldsIds: List<Id>,
|
||||
storeOfRelations: StoreOfRelations
|
||||
): ParsedFields
|
||||
|
||||
fun isFieldEditable(relation: ObjectWrapper.Relation): Boolean
|
||||
|
||||
fun isFieldCanBeDeletedFromType(field: ObjectWrapper.Relation): Boolean
|
||||
}
|
||||
|
||||
class FieldParserImpl @Inject constructor(
|
||||
|
@ -118,13 +140,16 @@ class FieldParserImpl @Inject constructor(
|
|||
val result = when (objectWrapper.layout) {
|
||||
ObjectType.Layout.DATE -> {
|
||||
val relativeDate = dateProvider.calculateRelativeDates(
|
||||
dateInSeconds = objectWrapper.getSingleValue<Double>(Relations.TIMESTAMP)?.toLong()
|
||||
dateInSeconds = objectWrapper.getSingleValue<Double>(Relations.TIMESTAMP)
|
||||
?.toLong()
|
||||
)
|
||||
stringResourceProvider.getRelativeDateName(relativeDate)
|
||||
}
|
||||
|
||||
ObjectType.Layout.NOTE -> {
|
||||
objectWrapper.snippet?.replace("\n", " ")?.take(MAX_SNIPPET_SIZE)
|
||||
}
|
||||
|
||||
in SupportedLayouts.fileLayouts -> {
|
||||
val fileName = if (objectWrapper.name.isNullOrBlank()) {
|
||||
stringResourceProvider.getUntitledObjectTitle()
|
||||
|
@ -137,6 +162,7 @@ class FieldParserImpl @Inject constructor(
|
|||
else -> "$fileName.${objectWrapper.fileExt}"
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
objectWrapper.name
|
||||
}
|
||||
|
@ -148,6 +174,15 @@ class FieldParserImpl @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun getObjectName(objectWrapper: ObjectWrapper.Type): String {
|
||||
val name = objectWrapper.name
|
||||
return if (name.isNullOrBlank()) {
|
||||
stringResourceProvider.getUntitledObjectTitle()
|
||||
} else {
|
||||
name
|
||||
}
|
||||
}
|
||||
|
||||
override fun getObjectTypeIdAndName(
|
||||
objectWrapper: ObjectWrapper.Basic,
|
||||
types: List<ObjectWrapper.Type>
|
||||
|
@ -164,4 +199,99 @@ class FieldParserImpl @Inject constructor(
|
|||
}
|
||||
}
|
||||
//endregion
|
||||
|
||||
//region Parsed fields
|
||||
|
||||
// Consolidated function to build ParsedFields.
|
||||
private suspend fun getParsedFields(
|
||||
objType: ObjectWrapper.Type,
|
||||
localFieldIds: Collection<Id>,
|
||||
storeOfRelations: StoreOfRelations
|
||||
): ParsedFields {
|
||||
|
||||
val headerFields = storeOfRelations.getValidRelations(
|
||||
ids = objType.recommendedFeaturedRelations
|
||||
)
|
||||
val sidebarFields = storeOfRelations.getValidRelations(
|
||||
ids = objType.recommendedRelations
|
||||
)
|
||||
val hiddenFields = storeOfRelations.getValidRelations(
|
||||
ids = objType.recommendedHiddenRelations
|
||||
)
|
||||
val fileFields = storeOfRelations.getValidRelations(
|
||||
ids = objType.recommendedFileRelations
|
||||
)
|
||||
|
||||
// Combine IDs from all recommended relations.
|
||||
val existingIds = (headerFields + sidebarFields + hiddenFields + fileFields)
|
||||
.map { it.id }
|
||||
.toSet()
|
||||
|
||||
// Filter out fields already present in the recommended groups.
|
||||
val allLocalFields = storeOfRelations.getValidRelations(
|
||||
ids = localFieldIds
|
||||
.filter { it !in existingIds }
|
||||
.toList()
|
||||
)
|
||||
|
||||
// Partition local fields into system and non-system fields.
|
||||
val (localSystemFields, localFieldsWithoutSystem) = allLocalFields.partition {
|
||||
Relations.systemRelationKeys.contains(it.key)
|
||||
}
|
||||
|
||||
return ParsedFields(
|
||||
header = headerFields,
|
||||
sidebar = sidebarFields,
|
||||
hidden = hiddenFields,
|
||||
localWithoutSystem = localFieldsWithoutSystem,
|
||||
localSystem = localSystemFields,
|
||||
file = fileFields
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getObjectParsedFields(
|
||||
objectType: ObjectWrapper.Type,
|
||||
objFieldKeys: List<Key>,
|
||||
storeOfRelations: StoreOfRelations
|
||||
): ParsedFields {
|
||||
val localFieldIds = storeOfRelations.getByKeys(
|
||||
keys = objFieldKeys
|
||||
).mapNotNull {
|
||||
if (it.isValidToUse) {
|
||||
it.id
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
return getParsedFields(
|
||||
objType = objectType,
|
||||
localFieldIds = localFieldIds,
|
||||
storeOfRelations = storeOfRelations
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getObjectTypeParsedFields(
|
||||
objectType: ObjectWrapper.Type,
|
||||
objectTypeConflictingFieldsIds: List<Id>,
|
||||
storeOfRelations: StoreOfRelations
|
||||
): ParsedFields {
|
||||
return getParsedFields(
|
||||
objType = objectType,
|
||||
localFieldIds = objectTypeConflictingFieldsIds,
|
||||
storeOfRelations = storeOfRelations
|
||||
)
|
||||
}
|
||||
|
||||
override fun isFieldEditable(relation: ObjectWrapper.Relation): Boolean {
|
||||
return !(relation.isReadOnly == true ||
|
||||
relation.isHidden == true ||
|
||||
relation.isArchived == true ||
|
||||
relation.isDeleted == true ||
|
||||
Relations.systemRelationKeys.contains(relation.key))
|
||||
}
|
||||
|
||||
override fun isFieldCanBeDeletedFromType(field: ObjectWrapper.Relation): Boolean {
|
||||
return !(field.isHidden == true || Relations.systemRelationKeys.contains(field.key))
|
||||
}
|
||||
//endregion
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package com.anytypeio.anytype.domain.primitives
|
||||
|
||||
import com.anytypeio.anytype.core_models.Command.ObjectTypeConflictingFields
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
|
||||
import com.anytypeio.anytype.domain.base.ResultInteractor
|
||||
import com.anytypeio.anytype.domain.block.repo.BlockRepository
|
||||
import javax.inject.Inject
|
||||
|
||||
class GetObjectTypeConflictingFields @Inject constructor(
|
||||
private val repo: BlockRepository,
|
||||
dispatchers: AppCoroutineDispatchers
|
||||
) : ResultInteractor<GetObjectTypeConflictingFields.Params, List<Id>>(dispatchers.io) {
|
||||
|
||||
override suspend fun doWork(params: Params): List<Id> {
|
||||
val command = ObjectTypeConflictingFields(
|
||||
spaceId = params.spaceId,
|
||||
objectTypeId = params.objectTypeId
|
||||
)
|
||||
return repo.objectTypeListConflictingRelations(command = command)
|
||||
}
|
||||
|
||||
data class Params(
|
||||
val spaceId: String,
|
||||
val objectTypeId: String
|
||||
)
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package com.anytypeio.anytype.domain.primitives
|
||||
|
||||
import com.anytypeio.anytype.core_models.Command
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
|
||||
import com.anytypeio.anytype.domain.base.ResultInteractor
|
||||
import com.anytypeio.anytype.domain.block.repo.BlockRepository
|
||||
import com.anytypeio.anytype.domain.primitives.SetObjectTypeHeaderRecommendedFields.Params
|
||||
import javax.inject.Inject
|
||||
|
||||
class SetObjectTypeHeaderRecommendedFields @Inject constructor(
|
||||
private val repo: BlockRepository,
|
||||
dispatchers: AppCoroutineDispatchers
|
||||
) : ResultInteractor<Params, Unit>(dispatchers.io) {
|
||||
|
||||
override suspend fun doWork(params: Params) {
|
||||
val command = Command.ObjectTypeSetRecommendedHeaderFields(
|
||||
objectTypeId = params.objectTypeId,
|
||||
fields = params.fields
|
||||
)
|
||||
return repo.objectTypeSetRecommendedHeaderFields(command = command)
|
||||
}
|
||||
|
||||
data class Params(
|
||||
val objectTypeId: String,
|
||||
val fields: List<Id>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package com.anytypeio.anytype.domain.primitives
|
||||
|
||||
import com.anytypeio.anytype.core_models.Command
|
||||
import com.anytypeio.anytype.core_models.Command.ObjectTypeConflictingFields
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
|
||||
import com.anytypeio.anytype.domain.base.ResultInteractor
|
||||
import com.anytypeio.anytype.domain.block.repo.BlockRepository
|
||||
import com.anytypeio.anytype.domain.primitives.SetObjectTypeRecommendedFields.Params
|
||||
import javax.inject.Inject
|
||||
|
||||
class SetObjectTypeRecommendedFields @Inject constructor(
|
||||
private val repo: BlockRepository,
|
||||
dispatchers: AppCoroutineDispatchers
|
||||
) : ResultInteractor<Params, Unit>(dispatchers.io) {
|
||||
|
||||
override suspend fun doWork(params: Params) {
|
||||
val command = Command.ObjectTypeSetRecommendedFields(
|
||||
objectTypeId = params.objectTypeId,
|
||||
fields = params.fields
|
||||
)
|
||||
return repo.objectTypeSetRecommendedFields(command = command)
|
||||
}
|
||||
|
||||
data class Params(
|
||||
val objectTypeId: String,
|
||||
val fields: List<Id>
|
||||
)
|
||||
}
|
|
@ -6,4 +6,5 @@ interface StringResourceProvider {
|
|||
fun getRelativeDateName(relativeDate: RelativeDate): String
|
||||
fun getDeletedObjectTitle(): String
|
||||
fun getUntitledObjectTitle(): String
|
||||
fun getSetOfObjectsTitle(): String
|
||||
}
|
|
@ -114,8 +114,14 @@ class ObjectTypesSubscriptionManager (
|
|||
Relations.DEFAULT_TEMPLATE_ID,
|
||||
Relations.SPACE_ID,
|
||||
Relations.UNIQUE_KEY,
|
||||
Relations.RESTRICTIONS
|
||||
),
|
||||
Relations.RESTRICTIONS,
|
||||
Relations.TARGET_SPACE_ID,
|
||||
Relations.TYPE,
|
||||
Relations.RECOMMENDED_RELATIONS,
|
||||
Relations.RECOMMENDED_FEATURED_RELATIONS,
|
||||
Relations.RECOMMENDED_HIDDEN_RELATIONS,
|
||||
Relations.RECOMMENDED_FILE_RELATIONS,
|
||||
),
|
||||
ignoreWorkspace = true
|
||||
)
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@ import com.anytypeio.anytype.core_models.RelationFormat
|
|||
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.primitives.RelationKey
|
||||
import com.anytypeio.anytype.core_models.primitives.SpaceId
|
||||
import com.anytypeio.anytype.core_models.restrictions.ObjectRestriction
|
||||
import com.anytypeio.anytype.domain.all_content.RestoreAllContentState
|
||||
|
@ -20,6 +19,8 @@ import com.anytypeio.anytype.domain.misc.UrlBuilder
|
|||
import com.anytypeio.anytype.domain.primitives.FieldParser
|
||||
import com.anytypeio.anytype.feature_allcontent.presentation.AllContentViewModel.Companion.DEFAULT_INITIAL_TAB
|
||||
import com.anytypeio.anytype.presentation.mapper.objectIcon
|
||||
import com.anytypeio.anytype.presentation.objects.MenuSortsItem
|
||||
import com.anytypeio.anytype.presentation.objects.ObjectsListSort
|
||||
import com.anytypeio.anytype.presentation.objects.ObjectIcon
|
||||
import com.anytypeio.anytype.presentation.objects.getDescriptionOrSnippet
|
||||
import com.anytypeio.anytype.presentation.objects.getProperType
|
||||
|
@ -41,41 +42,6 @@ sealed class AllContentMenuMode {
|
|||
override val isSelected: Boolean = false
|
||||
) : AllContentMenuMode()
|
||||
}
|
||||
|
||||
sealed class AllContentSort {
|
||||
abstract val relationKey: RelationKey
|
||||
abstract val sortType: DVSortType
|
||||
abstract val canGroupByDate: Boolean
|
||||
abstract val isSelected: Boolean
|
||||
|
||||
data class ByName(
|
||||
override val relationKey: RelationKey = RelationKey(Relations.NAME),
|
||||
override val sortType: DVSortType = DVSortType.ASC,
|
||||
override val canGroupByDate: Boolean = false,
|
||||
override val isSelected: Boolean = false
|
||||
) : AllContentSort()
|
||||
|
||||
data class ByDateUpdated(
|
||||
override val relationKey: RelationKey = RelationKey(Relations.LAST_MODIFIED_DATE),
|
||||
override val sortType: DVSortType = DVSortType.DESC,
|
||||
override val canGroupByDate: Boolean = true,
|
||||
override val isSelected: Boolean = false
|
||||
) : AllContentSort()
|
||||
|
||||
data class ByDateCreated(
|
||||
override val relationKey: RelationKey = RelationKey(Relations.CREATED_DATE),
|
||||
override val sortType: DVSortType = DVSortType.DESC,
|
||||
override val canGroupByDate: Boolean = true,
|
||||
override val isSelected: Boolean = false
|
||||
) : AllContentSort()
|
||||
|
||||
data class ByDateUsed(
|
||||
override val relationKey: RelationKey = RelationKey(Relations.LAST_USED_DATE),
|
||||
override val sortType: DVSortType = DVSortType.DESC,
|
||||
override val canGroupByDate: Boolean = false,
|
||||
override val isSelected: Boolean = false
|
||||
) : AllContentSort()
|
||||
}
|
||||
//endregion
|
||||
|
||||
//region VIEW STATES
|
||||
|
@ -190,18 +156,6 @@ sealed class UiMenuState {
|
|||
val showBin: Boolean = true
|
||||
) : UiMenuState()
|
||||
}
|
||||
|
||||
|
||||
sealed class MenuSortsItem {
|
||||
data class Container(val sort: AllContentSort) : MenuSortsItem()
|
||||
data class Sort(val sort: AllContentSort) : MenuSortsItem()
|
||||
data object Spacer : MenuSortsItem()
|
||||
data class SortType(
|
||||
val sort: AllContentSort,
|
||||
val sortType: DVSortType,
|
||||
val isSelected: Boolean
|
||||
) : MenuSortsItem()
|
||||
}
|
||||
//endregion
|
||||
|
||||
//region BOTTOM_MENU
|
||||
|
@ -217,14 +171,14 @@ sealed class UiSnackbarState {
|
|||
|
||||
//region MAPPING
|
||||
|
||||
fun RestoreAllContentState.Response.Success.mapToSort(): AllContentSort {
|
||||
fun RestoreAllContentState.Response.Success.mapToSort(): ObjectsListSort {
|
||||
val sortType = if (isAsc) DVSortType.ASC else DVSortType.DESC
|
||||
return when (activeSort) {
|
||||
Relations.CREATED_DATE -> AllContentSort.ByDateCreated(sortType = sortType)
|
||||
Relations.LAST_MODIFIED_DATE -> AllContentSort.ByDateUpdated(sortType = sortType)
|
||||
Relations.NAME -> AllContentSort.ByName(sortType = sortType)
|
||||
Relations.LAST_USED_DATE -> AllContentSort.ByDateUsed(sortType = sortType)
|
||||
else -> AllContentSort.ByName(sortType = DVSortType.ASC)
|
||||
Relations.CREATED_DATE -> ObjectsListSort.ByDateCreated(sortType = sortType)
|
||||
Relations.LAST_MODIFIED_DATE -> ObjectsListSort.ByDateUpdated(sortType = sortType)
|
||||
Relations.NAME -> ObjectsListSort.ByName(sortType = sortType)
|
||||
Relations.LAST_USED_DATE -> ObjectsListSort.ByDateUsed(sortType = sortType)
|
||||
else -> ObjectsListSort.ByName(sortType = DVSortType.ASC)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -302,12 +256,12 @@ fun ObjectWrapper.Basic.toAllContentRelation(
|
|||
)
|
||||
}
|
||||
|
||||
fun AllContentSort.toAnalyticsSortType(): Pair<String, String> {
|
||||
fun ObjectsListSort.toAnalyticsSortType(): Pair<String, String> {
|
||||
return when (this) {
|
||||
is AllContentSort.ByName -> "Name" to sortType.toAnalyticsSortType()
|
||||
is AllContentSort.ByDateUpdated -> "Updated" to sortType.toAnalyticsSortType()
|
||||
is AllContentSort.ByDateCreated -> "Created" to sortType.toAnalyticsSortType()
|
||||
is AllContentSort.ByDateUsed -> "Used" to sortType.toAnalyticsSortType()
|
||||
is ObjectsListSort.ByName -> "Name" to sortType.toAnalyticsSortType()
|
||||
is ObjectsListSort.ByDateUpdated -> "Updated" to sortType.toAnalyticsSortType()
|
||||
is ObjectsListSort.ByDateCreated -> "Created" to sortType.toAnalyticsSortType()
|
||||
is ObjectsListSort.ByDateUsed -> "Used" to sortType.toAnalyticsSortType()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,10 +7,11 @@ import com.anytypeio.anytype.core_models.Id
|
|||
import com.anytypeio.anytype.core_models.ObjectType
|
||||
import com.anytypeio.anytype.core_models.ObjectTypeIds
|
||||
import com.anytypeio.anytype.core_models.ObjectTypeUniqueKeys
|
||||
import com.anytypeio.anytype.core_models.RelationFormat
|
||||
import com.anytypeio.anytype.core_models.Relations
|
||||
import com.anytypeio.anytype.core_models.primitives.SpaceId
|
||||
import com.anytypeio.anytype.domain.library.StoreSearchParams
|
||||
import com.anytypeio.anytype.presentation.objects.ObjectsListSort
|
||||
import com.anytypeio.anytype.presentation.objects.toDVSort
|
||||
import com.anytypeio.anytype.presentation.search.ObjectSearchConstants.defaultKeys
|
||||
import com.anytypeio.anytype.presentation.search.ObjectSearchConstants.defaultKeysObjectType
|
||||
import com.anytypeio.anytype.presentation.search.ObjectSearchConstants.defaultRelationKeys
|
||||
|
@ -45,7 +46,7 @@ fun createSubscriptionParams(
|
|||
spaceId: Id,
|
||||
activeMode: UiTitleState,
|
||||
activeTab: AllContentTab,
|
||||
activeSort: AllContentSort,
|
||||
activeSort: ObjectsListSort,
|
||||
limitedObjectIds: List<String>,
|
||||
limit: Int,
|
||||
subscriptionId: String
|
||||
|
@ -73,7 +74,7 @@ fun createSubscriptionParams(
|
|||
|
||||
fun AllContentTab.filtersForSubscribe(
|
||||
spaces: List<Id>,
|
||||
activeSort: AllContentSort,
|
||||
activeSort: ObjectsListSort,
|
||||
limitedObjectIds: List<Id>,
|
||||
activeMode: UiTitleState
|
||||
): Pair<List<DVFilter>, List<DVSort>> {
|
||||
|
@ -240,36 +241,4 @@ private fun buildDeletedFilter(): List<DVFilter> {
|
|||
value = true
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun AllContentSort.toDVSort(): DVSort {
|
||||
return when (this) {
|
||||
is AllContentSort.ByDateCreated -> DVSort(
|
||||
relationKey = relationKey.key,
|
||||
type = sortType,
|
||||
relationFormat = RelationFormat.DATE,
|
||||
includeTime = true,
|
||||
)
|
||||
|
||||
is AllContentSort.ByDateUpdated -> DVSort(
|
||||
relationKey = relationKey.key,
|
||||
type = sortType,
|
||||
relationFormat = RelationFormat.DATE,
|
||||
includeTime = true,
|
||||
)
|
||||
|
||||
is AllContentSort.ByName -> DVSort(
|
||||
relationKey = relationKey.key,
|
||||
type = sortType,
|
||||
relationFormat = RelationFormat.LONG_TEXT,
|
||||
includeTime = false
|
||||
)
|
||||
|
||||
is AllContentSort.ByDateUsed -> DVSort(
|
||||
relationKey = relationKey.key,
|
||||
type = sortType,
|
||||
relationFormat = RelationFormat.DATE,
|
||||
includeTime = true,
|
||||
)
|
||||
}
|
||||
}
|
|
@ -30,9 +30,7 @@ import com.anytypeio.anytype.domain.search.SearchObjects
|
|||
import com.anytypeio.anytype.domain.workspace.RemoveObjectsFromWorkspace
|
||||
import com.anytypeio.anytype.feature_allcontent.models.AllContentBottomMenu
|
||||
import com.anytypeio.anytype.feature_allcontent.models.AllContentMenuMode
|
||||
import com.anytypeio.anytype.feature_allcontent.models.AllContentSort
|
||||
import com.anytypeio.anytype.feature_allcontent.models.AllContentTab
|
||||
import com.anytypeio.anytype.feature_allcontent.models.MenuSortsItem
|
||||
import com.anytypeio.anytype.feature_allcontent.models.UiContentItem
|
||||
import com.anytypeio.anytype.feature_allcontent.models.UiContentState
|
||||
import com.anytypeio.anytype.feature_allcontent.models.UiItemsState
|
||||
|
@ -63,6 +61,8 @@ import com.anytypeio.anytype.presentation.extension.sendAnalyticsObjectCreateEve
|
|||
import com.anytypeio.anytype.presentation.home.OpenObjectNavigation
|
||||
import com.anytypeio.anytype.presentation.home.navigation
|
||||
import com.anytypeio.anytype.presentation.navigation.NavPanelState
|
||||
import com.anytypeio.anytype.presentation.objects.MenuSortsItem
|
||||
import com.anytypeio.anytype.presentation.objects.ObjectsListSort
|
||||
import com.anytypeio.anytype.presentation.objects.getCreateObjectParams
|
||||
import java.time.Instant
|
||||
import java.time.LocalDate
|
||||
|
@ -116,7 +116,7 @@ class AllContentViewModel(
|
|||
) : ViewModel(), AnalyticSpaceHelperDelegate by analyticSpaceHelperDelegate {
|
||||
|
||||
private val searchResultIds = MutableStateFlow<List<Id>>(emptyList())
|
||||
private val sortState = MutableStateFlow<AllContentSort>(AllContentSort.ByName())
|
||||
private val sortState = MutableStateFlow<ObjectsListSort>(ObjectsListSort.ByName())
|
||||
val uiTitleState = MutableStateFlow<UiTitleState>(DEFAULT_INITIAL_MODE)
|
||||
val uiTabsState = MutableStateFlow<UiTabsState>(UiTabsState())
|
||||
val uiMenuState = MutableStateFlow<UiMenuState>(UiMenuState.Hidden)
|
||||
|
@ -279,7 +279,7 @@ class AllContentViewModel(
|
|||
|
||||
private suspend fun handleData(
|
||||
objWrappers: List<ObjectWrapper.Basic>,
|
||||
activeSort: AllContentSort,
|
||||
activeSort: ObjectsListSort,
|
||||
activeTab: AllContentTab
|
||||
): List<UiContentItem> {
|
||||
|
||||
|
@ -310,7 +310,7 @@ class AllContentViewModel(
|
|||
|
||||
private suspend fun mapToUiContentItems(
|
||||
objectWrappers: List<ObjectWrapper.Basic>,
|
||||
activeSort: AllContentSort,
|
||||
activeSort: ObjectsListSort,
|
||||
activeTab: AllContentTab
|
||||
): List<UiContentItem> {
|
||||
val isOwnerOrEditor = permission.value?.isOwnerOrEditor() == true
|
||||
|
@ -345,19 +345,19 @@ class AllContentViewModel(
|
|||
)
|
||||
}
|
||||
val result = when (activeSort) {
|
||||
is AllContentSort.ByDateCreated -> {
|
||||
is ObjectsListSort.ByDateCreated -> {
|
||||
groupItemsByDate(items = items, isSortByDateCreated = true, activeSort = activeSort)
|
||||
}
|
||||
|
||||
is AllContentSort.ByDateUpdated -> {
|
||||
is ObjectsListSort.ByDateUpdated -> {
|
||||
groupItemsByDate(items = items, isSortByDateCreated = false, activeSort = activeSort)
|
||||
}
|
||||
|
||||
is AllContentSort.ByName -> {
|
||||
is ObjectsListSort.ByName -> {
|
||||
items
|
||||
}
|
||||
|
||||
is AllContentSort.ByDateUsed -> {
|
||||
is ObjectsListSort.ByDateUsed -> {
|
||||
items
|
||||
}
|
||||
}
|
||||
|
@ -376,7 +376,7 @@ class AllContentViewModel(
|
|||
private fun groupItemsByDate(
|
||||
items: List<UiContentItem.Item>,
|
||||
isSortByDateCreated: Boolean,
|
||||
activeSort: AllContentSort
|
||||
activeSort: ObjectsListSort
|
||||
): List<UiContentItem> {
|
||||
|
||||
val groupedItems = mutableListOf<UiContentItem>()
|
||||
|
@ -502,35 +502,35 @@ class AllContentViewModel(
|
|||
}
|
||||
}
|
||||
|
||||
fun AllContentTab.sorts(activeSort: AllContentSort): List<MenuSortsItem.Sort> {
|
||||
fun AllContentTab.sorts(activeSort: ObjectsListSort): List<MenuSortsItem.Sort> {
|
||||
return when (this) {
|
||||
AllContentTab.TYPES -> {
|
||||
listOf(
|
||||
MenuSortsItem.Sort(
|
||||
sort = AllContentSort.ByName(isSelected = activeSort is AllContentSort.ByName)
|
||||
sort = ObjectsListSort.ByName(isSelected = activeSort is ObjectsListSort.ByName)
|
||||
),
|
||||
MenuSortsItem.Sort(
|
||||
sort = AllContentSort.ByDateUsed(isSelected = activeSort is AllContentSort.ByDateUsed)
|
||||
sort = ObjectsListSort.ByDateUsed(isSelected = activeSort is ObjectsListSort.ByDateUsed)
|
||||
)
|
||||
)
|
||||
}
|
||||
AllContentTab.RELATIONS -> {
|
||||
listOf(
|
||||
MenuSortsItem.Sort(
|
||||
sort = AllContentSort.ByName(isSelected = activeSort is AllContentSort.ByName)
|
||||
sort = ObjectsListSort.ByName(isSelected = activeSort is ObjectsListSort.ByName)
|
||||
)
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
listOf(
|
||||
MenuSortsItem.Sort(
|
||||
sort = AllContentSort.ByDateUpdated(isSelected = activeSort is AllContentSort.ByDateUpdated)
|
||||
sort = ObjectsListSort.ByDateUpdated(isSelected = activeSort is ObjectsListSort.ByDateUpdated)
|
||||
),
|
||||
MenuSortsItem.Sort(
|
||||
sort = AllContentSort.ByDateCreated(isSelected = activeSort is AllContentSort.ByDateCreated)
|
||||
sort = ObjectsListSort.ByDateCreated(isSelected = activeSort is ObjectsListSort.ByDateCreated)
|
||||
),
|
||||
MenuSortsItem.Sort(
|
||||
sort = AllContentSort.ByName(isSelected = activeSort is AllContentSort.ByName)
|
||||
sort = ObjectsListSort.ByName(isSelected = activeSort is ObjectsListSort.ByName)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -552,12 +552,12 @@ class AllContentViewModel(
|
|||
private fun AllContentTab.updateInitialState() {
|
||||
return when (this) {
|
||||
AllContentTab.TYPES -> {
|
||||
sortState.value = AllContentSort.ByName()
|
||||
sortState.value = ObjectsListSort.ByName()
|
||||
userInput.value = DEFAULT_QUERY
|
||||
uiTitleState.value = UiTitleState.AllContent
|
||||
}
|
||||
AllContentTab.RELATIONS -> {
|
||||
sortState.value = AllContentSort.ByName()
|
||||
sortState.value = ObjectsListSort.ByName()
|
||||
userInput.value = DEFAULT_QUERY
|
||||
uiTitleState.value = UiTitleState.AllContent
|
||||
}
|
||||
|
@ -598,19 +598,19 @@ class AllContentViewModel(
|
|||
}
|
||||
}
|
||||
|
||||
fun onSortClicked(sort: AllContentSort) {
|
||||
fun onSortClicked(sort: ObjectsListSort) {
|
||||
Timber.d("onSortClicked: $sort")
|
||||
val newSort = when (sort) {
|
||||
is AllContentSort.ByDateCreated -> {
|
||||
is ObjectsListSort.ByDateCreated -> {
|
||||
sort.copy(isSelected = true)
|
||||
}
|
||||
is AllContentSort.ByDateUpdated -> {
|
||||
is ObjectsListSort.ByDateUpdated -> {
|
||||
sort.copy(isSelected = true)
|
||||
}
|
||||
is AllContentSort.ByName -> {
|
||||
is ObjectsListSort.ByName -> {
|
||||
sort.copy(isSelected = true)
|
||||
}
|
||||
is AllContentSort.ByDateUsed -> {
|
||||
is ObjectsListSort.ByDateUsed -> {
|
||||
sort.copy(isSelected = true)
|
||||
}
|
||||
}
|
||||
|
@ -628,7 +628,7 @@ class AllContentViewModel(
|
|||
}
|
||||
}
|
||||
|
||||
private fun proceedWithSortSaving(activeTab: UiTabsState, sort: AllContentSort) {
|
||||
private fun proceedWithSortSaving(activeTab: UiTabsState, sort: ObjectsListSort) {
|
||||
if (activeTab.selectedTab == AllContentTab.TYPES
|
||||
|| activeTab.selectedTab == AllContentTab.RELATIONS
|
||||
) {
|
||||
|
|
|
@ -1,109 +1,77 @@
|
|||
package com.anytypeio.anytype.feature_allcontent.ui
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.foundation.layout.wrapContentSize
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment.Companion.CenterVertically
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.rotate
|
||||
import androidx.compose.ui.graphics.ColorFilter.Companion.tint
|
||||
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 androidx.compose.ui.unit.dp
|
||||
import com.anytypeio.anytype.core_models.DVSortType
|
||||
import com.anytypeio.anytype.core_ui.common.DefaultPreviews
|
||||
import com.anytypeio.anytype.core_ui.views.BodyCalloutRegular
|
||||
import com.anytypeio.anytype.core_ui.views.UXBody
|
||||
import com.anytypeio.anytype.core_ui.foundation.Divider
|
||||
import com.anytypeio.anytype.core_ui.lists.objects.menu.ObjectsListMenuItem
|
||||
import com.anytypeio.anytype.core_ui.lists.objects.menu.ObjectsListSortingMenuContainer
|
||||
import com.anytypeio.anytype.feature_allcontent.R
|
||||
import com.anytypeio.anytype.feature_allcontent.models.AllContentMenuMode
|
||||
import com.anytypeio.anytype.feature_allcontent.models.AllContentSort
|
||||
import com.anytypeio.anytype.feature_allcontent.models.MenuSortsItem
|
||||
import com.anytypeio.anytype.feature_allcontent.models.UiMenuState
|
||||
import com.anytypeio.anytype.presentation.objects.MenuSortsItem
|
||||
import com.anytypeio.anytype.presentation.objects.ObjectsListSort
|
||||
|
||||
@Composable
|
||||
fun AllContentMenu(
|
||||
uiMenuState: UiMenuState.Visible,
|
||||
onModeClick: (AllContentMenuMode) -> Unit,
|
||||
onSortClick: (AllContentSort) -> Unit,
|
||||
onSortClick: (ObjectsListSort) -> Unit,
|
||||
onBinClick: () -> Unit
|
||||
) {
|
||||
var sortingExpanded by remember { mutableStateOf(false) }
|
||||
|
||||
uiMenuState.mode.forEach { item ->
|
||||
MenuItem(
|
||||
ObjectsListMenuItem(
|
||||
title = getModeTitle(item),
|
||||
isSelected = item.isSelected,
|
||||
modifier = Modifier.clickable {
|
||||
onModeClick(item)
|
||||
}
|
||||
)
|
||||
Divider(0.5.dp)
|
||||
Divider(
|
||||
height = 0.5.dp,
|
||||
paddingStart = 0.dp,
|
||||
paddingEnd = 0.dp,
|
||||
color = colorResource(R.color.shape_secondary)
|
||||
)
|
||||
}
|
||||
|
||||
if (uiMenuState.mode.isNotEmpty()) {
|
||||
Divider(7.5.dp)
|
||||
Divider(
|
||||
height = 7.5.dp,
|
||||
paddingStart = 0.dp,
|
||||
paddingEnd = 0.dp,
|
||||
color = colorResource(R.color.shape_secondary)
|
||||
)
|
||||
}
|
||||
SortingBox(
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
sortingExpanded = !sortingExpanded
|
||||
},
|
||||
subtitle = uiMenuState.container.sort.title(),
|
||||
isExpanded = sortingExpanded
|
||||
|
||||
ObjectsListSortingMenuContainer(
|
||||
container = uiMenuState.container,
|
||||
sorts = uiMenuState.sorts,
|
||||
types = uiMenuState.types,
|
||||
sortingExpanded = sortingExpanded,
|
||||
onSortClick = onSortClick,
|
||||
onChangeSortExpandedState = { sortingExpanded = it }
|
||||
)
|
||||
Divider(0.5.dp)
|
||||
if (sortingExpanded) {
|
||||
uiMenuState.sorts.forEach { item ->
|
||||
MenuItem(
|
||||
title = item.sort.title(),
|
||||
isSelected = item.sort.isSelected,
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
onSortClick(item.sort)
|
||||
}
|
||||
)
|
||||
Divider(0.5.dp)
|
||||
}
|
||||
Divider(7.5.dp)
|
||||
uiMenuState.types.forEachIndexed { index, item ->
|
||||
MenuItem(
|
||||
title = item.sortType.title(item.sort),
|
||||
isSelected = item.isSelected,
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
val updatedSort = when (item.sort) {
|
||||
is AllContentSort.ByName -> item.sort.copy(sortType = item.sortType)
|
||||
is AllContentSort.ByDateCreated -> item.sort.copy(sortType = item.sortType)
|
||||
is AllContentSort.ByDateUpdated -> item.sort.copy(sortType = item.sortType)
|
||||
is AllContentSort.ByDateUsed -> item.sort.copy(sortType = item.sortType)
|
||||
}
|
||||
onSortClick(updatedSort)
|
||||
}
|
||||
)
|
||||
if (index < uiMenuState.types.size - 1) {
|
||||
Divider(0.5.dp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (uiMenuState.showBin && !sortingExpanded) {
|
||||
Divider(7.5.dp)
|
||||
MenuItem(
|
||||
Divider(
|
||||
height = 7.5.dp,
|
||||
paddingStart = 0.dp,
|
||||
paddingEnd = 0.dp,
|
||||
color = colorResource(R.color.shape_secondary)
|
||||
)
|
||||
ObjectsListMenuItem(
|
||||
title = stringResource(id = R.string.all_content_view_bin),
|
||||
isSelected = false,
|
||||
modifier = Modifier.clickable { onBinClick() }
|
||||
|
@ -111,83 +79,6 @@ fun AllContentMenu(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Divider(height: Dp) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(height)
|
||||
.background(colorResource(id = R.color.shape_secondary))
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SortingBox(modifier: Modifier, subtitle: String, isExpanded: Boolean) {
|
||||
val rotationAngle = if (isExpanded) 90f else 0f
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.background(colorResource(id = R.color.background_secondary)),
|
||||
verticalAlignment = CenterVertically
|
||||
) {
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.padding(start = 10.dp)
|
||||
.size(18.dp)
|
||||
.rotate(rotationAngle),
|
||||
painter = painterResource(R.drawable.ic_arrow_disclosure_18),
|
||||
contentDescription = "",
|
||||
colorFilter = tint(colorResource(id = R.color.glyph_selected))
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.wrapContentHeight()
|
||||
.padding(top = 11.dp, bottom = 10.dp, start = 6.dp)
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.all_content_sort_by),
|
||||
modifier = Modifier.wrapContentSize(),
|
||||
style = UXBody,
|
||||
color = colorResource(id = R.color.text_primary)
|
||||
)
|
||||
Text(
|
||||
text = subtitle,
|
||||
modifier = Modifier.wrapContentSize(),
|
||||
style = BodyCalloutRegular,
|
||||
color = colorResource(id = R.color.text_secondary)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MenuItem(modifier: Modifier, title: String, isSelected: Boolean) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.height(44.dp)
|
||||
.background(colorResource(id = R.color.background_secondary)),
|
||||
verticalAlignment = CenterVertically,
|
||||
) {
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.wrapContentSize()
|
||||
.padding(start = 12.dp),
|
||||
painter = painterResource(R.drawable.ic_check_16),
|
||||
contentDescription = "All Content mode selected",
|
||||
alpha = if (isSelected) 1f else 0f
|
||||
)
|
||||
Text(
|
||||
text = title,
|
||||
modifier = Modifier
|
||||
.wrapContentSize()
|
||||
.padding(start = 8.dp),
|
||||
style = UXBody,
|
||||
color = colorResource(id = R.color.text_primary)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
//region RESOURCES
|
||||
@Composable
|
||||
private fun getModeTitle(mode: AllContentMenuMode): String = stringResource(
|
||||
|
@ -196,43 +87,6 @@ private fun getModeTitle(mode: AllContentMenuMode): String = stringResource(
|
|||
is AllContentMenuMode.Unlinked -> R.string.all_content_title_only_unlinked
|
||||
}
|
||||
)
|
||||
|
||||
@Composable
|
||||
private fun AllContentSort.title(): String = stringResource(
|
||||
when (this) {
|
||||
is AllContentSort.ByDateCreated -> R.string.all_content_sort_date_created
|
||||
is AllContentSort.ByDateUpdated -> R.string.all_content_sort_date_updated
|
||||
is AllContentSort.ByName -> R.string.all_content_sort_name
|
||||
is AllContentSort.ByDateUsed -> R.string.all_content_sort_date_used
|
||||
}
|
||||
)
|
||||
|
||||
@Composable
|
||||
private fun DVSortType.title(sort: AllContentSort): String = when (this) {
|
||||
DVSortType.ASC -> {
|
||||
when (sort) {
|
||||
is AllContentSort.ByDateCreated, is AllContentSort.ByDateUpdated, is AllContentSort.ByDateUsed -> stringResource(
|
||||
id = R.string.all_content_sort_date_asc
|
||||
)
|
||||
|
||||
is AllContentSort.ByName -> stringResource(id = R.string.all_content_sort_name_asc)
|
||||
}
|
||||
}
|
||||
|
||||
DVSortType.DESC -> {
|
||||
when (sort) {
|
||||
is AllContentSort.ByDateCreated,
|
||||
is AllContentSort.ByDateUpdated,
|
||||
is AllContentSort.ByDateUsed -> stringResource(
|
||||
id = R.string.all_content_sort_date_desc
|
||||
)
|
||||
|
||||
is AllContentSort.ByName -> stringResource(id = R.string.all_content_sort_name_desc)
|
||||
}
|
||||
}
|
||||
|
||||
DVSortType.CUSTOM -> ""
|
||||
}
|
||||
//endregion
|
||||
|
||||
//region PREVIEW
|
||||
|
@ -247,28 +101,28 @@ fun AllContentMenuPreview() {
|
|||
),
|
||||
sorts = listOf(
|
||||
MenuSortsItem.Sort(
|
||||
sort = AllContentSort.ByName(isSelected = true)
|
||||
sort = ObjectsListSort.ByName(isSelected = true)
|
||||
),
|
||||
MenuSortsItem.Sort(
|
||||
AllContentSort.ByDateUpdated(isSelected = false)
|
||||
ObjectsListSort.ByDateUpdated(isSelected = false)
|
||||
),
|
||||
MenuSortsItem.Sort(
|
||||
AllContentSort.ByDateCreated(isSelected = false)
|
||||
ObjectsListSort.ByDateCreated(isSelected = false)
|
||||
)
|
||||
),
|
||||
types = listOf(
|
||||
MenuSortsItem.SortType(
|
||||
sortType = DVSortType.ASC,
|
||||
isSelected = true,
|
||||
sort = AllContentSort.ByName(isSelected = true)
|
||||
sort = ObjectsListSort.ByName(isSelected = true)
|
||||
),
|
||||
MenuSortsItem.SortType(
|
||||
sortType = DVSortType.DESC,
|
||||
isSelected = false,
|
||||
sort = AllContentSort.ByName(isSelected = false)
|
||||
sort = ObjectsListSort.ByName(isSelected = false)
|
||||
),
|
||||
),
|
||||
container = MenuSortsItem.Container(AllContentSort.ByName())
|
||||
container = MenuSortsItem.Container(ObjectsListSort.ByName())
|
||||
),
|
||||
onModeClick = {},
|
||||
onSortClick = {},
|
||||
|
|
|
@ -70,6 +70,7 @@ import com.anytypeio.anytype.core_models.primitives.SpaceId
|
|||
import com.anytypeio.anytype.core_ui.common.DefaultPreviews
|
||||
import com.anytypeio.anytype.core_ui.extensions.simpleIcon
|
||||
import com.anytypeio.anytype.core_ui.extensions.swapList
|
||||
import com.anytypeio.anytype.core_ui.foundation.DefaultSearchBar
|
||||
import com.anytypeio.anytype.core_ui.foundation.DismissBackground
|
||||
import com.anytypeio.anytype.core_ui.foundation.Divider
|
||||
import com.anytypeio.anytype.core_ui.foundation.components.BottomNavigationMenu
|
||||
|
@ -90,7 +91,6 @@ import com.anytypeio.anytype.feature_allcontent.BuildConfig
|
|||
import com.anytypeio.anytype.feature_allcontent.R
|
||||
import com.anytypeio.anytype.feature_allcontent.models.AllContentBottomMenu
|
||||
import com.anytypeio.anytype.feature_allcontent.models.AllContentMenuMode
|
||||
import com.anytypeio.anytype.feature_allcontent.models.AllContentSort
|
||||
import com.anytypeio.anytype.feature_allcontent.models.AllContentTab
|
||||
import com.anytypeio.anytype.feature_allcontent.models.UiContentItem
|
||||
import com.anytypeio.anytype.feature_allcontent.models.UiContentState
|
||||
|
@ -99,6 +99,7 @@ import com.anytypeio.anytype.feature_allcontent.models.UiMenuState
|
|||
import com.anytypeio.anytype.feature_allcontent.models.UiSnackbarState
|
||||
import com.anytypeio.anytype.feature_allcontent.models.UiTabsState
|
||||
import com.anytypeio.anytype.feature_allcontent.models.UiTitleState
|
||||
import com.anytypeio.anytype.presentation.objects.ObjectsListSort
|
||||
import com.anytypeio.anytype.presentation.navigation.NavPanelState
|
||||
import com.anytypeio.anytype.presentation.objects.ObjectIcon
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
@ -117,7 +118,7 @@ fun AllContentWrapperScreen(
|
|||
onTabClick: (AllContentTab) -> Unit,
|
||||
onQueryChanged: (String) -> Unit,
|
||||
onModeClick: (AllContentMenuMode) -> Unit,
|
||||
onSortClick: (AllContentSort) -> Unit,
|
||||
onSortClick: (ObjectsListSort) -> Unit,
|
||||
onItemClicked: (UiContentItem.Item) -> Unit,
|
||||
onTypeClicked: (UiContentItem) -> Unit,
|
||||
onRelationClicked: (UiContentItem) -> Unit,
|
||||
|
@ -180,7 +181,7 @@ fun AllContentMainScreen(
|
|||
onTabClick: (AllContentTab) -> Unit,
|
||||
onQueryChanged: (String) -> Unit,
|
||||
onModeClick: (AllContentMenuMode) -> Unit,
|
||||
onSortClick: (AllContentSort) -> Unit,
|
||||
onSortClick: (ObjectsListSort) -> Unit,
|
||||
onItemClicked: (UiContentItem.Item) -> Unit,
|
||||
onTypeClicked: (UiContentItem) -> Unit,
|
||||
onRelationClicked: (UiContentItem) -> Unit,
|
||||
|
@ -267,10 +268,14 @@ fun AllContentMainScreen(
|
|||
onTabClick(tab)
|
||||
}
|
||||
Spacer(modifier = Modifier.size(10.dp))
|
||||
AllContentSearchBar(onQueryChanged = {
|
||||
isSearchEmpty = it.isEmpty()
|
||||
onQueryChanged(it)
|
||||
})
|
||||
DefaultSearchBar(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
onQueryChanged = {
|
||||
isSearchEmpty = it.isEmpty()
|
||||
onQueryChanged(it)
|
||||
})
|
||||
Spacer(modifier = Modifier.size(10.dp))
|
||||
Divider(paddingStart = 0.dp, paddingEnd = 0.dp)
|
||||
}
|
||||
|
|
|
@ -1,17 +1,9 @@
|
|||
package com.anytypeio.anytype.feature_allcontent.ui
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.gestures.snapping.rememberSnapFlingBehavior
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
|
@ -22,54 +14,38 @@ import androidx.compose.foundation.layout.wrapContentWidth
|
|||
import androidx.compose.foundation.lazy.LazyRow
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.selection.LocalTextSelectionColors
|
||||
import androidx.compose.foundation.text.selection.TextSelectionColors
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.IconButton
|
||||
import androidx.compose.material.TextFieldDefaults
|
||||
import androidx.compose.material3.CenterAlignedTopAppBar
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.text.input.VisualTransformation
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.anytypeio.anytype.core_models.DVSortType
|
||||
import com.anytypeio.anytype.core_ui.common.DefaultPreviews
|
||||
import com.anytypeio.anytype.core_ui.extensions.bouncingClickable
|
||||
import com.anytypeio.anytype.core_ui.foundation.noRippleClickable
|
||||
import com.anytypeio.anytype.core_ui.foundation.noRippleThrottledClickable
|
||||
import com.anytypeio.anytype.core_ui.views.BodyRegular
|
||||
import com.anytypeio.anytype.core_ui.views.Title1
|
||||
import com.anytypeio.anytype.core_ui.views.Title2
|
||||
import com.anytypeio.anytype.feature_allcontent.R
|
||||
import com.anytypeio.anytype.feature_allcontent.models.AllContentMenuMode
|
||||
import com.anytypeio.anytype.feature_allcontent.models.AllContentSort
|
||||
import com.anytypeio.anytype.feature_allcontent.models.AllContentTab
|
||||
import com.anytypeio.anytype.feature_allcontent.models.MenuSortsItem
|
||||
import com.anytypeio.anytype.feature_allcontent.models.UiMenuState
|
||||
import com.anytypeio.anytype.feature_allcontent.models.UiTabsState
|
||||
import com.anytypeio.anytype.feature_allcontent.models.UiTitleState
|
||||
import com.anytypeio.anytype.presentation.objects.MenuSortsItem
|
||||
import com.anytypeio.anytype.presentation.objects.ObjectsListSort
|
||||
|
||||
//region AllContentTopBarContainer
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
|
@ -78,7 +54,7 @@ fun AllContentTopBarContainer(
|
|||
titleState: UiTitleState,
|
||||
uiMenuState: UiMenuState,
|
||||
onModeClick: (AllContentMenuMode) -> Unit,
|
||||
onSortClick: (AllContentSort) -> Unit,
|
||||
onSortClick: (ObjectsListSort) -> Unit,
|
||||
onBinClick: () -> Unit,
|
||||
onBackClick: () -> Unit
|
||||
) {
|
||||
|
@ -139,21 +115,21 @@ private fun AllContentTopBarContainerPreview() {
|
|||
AllContentMenuMode.Unlinked()
|
||||
),
|
||||
container = MenuSortsItem.Container(
|
||||
sort = AllContentSort.ByName(isSelected = true)
|
||||
sort = ObjectsListSort.ByName(isSelected = true)
|
||||
),
|
||||
sorts = listOf(
|
||||
MenuSortsItem.Sort(
|
||||
sort = AllContentSort.ByName(isSelected = true)
|
||||
sort = ObjectsListSort.ByName(isSelected = true)
|
||||
),
|
||||
),
|
||||
types = listOf(
|
||||
MenuSortsItem.SortType(
|
||||
sort = AllContentSort.ByName(isSelected = true),
|
||||
sort = ObjectsListSort.ByName(isSelected = true),
|
||||
sortType = DVSortType.DESC,
|
||||
isSelected = true
|
||||
),
|
||||
MenuSortsItem.SortType(
|
||||
sort = AllContentSort.ByDateCreated(isSelected = false),
|
||||
sort = ObjectsListSort.ByDateCreated(isSelected = false),
|
||||
sortType = DVSortType.ASC,
|
||||
isSelected = false
|
||||
),
|
||||
|
@ -302,122 +278,4 @@ private fun AllContentTabsPreview() {
|
|||
)
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
//region SearchBar
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
fun AllContentSearchBar(onQueryChanged: (String) -> Unit) {
|
||||
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
val focus = LocalFocusManager.current
|
||||
val focusRequester = FocusRequester()
|
||||
|
||||
val selectionColors = TextSelectionColors(
|
||||
backgroundColor = colorResource(id = R.color.cursor_color).copy(
|
||||
alpha = 0.2f
|
||||
),
|
||||
handleColor = colorResource(id = R.color.cursor_color),
|
||||
)
|
||||
|
||||
var query by remember { mutableStateOf(TextFieldValue()) }
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
.background(
|
||||
color = colorResource(id = R.color.shape_transparent),
|
||||
shape = RoundedCornerShape(10.dp)
|
||||
)
|
||||
.height(40.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_search_18),
|
||||
contentDescription = "Search icon",
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterVertically)
|
||||
.padding(
|
||||
start = 11.dp
|
||||
)
|
||||
)
|
||||
CompositionLocalProvider(value = LocalTextSelectionColors provides selectionColors) {
|
||||
|
||||
BasicTextField(
|
||||
value = query,
|
||||
modifier = Modifier
|
||||
.weight(1.0f)
|
||||
.padding(start = 6.dp)
|
||||
.align(Alignment.CenterVertically)
|
||||
.focusRequester(focusRequester),
|
||||
textStyle = BodyRegular.copy(
|
||||
color = colorResource(id = R.color.text_primary)
|
||||
),
|
||||
onValueChange = { input ->
|
||||
query = input.also {
|
||||
onQueryChanged(input.text)
|
||||
}
|
||||
},
|
||||
singleLine = true,
|
||||
maxLines = 1,
|
||||
keyboardActions = KeyboardActions(
|
||||
onDone = {
|
||||
focus.clearFocus(true)
|
||||
}
|
||||
),
|
||||
decorationBox = @Composable { innerTextField ->
|
||||
TextFieldDefaults.OutlinedTextFieldDecorationBox(
|
||||
value = query.text,
|
||||
innerTextField = innerTextField,
|
||||
enabled = true,
|
||||
singleLine = true,
|
||||
visualTransformation = VisualTransformation.None,
|
||||
interactionSource = interactionSource,
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringResource(id = R.string.search),
|
||||
style = BodyRegular.copy(
|
||||
color = colorResource(id = R.color.text_tertiary)
|
||||
)
|
||||
)
|
||||
},
|
||||
colors = TextFieldDefaults.textFieldColors(
|
||||
backgroundColor = Color.Transparent,
|
||||
cursorColor = colorResource(id = R.color.cursor_color),
|
||||
),
|
||||
border = {},
|
||||
contentPadding = PaddingValues()
|
||||
)
|
||||
},
|
||||
cursorBrush = SolidColor(colorResource(id = R.color.palette_system_blue)),
|
||||
)
|
||||
}
|
||||
Spacer(Modifier.width(9.dp))
|
||||
AnimatedVisibility(
|
||||
visible = query.text.isNotEmpty(),
|
||||
enter = fadeIn(tween(100)),
|
||||
exit = fadeOut(tween(100))
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_clear_18),
|
||||
contentDescription = "Clear icon",
|
||||
modifier = Modifier
|
||||
.padding(end = 9.dp)
|
||||
.noRippleClickable {
|
||||
query = TextFieldValue().also {
|
||||
onQueryChanged("")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DefaultPreviews
|
||||
@Composable
|
||||
private fun AllContentSearchBarPreview() {
|
||||
AllContentSearchBar() {}
|
||||
}
|
||||
//endregion
|
|
@ -1,20 +1,9 @@
|
|||
package com.anytypeio.anytype.feature_date.mapping
|
||||
|
||||
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.RelationListWithValueItem
|
||||
import com.anytypeio.anytype.core_models.Relations
|
||||
import com.anytypeio.anytype.core_models.primitives.SpaceId
|
||||
import com.anytypeio.anytype.core_models.restrictions.ObjectRestriction
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.domain.objects.StoreOfRelations
|
||||
import com.anytypeio.anytype.domain.primitives.FieldParser
|
||||
import com.anytypeio.anytype.feature_date.viewmodel.UiFieldsItem
|
||||
import com.anytypeio.anytype.feature_date.viewmodel.UiObjectsListItem
|
||||
import com.anytypeio.anytype.presentation.mapper.objectIcon
|
||||
import com.anytypeio.anytype.presentation.objects.getProperType
|
||||
import timber.log.Timber
|
||||
|
||||
suspend fun List<RelationListWithValueItem>.toUiFieldsItem(
|
||||
|
@ -52,33 +41,4 @@ suspend fun List<RelationListWithValueItem>.toUiFieldsItem(
|
|||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun ObjectWrapper.Basic.toUiObjectsListItem(
|
||||
space: SpaceId,
|
||||
urlBuilder: UrlBuilder,
|
||||
objectTypes: List<ObjectWrapper.Type>,
|
||||
fieldParser: FieldParser,
|
||||
isOwnerOrEditor: Boolean
|
||||
): UiObjectsListItem {
|
||||
val obj = this
|
||||
val typeUrl = obj.getProperType()
|
||||
val isProfile = typeUrl == MarketplaceObjectTypeIds.PROFILE
|
||||
val layout = obj.layout ?: ObjectType.Layout.BASIC
|
||||
return UiObjectsListItem.Item(
|
||||
id = obj.id,
|
||||
space = space,
|
||||
name = fieldParser.getObjectName(obj),
|
||||
type = typeUrl,
|
||||
typeName = objectTypes.firstOrNull { type ->
|
||||
if (isProfile) {
|
||||
type.uniqueKey == ObjectTypeUniqueKeys.PROFILE
|
||||
} else {
|
||||
type.id == typeUrl
|
||||
}
|
||||
}?.name,
|
||||
layout = layout,
|
||||
icon = obj.objectIcon(builder = urlBuilder),
|
||||
isPossibleToDelete = isOwnerOrEditor && !restrictions.contains(ObjectRestriction.DELETE)
|
||||
)
|
||||
}
|
|
@ -24,7 +24,6 @@ import androidx.compose.material3.SnackbarResult
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.colorResource
|
||||
|
@ -32,18 +31,19 @@ import androidx.compose.ui.res.stringResource
|
|||
import androidx.compose.ui.unit.dp
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_ui.foundation.components.BottomNavigationMenu
|
||||
import com.anytypeio.anytype.core_ui.lists.objects.PaginatedObjectList
|
||||
import com.anytypeio.anytype.core_ui.lists.objects.UiContentState
|
||||
import com.anytypeio.anytype.core_ui.lists.objects.UiObjectsListState
|
||||
import com.anytypeio.anytype.core_ui.syncstatus.SpaceSyncStatusScreen
|
||||
import com.anytypeio.anytype.core_utils.insets.EDGE_TO_EDGE_MIN_SDK
|
||||
import com.anytypeio.anytype.feature_date.R
|
||||
import com.anytypeio.anytype.feature_date.ui.models.DateEvent
|
||||
import com.anytypeio.anytype.feature_date.viewmodel.UiCalendarIconState
|
||||
import com.anytypeio.anytype.feature_date.viewmodel.UiCalendarState
|
||||
import com.anytypeio.anytype.feature_date.viewmodel.UiContentState
|
||||
import com.anytypeio.anytype.feature_date.viewmodel.UiFieldsSheetState
|
||||
import com.anytypeio.anytype.feature_date.viewmodel.UiFieldsState
|
||||
import com.anytypeio.anytype.feature_date.viewmodel.UiHeaderState
|
||||
import com.anytypeio.anytype.feature_date.viewmodel.UiNavigationWidget
|
||||
import com.anytypeio.anytype.feature_date.viewmodel.UiObjectsListState
|
||||
import com.anytypeio.anytype.feature_date.viewmodel.UiSnackbarState
|
||||
import com.anytypeio.anytype.feature_date.viewmodel.UiSyncStatusBadgeState
|
||||
import com.anytypeio.anytype.presentation.sync.SyncStatusWidgetState
|
||||
|
@ -68,8 +68,6 @@ fun DateMainScreen(
|
|||
onDateEvent: (DateEvent) -> Unit
|
||||
) {
|
||||
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
val snackBarHostState = remember { SnackbarHostState() }
|
||||
|
||||
val snackBarText = stringResource(R.string.all_content_snackbar_title)
|
||||
|
@ -156,11 +154,19 @@ fun DateMainScreen(
|
|||
if (uiContentState is UiContentState.Empty) {
|
||||
EmptyScreen()
|
||||
}
|
||||
ObjectsScreen(
|
||||
PaginatedObjectList(
|
||||
state = uiObjectsListState,
|
||||
uiState = uiContentState,
|
||||
canPaginate = canPaginate,
|
||||
onDateEvent = onDateEvent,
|
||||
onLoadMore = {
|
||||
DateEvent.ObjectsList.OnLoadMore
|
||||
},
|
||||
onMoveToBin = { item ->
|
||||
DateEvent.ObjectsList.OnObjectMoveToBin(item)
|
||||
},
|
||||
onObjectClicked = { item ->
|
||||
DateEvent.ObjectsList.OnObjectClicked(item)
|
||||
}
|
||||
)
|
||||
BottomNavigationMenu(
|
||||
modifier = Modifier
|
||||
|
|
|
@ -1,337 +0,0 @@
|
|||
package com.anytypeio.anytype.feature_date.ui
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.shrinkVertically
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxScope
|
||||
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.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.ListItemDefaults
|
||||
import androidx.compose.material3.SwipeToDismissBox
|
||||
import androidx.compose.material3.SwipeToDismissBoxValue
|
||||
import androidx.compose.material3.rememberSwipeToDismissBoxState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.anytypeio.anytype.core_ui.common.DefaultPreviews
|
||||
import com.anytypeio.anytype.core_ui.common.ShimmerEffect
|
||||
import com.anytypeio.anytype.core_ui.extensions.swapList
|
||||
import com.anytypeio.anytype.core_ui.foundation.DismissBackground
|
||||
import com.anytypeio.anytype.core_ui.foundation.Divider
|
||||
import com.anytypeio.anytype.core_ui.foundation.noRippleThrottledClickable
|
||||
import com.anytypeio.anytype.core_ui.views.ButtonSize
|
||||
import com.anytypeio.anytype.core_ui.views.PreviewTitle2Regular
|
||||
import com.anytypeio.anytype.core_ui.views.Relations3
|
||||
import com.anytypeio.anytype.core_ui.views.animations.DotsLoadingIndicator
|
||||
import com.anytypeio.anytype.core_ui.views.animations.FadeAnimationSpecs
|
||||
import com.anytypeio.anytype.core_ui.widgets.ListWidgetObjectIcon
|
||||
import com.anytypeio.anytype.feature_date.R
|
||||
import com.anytypeio.anytype.feature_date.ui.models.DateEvent
|
||||
import com.anytypeio.anytype.feature_date.ui.models.StubVerticalItems
|
||||
import com.anytypeio.anytype.feature_date.viewmodel.UiContentState
|
||||
import com.anytypeio.anytype.feature_date.viewmodel.UiObjectsListItem
|
||||
import com.anytypeio.anytype.feature_date.viewmodel.UiObjectsListState
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun ObjectsScreen(
|
||||
state: UiObjectsListState,
|
||||
uiState: UiContentState,
|
||||
canPaginate: Boolean,
|
||||
onDateEvent: (DateEvent) -> Unit
|
||||
) {
|
||||
val items = remember { mutableStateListOf<UiObjectsListItem>() }
|
||||
items.swapList(state.items)
|
||||
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
val lazyListState = rememberLazyListState()
|
||||
|
||||
val canPaginateState = remember { mutableStateOf(false) }
|
||||
LaunchedEffect(key1 = canPaginate) {
|
||||
canPaginateState.value = canPaginate
|
||||
}
|
||||
|
||||
val shouldStartPaging = remember {
|
||||
derivedStateOf {
|
||||
canPaginateState.value && (lazyListState.layoutInfo.visibleItemsInfo.lastOrNull()?.index
|
||||
?: -9) >= (lazyListState.layoutInfo.totalItemsCount - 2)
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(key1 = shouldStartPaging.value) {
|
||||
if (shouldStartPaging.value && uiState is UiContentState.Idle) {
|
||||
onDateEvent(DateEvent.ObjectsList.OnLoadMore)
|
||||
}
|
||||
}
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.padding(top = 8.dp)
|
||||
.fillMaxSize(),
|
||||
state = lazyListState
|
||||
) {
|
||||
items(
|
||||
count = items.size,
|
||||
key = { index -> items[index].id },
|
||||
contentType = { index ->
|
||||
when (items[index]) {
|
||||
is UiObjectsListItem.Loading -> "loading"
|
||||
is UiObjectsListItem.Item -> "item"
|
||||
}
|
||||
}
|
||||
) { index ->
|
||||
val item = items[index]
|
||||
when (item) {
|
||||
is UiObjectsListItem.Item -> {
|
||||
SwipeToDismissListItems(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.animateItem()
|
||||
.noRippleThrottledClickable {
|
||||
onDateEvent(DateEvent.ObjectsList.OnObjectClicked(item))
|
||||
},
|
||||
item = item,
|
||||
onDateEvent = onDateEvent
|
||||
)
|
||||
Divider(paddingStart = 16.dp, paddingEnd = 16.dp)
|
||||
}
|
||||
is UiObjectsListItem.Loading -> {
|
||||
ListItemLoading(modifier = Modifier)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (uiState is UiContentState.Paging) {
|
||||
item {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillParentMaxWidth()
|
||||
.height(52.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
LoadingState()
|
||||
}
|
||||
}
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(200.dp))
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(key1 = uiState) {
|
||||
if (uiState is UiContentState.Idle) {
|
||||
if (uiState.scrollToTop) {
|
||||
scope.launch {
|
||||
lazyListState.scrollToItem(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ListItem(
|
||||
modifier: Modifier,
|
||||
item: UiObjectsListItem.Item
|
||||
) {
|
||||
val name = item.name.trim().ifBlank { stringResource(R.string.untitled) }
|
||||
val createdBy = item.createdBy
|
||||
val typeName = item.typeName
|
||||
ListItem(
|
||||
colors = ListItemDefaults.colors(
|
||||
containerColor = colorResource(id = R.color.background_primary),
|
||||
),
|
||||
modifier = modifier
|
||||
.height(72.dp)
|
||||
.fillMaxWidth(),
|
||||
headlineContent = {
|
||||
Text(
|
||||
text = name,
|
||||
style = PreviewTitle2Regular,
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
},
|
||||
supportingContent = {
|
||||
Row {
|
||||
if (typeName != null) {
|
||||
Text(
|
||||
text = typeName,
|
||||
style = Relations3,
|
||||
color = colorResource(id = R.color.text_secondary),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
if (!createdBy.isNullOrBlank()) {
|
||||
Text(
|
||||
text = "${stringResource(R.string.date_layout_item_created_by)} • $createdBy",
|
||||
style = Relations3,
|
||||
color = colorResource(id = R.color.text_secondary),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
leadingContent = {
|
||||
ListWidgetObjectIcon(icon = item.icon, modifier = Modifier, iconSize = 48.dp)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
private fun ListItemLoading(
|
||||
modifier: Modifier
|
||||
) {
|
||||
ListItem(
|
||||
colors = ListItemDefaults.colors(
|
||||
containerColor = colorResource(id = R.color.background_primary),
|
||||
),
|
||||
modifier = modifier
|
||||
.height(72.dp)
|
||||
.fillMaxWidth(),
|
||||
headlineContent = {
|
||||
ShimmerEffect(
|
||||
modifier = Modifier
|
||||
.width(164.dp)
|
||||
.height(18.dp)
|
||||
)
|
||||
},
|
||||
supportingContent = {
|
||||
ShimmerEffect(
|
||||
modifier = Modifier
|
||||
.width(64.dp)
|
||||
.height(13.dp)
|
||||
)
|
||||
},
|
||||
leadingContent = {
|
||||
ShimmerEffect(
|
||||
modifier = Modifier
|
||||
.size(48.dp)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SwipeToDismissListItems(
|
||||
item: UiObjectsListItem.Item,
|
||||
modifier: Modifier,
|
||||
animationDuration: Int = 500,
|
||||
onDateEvent: (DateEvent) -> Unit,
|
||||
) {
|
||||
var isRemoved by remember { mutableStateOf(false) }
|
||||
val dismissState = rememberSwipeToDismissBoxState(
|
||||
initialValue = SwipeToDismissBoxValue.Settled,
|
||||
confirmValueChange = { value ->
|
||||
if (value == SwipeToDismissBoxValue.EndToStart) {
|
||||
isRemoved = true
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
return@rememberSwipeToDismissBoxState true
|
||||
},
|
||||
positionalThreshold = { it * .5f }
|
||||
)
|
||||
|
||||
if (dismissState.currentValue != SwipeToDismissBoxValue.Settled) {
|
||||
LaunchedEffect(Unit) {
|
||||
dismissState.snapTo(SwipeToDismissBoxValue.Settled)
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(key1 = isRemoved) {
|
||||
if (isRemoved) {
|
||||
delay(animationDuration.toLong())
|
||||
onDateEvent(DateEvent.ObjectsList.OnObjectMoveToBin(item))
|
||||
}
|
||||
}
|
||||
AnimatedVisibility(
|
||||
visible = !isRemoved,
|
||||
exit = shrinkVertically(
|
||||
animationSpec = tween(durationMillis = animationDuration),
|
||||
shrinkTowards = Alignment.Top
|
||||
) + fadeOut()
|
||||
) {
|
||||
SwipeToDismissBox(
|
||||
modifier = modifier,
|
||||
state = dismissState,
|
||||
enableDismissFromEndToStart = item.isPossibleToDelete,
|
||||
enableDismissFromStartToEnd = false,
|
||||
backgroundContent = {
|
||||
DismissBackground(
|
||||
actionText = stringResource(R.string.move_to_bin),
|
||||
dismissState = dismissState
|
||||
)
|
||||
},
|
||||
content = {
|
||||
ListItem(
|
||||
modifier = Modifier
|
||||
.noRippleThrottledClickable {
|
||||
onDateEvent(DateEvent.ObjectsList.OnObjectClicked(item))
|
||||
},
|
||||
item = item
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BoxScope.LoadingState() {
|
||||
val loadingAlpha by animateFloatAsState(targetValue = 1f, label = "")
|
||||
DotsLoadingIndicator(
|
||||
animating = true,
|
||||
modifier = Modifier
|
||||
.graphicsLayer { alpha = loadingAlpha }
|
||||
.align(Alignment.Center),
|
||||
animationSpecs = FadeAnimationSpecs(itemCount = 3),
|
||||
color = colorResource(id = R.color.glyph_active),
|
||||
size = ButtonSize.Small
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@DefaultPreviews
|
||||
fun ObjectsListScreenPreview() {
|
||||
val contentListState = UiObjectsListState(
|
||||
items = StubVerticalItems
|
||||
)
|
||||
ObjectsScreen(
|
||||
state = contentListState,
|
||||
uiState = UiContentState.Idle(scrollToTop = false),
|
||||
canPaginate = true,
|
||||
onDateEvent = {}
|
||||
)
|
||||
}
|
|
@ -4,7 +4,7 @@ import com.anytypeio.anytype.core_models.TimeInMillis
|
|||
import com.anytypeio.anytype.core_models.multiplayer.SpaceSyncAndP2PStatusState
|
||||
import com.anytypeio.anytype.core_models.primitives.TimestampInSeconds
|
||||
import com.anytypeio.anytype.feature_date.viewmodel.UiFieldsItem
|
||||
import com.anytypeio.anytype.feature_date.viewmodel.UiObjectsListItem
|
||||
import com.anytypeio.anytype.presentation.objects.UiObjectsListItem
|
||||
|
||||
sealed class DateEvent {
|
||||
|
||||
|
|
|
@ -1,49 +1,9 @@
|
|||
package com.anytypeio.anytype.feature_date.ui.models
|
||||
|
||||
import com.anytypeio.anytype.core_models.ObjectType
|
||||
import com.anytypeio.anytype.core_models.RelationFormat
|
||||
import com.anytypeio.anytype.core_models.Relations
|
||||
import com.anytypeio.anytype.core_models.primitives.RelationKey
|
||||
import com.anytypeio.anytype.core_models.primitives.SpaceId
|
||||
import com.anytypeio.anytype.feature_date.viewmodel.UiFieldsItem
|
||||
import com.anytypeio.anytype.feature_date.viewmodel.UiObjectsListItem
|
||||
import com.anytypeio.anytype.presentation.objects.ObjectIcon
|
||||
|
||||
val StubVerticalItems = listOf(
|
||||
UiObjectsListItem.Item(
|
||||
id = "1",
|
||||
name = "Task Object",
|
||||
space = SpaceId("space1"),
|
||||
type = "type1",
|
||||
typeName = "Task",
|
||||
createdBy = "by Joseph Wolf",
|
||||
layout = ObjectType.Layout.TODO,
|
||||
icon = ObjectIcon.Task(isChecked = true)
|
||||
),
|
||||
UiObjectsListItem.Item(
|
||||
id = "2",
|
||||
name = "Page Object",
|
||||
space = SpaceId("space2"),
|
||||
type = "type2",
|
||||
typeName = "Page",
|
||||
createdBy = "by Mike Long",
|
||||
layout = ObjectType.Layout.BASIC,
|
||||
icon = ObjectIcon.Empty.Page
|
||||
),
|
||||
UiObjectsListItem.Item(
|
||||
id = "3",
|
||||
name = "File Object",
|
||||
space = SpaceId("space3"),
|
||||
type = "type3",
|
||||
typeName = "File",
|
||||
createdBy = "by John Doe",
|
||||
layout = ObjectType.Layout.FILE,
|
||||
icon = ObjectIcon.File(
|
||||
mime = "image/png",
|
||||
fileName = "test_image.png"
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val StubHorizontalItems = listOf(
|
||||
UiFieldsItem.Settings(),
|
||||
|
|
|
@ -2,7 +2,6 @@ package com.anytypeio.anytype.feature_date.viewmodel
|
|||
|
||||
import com.anytypeio.anytype.core_models.DVSortType
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.ObjectType
|
||||
import com.anytypeio.anytype.core_models.RelationFormat
|
||||
import com.anytypeio.anytype.core_models.RelativeDate
|
||||
import com.anytypeio.anytype.core_models.TimeInMillis
|
||||
|
@ -11,7 +10,6 @@ import com.anytypeio.anytype.core_models.primitives.RelationKey
|
|||
import com.anytypeio.anytype.core_models.primitives.SpaceId
|
||||
import com.anytypeio.anytype.core_models.primitives.TimestampInSeconds
|
||||
import com.anytypeio.anytype.feature_date.viewmodel.UiFieldsItem.Loading
|
||||
import com.anytypeio.anytype.presentation.objects.ObjectIcon
|
||||
|
||||
data class DateObjectVmParams(
|
||||
val objectId: Id,
|
||||
|
@ -100,55 +98,12 @@ sealed class UiFieldsItem {
|
|||
}
|
||||
}
|
||||
|
||||
data class UiObjectsListState(
|
||||
val items: List<UiObjectsListItem>
|
||||
) {
|
||||
companion object {
|
||||
|
||||
val Empty = UiObjectsListState(items = emptyList())
|
||||
val LoadingState = UiObjectsListState(
|
||||
items = listOf(
|
||||
UiObjectsListItem.Loading("Loading-Item-1"),
|
||||
UiObjectsListItem.Loading("Loading-Item-2"),
|
||||
UiObjectsListItem.Loading("Loading-Item-3"),
|
||||
UiObjectsListItem.Loading("Loading-Item-4"),
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
sealed class UiObjectsListItem {
|
||||
|
||||
abstract val id: String
|
||||
|
||||
data class Loading(override val id: String) : UiObjectsListItem()
|
||||
|
||||
data class Item(
|
||||
override val id: String,
|
||||
val name: String,
|
||||
val space: SpaceId,
|
||||
val type: String? = null,
|
||||
val typeName: String? = null,
|
||||
val createdBy: String? = null,
|
||||
val layout: ObjectType.Layout? = null,
|
||||
val icon: ObjectIcon = ObjectIcon.None,
|
||||
val isPossibleToDelete: Boolean = false
|
||||
) : UiObjectsListItem()
|
||||
}
|
||||
|
||||
sealed class UiNavigationWidget {
|
||||
data object Hidden : UiNavigationWidget()
|
||||
data object Editor : UiNavigationWidget()
|
||||
data object Viewer : UiNavigationWidget()
|
||||
}
|
||||
|
||||
sealed class UiContentState {
|
||||
data class Idle(val scrollToTop: Boolean = false) : UiContentState()
|
||||
data object InitLoading : UiContentState()
|
||||
data object Paging : UiContentState()
|
||||
data object Empty : UiContentState()
|
||||
}
|
||||
|
||||
sealed class UiFieldsSheetState {
|
||||
data object Hidden : UiFieldsSheetState()
|
||||
data class Visible(
|
||||
|
|
|
@ -14,6 +14,9 @@ import com.anytypeio.anytype.core_models.TimeInSeconds
|
|||
import com.anytypeio.anytype.core_models.getSingleValue
|
||||
import com.anytypeio.anytype.core_models.primitives.SpaceId
|
||||
import com.anytypeio.anytype.core_models.primitives.TimestampInSeconds
|
||||
import com.anytypeio.anytype.core_ui.lists.objects.UiContentState
|
||||
import com.anytypeio.anytype.core_ui.lists.objects.UiContentState.Idle
|
||||
import com.anytypeio.anytype.core_ui.lists.objects.UiObjectsListState
|
||||
import com.anytypeio.anytype.domain.base.fold
|
||||
import com.anytypeio.anytype.domain.event.interactor.SpaceSyncAndP2PStatusProvider
|
||||
import com.anytypeio.anytype.domain.library.StoreSearchParams
|
||||
|
@ -29,11 +32,9 @@ import com.anytypeio.anytype.domain.objects.StoreOfRelations
|
|||
import com.anytypeio.anytype.domain.page.CreateObject
|
||||
import com.anytypeio.anytype.domain.primitives.FieldParser
|
||||
import com.anytypeio.anytype.domain.relations.GetObjectRelationListById
|
||||
import com.anytypeio.anytype.feature_date.viewmodel.UiErrorState.Reason
|
||||
import com.anytypeio.anytype.feature_date.mapping.toUiFieldsItem
|
||||
import com.anytypeio.anytype.feature_date.mapping.toUiObjectsListItem
|
||||
import com.anytypeio.anytype.feature_date.ui.models.DateEvent
|
||||
import com.anytypeio.anytype.feature_date.viewmodel.UiContentState.*
|
||||
import com.anytypeio.anytype.feature_date.viewmodel.UiErrorState.Reason
|
||||
import com.anytypeio.anytype.presentation.analytics.AnalyticSpaceHelperDelegate
|
||||
import com.anytypeio.anytype.presentation.extension.sendAnalyticsClickDateBack
|
||||
import com.anytypeio.anytype.presentation.extension.sendAnalyticsClickDateCalendarView
|
||||
|
@ -44,7 +45,9 @@ import com.anytypeio.anytype.presentation.extension.sendAnalyticsScreenDate
|
|||
import com.anytypeio.anytype.presentation.extension.sendAnalyticsSwitchRelationDate
|
||||
import com.anytypeio.anytype.presentation.home.OpenObjectNavigation
|
||||
import com.anytypeio.anytype.presentation.home.navigation
|
||||
import com.anytypeio.anytype.presentation.objects.UiObjectsListItem
|
||||
import com.anytypeio.anytype.presentation.objects.getCreateObjectParams
|
||||
import com.anytypeio.anytype.presentation.objects.toUiObjectsListItem
|
||||
import com.anytypeio.anytype.presentation.search.GlobalSearchViewModel.Companion.DEFAULT_DEBOUNCE_DURATION
|
||||
import com.anytypeio.anytype.presentation.sync.SyncStatusWidgetState
|
||||
import com.anytypeio.anytype.presentation.sync.toSyncStatusWidgetState
|
||||
|
|
61
feature-object-type/build.gradle
Normal file
61
feature-object-type/build.gradle
Normal file
|
@ -0,0 +1,61 @@
|
|||
plugins {
|
||||
id "com.android.library"
|
||||
id "kotlin-android"
|
||||
alias(libs.plugins.compose.compiler)
|
||||
}
|
||||
|
||||
android {
|
||||
|
||||
defaultConfig {
|
||||
buildConfigField "boolean", "USE_NEW_WINDOW_INSET_API", "true"
|
||||
buildConfigField "boolean", "USE_EDGE_TO_EDGE", "true"
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
compose true
|
||||
}
|
||||
|
||||
namespace 'com.anytypeio.anytype.feature_object_type'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation project(':domain')
|
||||
implementation project(':core-ui')
|
||||
implementation project(':analytics')
|
||||
implementation project(':core-models')
|
||||
implementation project(':core-utils')
|
||||
implementation project(':localization')
|
||||
implementation project(':presentation')
|
||||
implementation project(':library-emojifier')
|
||||
|
||||
compileOnly libs.javaxInject
|
||||
|
||||
implementation libs.lifecycleViewModel
|
||||
implementation libs.lifecycleRuntime
|
||||
|
||||
implementation libs.appcompat
|
||||
implementation libs.compose
|
||||
implementation libs.composeFoundation
|
||||
implementation libs.composeToolingPreview
|
||||
implementation libs.composeMaterial3
|
||||
implementation libs.composeMaterial
|
||||
implementation libs.navigationCompose
|
||||
implementation libs.composeReorderable
|
||||
|
||||
debugImplementation libs.composeTooling
|
||||
|
||||
implementation libs.timber
|
||||
|
||||
testImplementation project(':test:android-utils')
|
||||
testImplementation project(':test:utils')
|
||||
testImplementation project(":test:core-models-stub")
|
||||
testImplementation libs.junit
|
||||
testImplementation libs.kotlinTest
|
||||
testImplementation libs.robolectric
|
||||
testImplementation libs.androidXTestCore
|
||||
testImplementation libs.mockitoKotlin
|
||||
testImplementation libs.coroutineTesting
|
||||
testImplementation libs.timberJUnit
|
||||
testImplementation libs.turbine
|
||||
}
|
2
feature-object-type/src/main/AndroidManifest.xml
Normal file
2
feature-object-type/src/main/AndroidManifest.xml
Normal file
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest/>
|
|
@ -0,0 +1,52 @@
|
|||
package com.anytypeio.anytype.feature_object_type.fields
|
||||
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.RelationFormat
|
||||
|
||||
sealed class FieldEvent {
|
||||
|
||||
data object OnFieldEditScreenDismiss : FieldEvent()
|
||||
data object OnAddFieldScreenDismiss : FieldEvent()
|
||||
|
||||
data class OnFieldItemClick(val item: UiFieldsListItem) : FieldEvent()
|
||||
|
||||
data class OnAddToHeaderFieldClick(
|
||||
val item: UiAddFieldItem
|
||||
) : FieldEvent()
|
||||
|
||||
data class OnAddToSidebarFieldClick(
|
||||
val item: UiAddFieldItem
|
||||
) : FieldEvent()
|
||||
|
||||
data class OnSaveButtonClicked(
|
||||
val name: String,
|
||||
val format: RelationFormat,
|
||||
val limitObjectTypes: List<Id>
|
||||
) : FieldEvent()
|
||||
|
||||
data object OnChangeTypeClick : FieldEvent()
|
||||
data object OnLimitTypesClick : FieldEvent()
|
||||
|
||||
sealed class FieldItemMenu : FieldEvent() {
|
||||
data class OnDeleteFromTypeClick(val item: UiFieldsListItem) : FieldItemMenu()
|
||||
data class OnRemoveLocalClick(val item: UiFieldsListItem) : FieldItemMenu()
|
||||
data class OnAddLocalToTypeClick(val item: UiFieldsListItem) : FieldItemMenu()
|
||||
}
|
||||
|
||||
sealed class FieldLocalInfo : FieldEvent() {
|
||||
data object OnDismiss : FieldLocalInfo()
|
||||
}
|
||||
|
||||
sealed class Section : FieldEvent() {
|
||||
data object OnAddToHeaderIconClick : Section()
|
||||
data object OnAddToSidebarIconClick : Section()
|
||||
data object OnLocalInfoClick : Section()
|
||||
}
|
||||
|
||||
sealed class DragEvent : FieldEvent() {
|
||||
data class OnMove(val fromKey: String, val toKey: String) : DragEvent()
|
||||
data object OnDragEnd : DragEvent()
|
||||
}
|
||||
|
||||
data class OnAddFieldSearchQueryChanged(val query: String) : FieldEvent()
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue