mirror of
https://github.com/anyproto/anytype-kotlin.git
synced 2025-06-07 21:37:02 +09:00
DROID-3429 Primitives | Edit type properties, part 1 (#2138)
This commit is contained in:
parent
b566ba30e0
commit
3932c2fe87
42 changed files with 750 additions and 1142 deletions
|
@ -177,6 +177,7 @@ dependencies {
|
|||
implementation project(':feature-all-content')
|
||||
implementation project(':feature-date')
|
||||
implementation project(':feature-object-type')
|
||||
implementation project(':feature-properties')
|
||||
|
||||
//Compile time dependencies
|
||||
ksp libs.daggerCompiler
|
||||
|
|
|
@ -9,6 +9,7 @@ import com.anytypeio.anytype.di.feature.DaggerAllContentComponent
|
|||
import com.anytypeio.anytype.di.feature.DaggerAppPreferencesComponent
|
||||
import com.anytypeio.anytype.di.feature.DaggerBacklinkOrAddToObjectComponent
|
||||
import com.anytypeio.anytype.di.feature.DaggerDateObjectComponent
|
||||
import com.anytypeio.anytype.di.feature.DaggerEditTypePropertiesComponent
|
||||
import com.anytypeio.anytype.di.feature.DaggerLinkToObjectComponent
|
||||
import com.anytypeio.anytype.di.feature.DaggerMoveToComponent
|
||||
import com.anytypeio.anytype.di.feature.DaggerObjectTypeComponent
|
||||
|
@ -109,6 +110,7 @@ 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.feature_properties.add.EditTypePropertiesVmParams
|
||||
import com.anytypeio.anytype.gallery_experience.viewmodel.GalleryInstallationViewModel
|
||||
import com.anytypeio.anytype.presentation.editor.EditorViewModel
|
||||
import com.anytypeio.anytype.presentation.history.VersionHistoryViewModel
|
||||
|
@ -1141,6 +1143,12 @@ class ComponentManager(
|
|||
.create(params, findComponentDependencies())
|
||||
}
|
||||
|
||||
val editTypePropertiesComponent = ComponentWithParams { params: EditTypePropertiesVmParams ->
|
||||
DaggerEditTypePropertiesComponent
|
||||
.factory()
|
||||
.create(params, findComponentDependencies())
|
||||
}
|
||||
|
||||
class Component<T>(private val builder: () -> T) {
|
||||
|
||||
private var instance: T? = null
|
||||
|
|
|
@ -5,14 +5,12 @@ 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
|
||||
|
@ -22,15 +20,14 @@ 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.feature_object_type.viewmodel.ObjectTypeVMFactory
|
||||
import com.anytypeio.anytype.presentation.analytics.AnalyticSpaceHelperDelegate
|
||||
import com.anytypeio.anytype.presentation.editor.cover.CoverImageHashProvider
|
||||
import com.anytypeio.anytype.providers.DefaultCoverImageHashProvider
|
||||
|
@ -81,19 +78,6 @@ object ObjectTypeModule {
|
|||
logger = logger
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@PerScreen
|
||||
fun createObject(
|
||||
repo: BlockRepository,
|
||||
getDefaultObjectType: GetDefaultObjectType,
|
||||
dispatchers: AppCoroutineDispatchers,
|
||||
): CreateObject = CreateObject(
|
||||
repo = repo,
|
||||
getDefaultObjectType = getDefaultObjectType,
|
||||
dispatchers = dispatchers
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@PerScreen
|
||||
|
@ -123,13 +107,6 @@ object ObjectTypeModule {
|
|||
dispatchers: AppCoroutineDispatchers
|
||||
): GetObjectTypeConflictingFields = GetObjectTypeConflictingFields(repo, dispatchers)
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@PerScreen
|
||||
fun provideCreateObjectSetUseCase(
|
||||
repo: BlockRepository
|
||||
): CreateObjectSet = CreateObjectSet(repo = repo)
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@PerScreen
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
package com.anytypeio.anytype.di.feature
|
||||
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.anytypeio.anytype.core_utils.di.scope.PerModal
|
||||
import com.anytypeio.anytype.di.common.ComponentDependencies
|
||||
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
|
||||
import com.anytypeio.anytype.domain.block.repo.BlockRepository
|
||||
import com.anytypeio.anytype.domain.`object`.SetObjectDetails
|
||||
import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
|
||||
import com.anytypeio.anytype.domain.objects.StoreOfRelations
|
||||
import com.anytypeio.anytype.domain.primitives.SetObjectTypeRecommendedFields
|
||||
import com.anytypeio.anytype.domain.relations.CreateRelation
|
||||
import com.anytypeio.anytype.domain.resources.StringResourceProvider
|
||||
import com.anytypeio.anytype.feature_properties.EditTypePropertiesViewModelFactory
|
||||
import com.anytypeio.anytype.feature_properties.add.EditTypePropertiesVmParams
|
||||
import com.anytypeio.anytype.ui.primitives.EditTypePropertiesFragment
|
||||
import dagger.Binds
|
||||
import dagger.BindsInstance
|
||||
import dagger.Component
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
||||
//region EDIT OBJECT TYPE PROPERTIES SCREEN
|
||||
@PerModal
|
||||
@Component(
|
||||
modules = [
|
||||
EditTypePropertiesModule::class,
|
||||
EditTypePropertiesModule.Declarations::class
|
||||
],
|
||||
dependencies = [EditTypePropertiesDependencies::class]
|
||||
)
|
||||
interface EditTypePropertiesComponent {
|
||||
@Component.Factory
|
||||
interface Factory {
|
||||
fun create(
|
||||
@BindsInstance vmParams: EditTypePropertiesVmParams,
|
||||
dependencies: EditTypePropertiesDependencies
|
||||
): EditTypePropertiesComponent
|
||||
}
|
||||
|
||||
fun inject(fragment: EditTypePropertiesFragment)
|
||||
}
|
||||
|
||||
@Module
|
||||
object EditTypePropertiesModule {
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@PerModal
|
||||
fun provideTypeSetRecommendedFields(
|
||||
repo: BlockRepository,
|
||||
dispatchers: AppCoroutineDispatchers
|
||||
): SetObjectTypeRecommendedFields = SetObjectTypeRecommendedFields(repo, dispatchers)
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@PerModal
|
||||
fun createRelation(
|
||||
repo: BlockRepository,
|
||||
storeOfRelations: StoreOfRelations,
|
||||
) = CreateRelation(repo, storeOfRelations)
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@PerModal
|
||||
fun provideSetObjectDetails(
|
||||
repo: BlockRepository,
|
||||
dispatchers: AppCoroutineDispatchers
|
||||
): SetObjectDetails = SetObjectDetails(repo, dispatchers)
|
||||
|
||||
@Module
|
||||
interface Declarations {
|
||||
@PerModal
|
||||
@Binds
|
||||
fun bindViewModelFactory(
|
||||
factory: EditTypePropertiesViewModelFactory
|
||||
): ViewModelProvider.Factory
|
||||
}
|
||||
}
|
||||
|
||||
interface EditTypePropertiesDependencies : ComponentDependencies {
|
||||
fun provideStringResourceProvider(): StringResourceProvider
|
||||
fun provideStoreOfRelations(): StoreOfRelations
|
||||
fun provideStoreOfObjectTypes(): StoreOfObjectTypes
|
||||
fun provideBlockRepository(): BlockRepository
|
||||
fun provideAppCoroutineDispatchers(): AppCoroutineDispatchers
|
||||
}
|
||||
//endregion
|
|
@ -3,6 +3,7 @@ package com.anytypeio.anytype.di.main
|
|||
import com.anytypeio.anytype.app.AndroidApplication
|
||||
import com.anytypeio.anytype.di.common.ComponentDependencies
|
||||
import com.anytypeio.anytype.di.common.ComponentDependenciesKey
|
||||
import com.anytypeio.anytype.di.feature.EditTypePropertiesDependencies
|
||||
import com.anytypeio.anytype.di.feature.AllContentDependencies
|
||||
import com.anytypeio.anytype.di.feature.AppPreferencesDependencies
|
||||
import com.anytypeio.anytype.di.feature.BacklinkOrAddToObjectDependencies
|
||||
|
@ -143,6 +144,7 @@ interface MainComponent :
|
|||
SelectChatReactionDependencies,
|
||||
ChatReactionDependencies,
|
||||
ParticipantComponentDependencies,
|
||||
EditTypePropertiesDependencies,
|
||||
DebugDependencies
|
||||
{
|
||||
|
||||
|
@ -414,4 +416,9 @@ abstract class ComponentDependenciesModule {
|
|||
@IntoMap
|
||||
@ComponentDependenciesKey(DebugDependencies::class)
|
||||
abstract fun provideDebugDependencies(component: MainComponent): ComponentDependencies
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ComponentDependenciesKey(EditTypePropertiesDependencies::class)
|
||||
abstract fun provideEditTypePropertiesDependencies(component: MainComponent): ComponentDependencies
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
package com.anytypeio.anytype.ui.primitives
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.fragment.compose.content
|
||||
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.ui.BaseBottomSheetComposeFragment
|
||||
import com.anytypeio.anytype.di.common.componentManager
|
||||
import com.anytypeio.anytype.feature_properties.EditTypePropertiesViewModel
|
||||
import com.anytypeio.anytype.feature_properties.EditTypePropertiesViewModelFactory
|
||||
import com.anytypeio.anytype.feature_properties.add.EditTypePropertiesVmParams
|
||||
import javax.inject.Inject
|
||||
|
||||
class EditTypePropertiesFragment : BaseBottomSheetComposeFragment() {
|
||||
|
||||
@Inject
|
||||
lateinit var viewModelFactory: EditTypePropertiesViewModelFactory
|
||||
private val vm by viewModels<EditTypePropertiesViewModel> { viewModelFactory }
|
||||
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 {
|
||||
}
|
||||
}
|
||||
|
||||
override fun injectDependencies() {
|
||||
val params = EditTypePropertiesVmParams(
|
||||
objectTypeId = typeId,
|
||||
spaceId = SpaceId(space)
|
||||
|
||||
)
|
||||
componentManager().editTypePropertiesComponent.get(params).inject(this)
|
||||
}
|
||||
|
||||
override fun releaseDependencies() {
|
||||
componentManager().editTypePropertiesComponent.release()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun args(objectId: Id, space: Id) = bundleOf(
|
||||
ARG_OBJECT_ID to objectId,
|
||||
ARG_SPACE to space
|
||||
)
|
||||
|
||||
const val ARG_OBJECT_ID = "arg.primitives.edit.type.property.object.id"
|
||||
const val ARG_SPACE = "arg.primitives.edit.type.property.space"
|
||||
|
||||
const val DEFAULT_PADDING_TOP = 10
|
||||
}
|
||||
}
|
|
@ -302,11 +302,11 @@ class ObjectFieldsFragment : BaseBottomSheetComposeFragment(),
|
|||
)
|
||||
}
|
||||
|
||||
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 ARG_CTX = "arg.primitives.properties.ctx"
|
||||
const val ARG_SPACE = "arg.primitives.properties.space"
|
||||
const val ARG_TARGET = "arg.primitives.properties.target"
|
||||
const val ARG_LOCKED = "arg.primitives.properties.locked"
|
||||
const val ARG_SET_FLOW = "arg.primitives.properties.set_flow"
|
||||
|
||||
const val DEFAULT_PADDING_TOP = 10
|
||||
}
|
||||
|
|
|
@ -44,9 +44,8 @@ class ObjectTypeFieldsFragment : BaseBottomSheetComposeFragment() {
|
|||
uiFieldsListState = vm.uiFieldsListState.collectAsStateWithLifecycle().value,
|
||||
uiTitleState = vm.uiTitleState.collectAsStateWithLifecycle().value,
|
||||
uiIconState = vm.uiIconState.collectAsStateWithLifecycle().value,
|
||||
uiFieldEditOrNewState = vm.uiFieldEditOrNewState.collectAsStateWithLifecycle().value,
|
||||
uiEditPropertyState = vm.uiEditPropertyScreen.collectAsStateWithLifecycle().value,
|
||||
uiFieldLocalInfoState = vm.uiFieldLocalInfoState.collectAsStateWithLifecycle().value,
|
||||
uiAddFieldsScreenState = vm.uiAddFieldsState.collectAsStateWithLifecycle().value,
|
||||
fieldEvent = vm::onFieldEvent
|
||||
)
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ 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
|
||||
|
@ -25,7 +24,6 @@ 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.fields.ui.FieldsMainScreen
|
||||
|
@ -34,14 +32,7 @@ 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.viewmodel.ObjectTypeVMFactory
|
||||
import com.anytypeio.anytype.feature_object_type.viewmodel.ObjectTypeViewModel
|
||||
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
|
||||
|
@ -55,7 +46,6 @@ import timber.log.Timber
|
|||
class ObjectTypeFragment : BaseComposeFragment() {
|
||||
@Inject
|
||||
lateinit var factory: ObjectTypeVMFactory
|
||||
|
||||
private val vm by viewModels<ObjectTypeViewModel> { factory }
|
||||
private lateinit var navComposeController: NavHostController
|
||||
|
||||
|
@ -121,12 +111,18 @@ class ObjectTypeFragment : BaseComposeFragment() {
|
|||
navComposeController.navigate(OBJ_TYPE_FIELDS)
|
||||
}
|
||||
|
||||
is ObjectTypeCommand.OpenAddFieldScreen -> {
|
||||
RelationAddToObjectFragment.new(
|
||||
ctx = command.typeId,
|
||||
space = command.space,
|
||||
isSetOrCollection = command.isSet
|
||||
).showChildFragment()
|
||||
is ObjectTypeCommand.OpenEditTypePropertiesScreen -> {
|
||||
runCatching {
|
||||
findNavController().navigate(
|
||||
R.id.editTypePropertiesScreen,
|
||||
EditTypePropertiesFragment.args(
|
||||
objectId = command.typeId,
|
||||
space = command.space
|
||||
)
|
||||
)
|
||||
}.onFailure {
|
||||
Timber.e(it, "Error while opening edit object type properties screen")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -174,88 +170,12 @@ class ObjectTypeFragment : BaseComposeFragment() {
|
|||
uiFieldsListState = vm.uiFieldsListState.collectAsStateWithLifecycle().value,
|
||||
uiTitleState = vm.uiTitleState.collectAsStateWithLifecycle().value,
|
||||
uiIconState = vm.uiIconState.collectAsStateWithLifecycle().value,
|
||||
uiFieldEditOrNewState = vm.uiFieldEditOrNewState.collectAsStateWithLifecycle().value,
|
||||
uiEditPropertyState = vm.uiEditPropertyScreen.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)
|
||||
|
@ -282,15 +202,6 @@ class ObjectTypeFragment : BaseComposeFragment() {
|
|||
}
|
||||
}
|
||||
}
|
||||
when (val state = errorStateScreen) {
|
||||
UiErrorState.Hidden -> {
|
||||
|
||||
}
|
||||
|
||||
is UiErrorState.Show -> {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun injectDependencies() {
|
||||
|
|
|
@ -249,6 +249,10 @@
|
|||
app:destination="@id/typeSetIconPickerScreen" />
|
||||
</fragment>
|
||||
|
||||
<dialog
|
||||
android:id="@+id/editTypePropertiesScreen"
|
||||
android:name="com.anytypeio.anytype.ui.primitives.EditTypePropertiesFragment"/>
|
||||
|
||||
<dialog
|
||||
android:id="@+id/selectWidgetSourceScreen"
|
||||
android:name="com.anytypeio.anytype.ui.widgets.SelectWidgetSourceFragment" />
|
||||
|
|
|
@ -5,10 +5,42 @@ import androidx.compose.foundation.layout.ime
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.composed
|
||||
import androidx.compose.ui.draw.drawBehind
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.anytypeio.anytype.core_ui.R
|
||||
|
||||
@Composable
|
||||
fun keyboardAsState(): State<Boolean> {
|
||||
val isImeVisible = WindowInsets.ime.getBottom(LocalDensity.current) > 0
|
||||
return rememberUpdatedState(isImeVisible)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Modifier.bottomBorder(
|
||||
strokeWidth: Dp = 0.5.dp,
|
||||
color: Color = colorResource(R.color.shape_primary)
|
||||
) = composed(
|
||||
factory = {
|
||||
val density = LocalDensity.current
|
||||
val strokeWidthPx = density.run { strokeWidth.toPx() }
|
||||
|
||||
Modifier.drawBehind {
|
||||
val width = size.width
|
||||
val height = size.height - strokeWidthPx / 2
|
||||
|
||||
drawLine(
|
||||
color = color,
|
||||
start = Offset(x = 0f, y = height),
|
||||
end = Offset(x = width, y = height),
|
||||
strokeWidth = strokeWidthPx
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
|
@ -278,8 +278,7 @@ fun ObjectLayoutView.getName(): Int? = when (this) {
|
|||
|
||||
@StringRes
|
||||
fun RelationFormat.getPrettyName(): Int = when (this) {
|
||||
RelationFormat.LONG_TEXT -> R.string.relation_format_long_text
|
||||
RelationFormat.SHORT_TEXT -> R.string.relation_format_short_text
|
||||
RelationFormat.LONG_TEXT, RelationFormat.SHORT_TEXT -> R.string.relation_format_long_text
|
||||
RelationFormat.NUMBER -> R.string.relation_format_number
|
||||
RelationFormat.STATUS -> R.string.relation_format_status
|
||||
RelationFormat.TAG -> R.string.relation_format_tag
|
||||
|
|
|
@ -48,6 +48,7 @@ import com.anytypeio.anytype.core_ui.R
|
|||
@Composable
|
||||
fun DefaultSearchBar(
|
||||
modifier: Modifier = Modifier,
|
||||
hint: Int = R.string.search,
|
||||
onQueryChanged: (String) -> Unit
|
||||
) {
|
||||
|
||||
|
@ -80,7 +81,7 @@ fun DefaultSearchBar(
|
|||
modifier = Modifier
|
||||
.align(Alignment.CenterVertically)
|
||||
.padding(
|
||||
start = 11.dp
|
||||
start = 10.dp
|
||||
)
|
||||
)
|
||||
CompositionLocalProvider(value = LocalTextSelectionColors provides selectionColors) {
|
||||
|
@ -117,9 +118,9 @@ fun DefaultSearchBar(
|
|||
interactionSource = interactionSource,
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringResource(id = R.string.search),
|
||||
text = stringResource(id = hint),
|
||||
style = BodyRegular.copy(
|
||||
color = colorResource(id = R.color.text_tertiary)
|
||||
color = colorResource(id = R.color.glyph_active)
|
||||
)
|
||||
)
|
||||
},
|
||||
|
|
|
@ -209,18 +209,30 @@ class FieldParserImpl @Inject constructor(
|
|||
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
|
||||
)
|
||||
// Clean recommended IDs based on priority.
|
||||
// recommendedFeaturedRelations always remain.
|
||||
val featuredIds = objType.recommendedFeaturedRelations.distinct()
|
||||
|
||||
// recommendedRelations: remove any ids that appear in featuredIds.
|
||||
val relationsIds = objType.recommendedRelations
|
||||
.filter { it !in featuredIds }
|
||||
.distinct()
|
||||
|
||||
// recommendedFileRelations: remove ids that are in featuredIds or relationsIds.
|
||||
val fileIds = objType.recommendedFileRelations
|
||||
.filter { it !in featuredIds && it !in relationsIds }
|
||||
.distinct()
|
||||
|
||||
// recommendedHiddenRelations: remove ids that are in featuredIds, relationsIds, or fileIds.
|
||||
val hiddenIds = objType.recommendedHiddenRelations
|
||||
.filter { it !in featuredIds && it !in relationsIds && it !in fileIds }
|
||||
.distinct()
|
||||
|
||||
// Fetch valid relations for each recommended group.
|
||||
val headerFields = storeOfRelations.getValidRelations(ids = featuredIds)
|
||||
val sidebarFields = storeOfRelations.getValidRelations(ids = relationsIds)
|
||||
val fileFields = storeOfRelations.getValidRelations(ids = fileIds)
|
||||
val hiddenFields = storeOfRelations.getValidRelations(ids = hiddenIds)
|
||||
|
||||
// Combine IDs from all recommended relations.
|
||||
val existingIds = (headerFields + sidebarFields + hiddenFields + fileFields)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.anytypeio.anytype.domain.resources
|
||||
|
||||
import com.anytypeio.anytype.core_models.RelationFormat
|
||||
import com.anytypeio.anytype.core_models.RelativeDate
|
||||
|
||||
interface StringResourceProvider {
|
||||
|
@ -7,4 +8,5 @@ interface StringResourceProvider {
|
|||
fun getDeletedObjectTitle(): String
|
||||
fun getUntitledObjectTitle(): String
|
||||
fun getSetOfObjectsTitle(): String
|
||||
fun getPropertiesFormatPrettyString(format: RelationFormat): String
|
||||
}
|
|
@ -52,10 +52,6 @@ import androidx.compose.runtime.setValue
|
|||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Alignment.Companion.CenterVertically
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.composed
|
||||
import androidx.compose.ui.draw.drawBehind
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.colorResource
|
||||
|
@ -63,11 +59,11 @@ import androidx.compose.ui.res.painterResource
|
|||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.primitives.SpaceId
|
||||
import com.anytypeio.anytype.core_ui.common.DefaultPreviews
|
||||
import com.anytypeio.anytype.core_ui.common.bottomBorder
|
||||
import com.anytypeio.anytype.core_ui.extensions.simpleIcon
|
||||
import com.anytypeio.anytype.core_ui.extensions.swapList
|
||||
import com.anytypeio.anytype.core_ui.foundation.DefaultSearchBar
|
||||
|
@ -89,7 +85,6 @@ import com.anytypeio.anytype.core_ui.widgets.ListWidgetObjectIcon
|
|||
import com.anytypeio.anytype.core_utils.insets.EDGE_TO_EDGE_MIN_SDK
|
||||
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.AllContentTab
|
||||
import com.anytypeio.anytype.feature_allcontent.models.UiContentItem
|
||||
|
@ -824,29 +819,6 @@ object AllContentNavigation {
|
|||
const val ALL_CONTENT_MAIN = "all_content_main"
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Modifier.bottomBorder(
|
||||
strokeWidth: Dp = 0.5.dp,
|
||||
color: Color = colorResource(R.color.shape_primary)
|
||||
) = composed(
|
||||
factory = {
|
||||
val density = LocalDensity.current
|
||||
val strokeWidthPx = density.run { strokeWidth.toPx() }
|
||||
|
||||
Modifier.drawBehind {
|
||||
val width = size.width
|
||||
val height = size.height - strokeWidthPx / 2
|
||||
|
||||
drawLine(
|
||||
color = color,
|
||||
start = Offset(x = 0f, y = height),
|
||||
end = Offset(x = width, y = height),
|
||||
strokeWidth = strokeWidthPx
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun SwipeToDismissListItems(
|
||||
item: UiContentItem.Item,
|
||||
|
|
|
@ -28,6 +28,7 @@ dependencies {
|
|||
implementation project(':localization')
|
||||
implementation project(':presentation')
|
||||
implementation project(':library-emojifier')
|
||||
implementation project(':feature-properties')
|
||||
|
||||
compileOnly libs.javaxInject
|
||||
|
||||
|
|
|
@ -1,35 +1,13 @@
|
|||
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 object OnEditPropertyScreenDismiss : 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()
|
||||
}
|
||||
|
||||
|
@ -38,7 +16,6 @@ sealed class FieldEvent {
|
|||
}
|
||||
|
||||
sealed class Section : FieldEvent() {
|
||||
data object OnAddToHeaderIconClick : Section()
|
||||
data object OnAddToSidebarIconClick : Section()
|
||||
data object OnLocalInfoClick : Section()
|
||||
}
|
||||
|
@ -47,6 +24,4 @@ sealed class FieldEvent {
|
|||
data class OnMove(val fromKey: String, val toKey: String) : DragEvent()
|
||||
data object OnDragEnd : DragEvent()
|
||||
}
|
||||
|
||||
data class OnAddFieldSearchQueryChanged(val query: String) : FieldEvent()
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package com.anytypeio.anytype.feature_object_type.fields
|
|||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.Key
|
||||
import com.anytypeio.anytype.core_models.RelationFormat
|
||||
import com.anytypeio.anytype.feature_properties.edit.UiPropertyLimitTypeItem
|
||||
import com.anytypeio.anytype.presentation.objects.ObjectIcon
|
||||
|
||||
//region Top bar
|
||||
|
@ -34,6 +35,7 @@ data class UiFieldsListState(val items: List<UiFieldsListItem>) {
|
|||
}
|
||||
}
|
||||
|
||||
//todo rename to UiPropertiesListItem
|
||||
sealed class UiFieldsListItem {
|
||||
abstract val id: Id
|
||||
|
||||
|
@ -41,7 +43,7 @@ sealed class UiFieldsListItem {
|
|||
abstract val fieldKey: Key
|
||||
abstract val fieldTitle: String
|
||||
abstract val format: RelationFormat
|
||||
abstract val limitObjectTypes: List<UiFieldObjectItem>
|
||||
abstract val limitObjectTypes: List<UiPropertyLimitTypeItem>
|
||||
abstract val canDelete: Boolean
|
||||
abstract val isEditableField: Boolean
|
||||
|
||||
|
@ -50,7 +52,7 @@ sealed class UiFieldsListItem {
|
|||
override val fieldKey: Key,
|
||||
override val fieldTitle: String,
|
||||
override val format: RelationFormat,
|
||||
override val limitObjectTypes: List<UiFieldObjectItem> = emptyList(),
|
||||
override val limitObjectTypes: List<UiPropertyLimitTypeItem> = emptyList(),
|
||||
override val canDelete: Boolean,
|
||||
override val isEditableField: Boolean
|
||||
) : Item()
|
||||
|
@ -60,7 +62,7 @@ sealed class UiFieldsListItem {
|
|||
override val fieldKey: Key,
|
||||
override val fieldTitle: String,
|
||||
override val format: RelationFormat,
|
||||
override val limitObjectTypes: List<UiFieldObjectItem> = emptyList(),
|
||||
override val limitObjectTypes: List<UiPropertyLimitTypeItem> = emptyList(),
|
||||
override val canDelete: Boolean = false,
|
||||
override val isEditableField: Boolean
|
||||
) : Item()
|
||||
|
@ -186,18 +188,4 @@ sealed class UiLocalsFieldsInfoState {
|
|||
}
|
||||
//endregion
|
||||
|
||||
//region Add Fields screen
|
||||
sealed class UiAddFieldsScreenState {
|
||||
data object Hidden : UiAddFieldsScreenState()
|
||||
data class Visible(val items: List<UiAddFieldItem>, val addToHeader: Boolean) : UiAddFieldsScreenState()
|
||||
}
|
||||
|
||||
data class UiAddFieldItem(
|
||||
val id: Id,
|
||||
val fieldKey: Key,
|
||||
val fieldTitle: String,
|
||||
val format: RelationFormat
|
||||
)
|
||||
//endregion
|
||||
|
||||
|
||||
|
|
|
@ -1,170 +0,0 @@
|
|||
package com.anytypeio.anytype.feature_object_type.fields.ui
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.safeDrawing
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
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.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.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.anytypeio.anytype.core_models.RelationFormat
|
||||
import com.anytypeio.anytype.core_ui.R
|
||||
import com.anytypeio.anytype.core_ui.common.DefaultPreviews
|
||||
import com.anytypeio.anytype.core_ui.extensions.simpleIcon
|
||||
import com.anytypeio.anytype.core_ui.foundation.DefaultSearchBar
|
||||
import com.anytypeio.anytype.core_ui.foundation.noRippleThrottledClickable
|
||||
import com.anytypeio.anytype.core_ui.views.BodyRegular
|
||||
import com.anytypeio.anytype.core_ui.widgets.dv.DragHandle
|
||||
import com.anytypeio.anytype.feature_object_type.fields.FieldEvent
|
||||
import com.anytypeio.anytype.feature_object_type.fields.UiAddFieldItem
|
||||
import com.anytypeio.anytype.feature_object_type.fields.UiAddFieldsScreenState
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun AddFieldScreen(
|
||||
state: UiAddFieldsScreenState,
|
||||
fieldEvent: (FieldEvent) -> Unit
|
||||
) {
|
||||
val bottomSheetState = rememberModalBottomSheetState(
|
||||
skipPartiallyExpanded = true
|
||||
)
|
||||
|
||||
var isSearchEmpty by remember { mutableStateOf(true) }
|
||||
|
||||
val lazyListState = rememberLazyListState()
|
||||
|
||||
if (state is UiAddFieldsScreenState.Visible) {
|
||||
ModalBottomSheet(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.windowInsetsPadding(WindowInsets.safeDrawing)
|
||||
.nestedScroll(rememberNestedScrollInteropConnection()),
|
||||
dragHandle = { DragHandle() },
|
||||
scrimColor = colorResource(id = R.color.modal_screen_outside_background),
|
||||
containerColor = colorResource(id = R.color.background_primary),
|
||||
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp),
|
||||
sheetState = bottomSheetState,
|
||||
onDismissRequest = {
|
||||
fieldEvent(FieldEvent.OnAddFieldScreenDismiss)
|
||||
},
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxSize(),
|
||||
state = lazyListState
|
||||
) {
|
||||
item {
|
||||
DefaultSearchBar(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
.padding(horizontal = 20.dp)
|
||||
) {
|
||||
isSearchEmpty = it.isEmpty()
|
||||
fieldEvent(FieldEvent.OnAddFieldSearchQueryChanged(it))
|
||||
}
|
||||
}
|
||||
items(
|
||||
count = state.items.size,
|
||||
key = { index -> state.items[index].id },
|
||||
itemContent = { index ->
|
||||
val item = state.items[index]
|
||||
FieldItem(
|
||||
modifier = commonItemModifier()
|
||||
.noRippleThrottledClickable {
|
||||
if (state.addToHeader) {
|
||||
fieldEvent(
|
||||
FieldEvent.OnAddToHeaderFieldClick(
|
||||
item = item
|
||||
)
|
||||
)
|
||||
} else {
|
||||
fieldEvent(
|
||||
FieldEvent.OnAddToSidebarFieldClick(
|
||||
item = item
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
item = item
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
private fun FieldItem(
|
||||
modifier: Modifier,
|
||||
item: UiAddFieldItem
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier,
|
||||
verticalAlignment = CenterVertically
|
||||
) {
|
||||
val formatIcon = item.format.simpleIcon()
|
||||
if (formatIcon != null) {
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.padding(end = 10.dp)
|
||||
.size(24.dp),
|
||||
painter = painterResource(id = formatIcon),
|
||||
contentDescription = "Relation format icon",
|
||||
)
|
||||
}
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1.0f)
|
||||
.padding(end = 16.dp),
|
||||
text = item.fieldTitle,
|
||||
style = BodyRegular,
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@DefaultPreviews
|
||||
@Composable
|
||||
fun PreviewAddFieldScreen() {
|
||||
AddFieldScreen(
|
||||
state = UiAddFieldsScreenState.Visible(
|
||||
items = listOf(
|
||||
UiAddFieldItem(
|
||||
id = "1",
|
||||
fieldKey = "key",
|
||||
fieldTitle = "Title",
|
||||
format = RelationFormat.LONG_TEXT
|
||||
)
|
||||
),
|
||||
addToHeader = true
|
||||
),
|
||||
fieldEvent = {}
|
||||
)
|
||||
}
|
|
@ -1,386 +0,0 @@
|
|||
package com.anytypeio.anytype.feature_object_type.fields.ui
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
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
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.focus.onFocusChanged
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.platform.SoftwareKeyboardController
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.anytypeio.anytype.core_models.RelationFormat
|
||||
import com.anytypeio.anytype.core_ui.R
|
||||
import com.anytypeio.anytype.core_ui.common.DefaultPreviews
|
||||
import com.anytypeio.anytype.core_ui.extensions.getPrettyName
|
||||
import com.anytypeio.anytype.core_ui.extensions.simpleIcon
|
||||
import com.anytypeio.anytype.core_ui.foundation.Divider
|
||||
import com.anytypeio.anytype.core_ui.foundation.noRippleThrottledClickable
|
||||
import com.anytypeio.anytype.core_ui.views.BodyRegular
|
||||
import com.anytypeio.anytype.core_ui.views.ButtonPrimary
|
||||
import com.anytypeio.anytype.core_ui.views.ButtonSize
|
||||
import com.anytypeio.anytype.core_ui.views.Caption1Regular
|
||||
import com.anytypeio.anytype.core_ui.views.HeadlineHeading
|
||||
import com.anytypeio.anytype.core_ui.views.Title2
|
||||
import com.anytypeio.anytype.core_ui.widgets.ListWidgetObjectIcon
|
||||
import com.anytypeio.anytype.core_ui.widgets.dv.DragHandle
|
||||
import com.anytypeio.anytype.feature_object_type.fields.FieldEvent
|
||||
import com.anytypeio.anytype.feature_object_type.fields.UiFieldEditOrNewState
|
||||
import com.anytypeio.anytype.feature_object_type.fields.UiFieldObjectItem
|
||||
import com.anytypeio.anytype.feature_object_type.ui.createDummyFieldDraggableItem
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun EditFieldScreen(
|
||||
modifier: Modifier,
|
||||
uiFieldEditOrNewState: UiFieldEditOrNewState,
|
||||
fieldEvent: (FieldEvent) -> Unit
|
||||
) {
|
||||
if (uiFieldEditOrNewState is UiFieldEditOrNewState.Visible) {
|
||||
val bottomSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||
ModalBottomSheet(
|
||||
modifier = modifier,
|
||||
dragHandle = { DragHandle() },
|
||||
scrimColor = colorResource(id = R.color.modal_screen_outside_background),
|
||||
containerColor = colorResource(id = R.color.background_primary),
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
sheetState = bottomSheetState,
|
||||
onDismissRequest = { fieldEvent(FieldEvent.OnFieldEditScreenDismiss) },
|
||||
) {
|
||||
EditFieldContent(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
uiState = uiFieldEditOrNewState,
|
||||
fieldEvent = fieldEvent
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun EditFieldContent(
|
||||
modifier: Modifier,
|
||||
uiState: UiFieldEditOrNewState.Visible,
|
||||
fieldEvent: (FieldEvent) -> Unit
|
||||
) {
|
||||
|
||||
val field = uiState.item
|
||||
var innerValue by remember(field.fieldTitle) { mutableStateOf(field.fieldTitle) }
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
val isEditable = field.isEditableField
|
||||
|
||||
val title = when (uiState) {
|
||||
is UiFieldEditOrNewState.Visible.Edit -> stringResource(R.string.object_type_fields_edit_field)
|
||||
is UiFieldEditOrNewState.Visible.New -> stringResource(R.string.object_type_fields_new_field)
|
||||
is UiFieldEditOrNewState.Visible.ViewOnly -> stringResource(R.string.object_type_fields_preview_field)
|
||||
}
|
||||
|
||||
Column(modifier = modifier) {
|
||||
// Header title
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(44.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
text = title,
|
||||
style = Title2,
|
||||
color = colorResource(id = R.color.text_primary)
|
||||
)
|
||||
}
|
||||
|
||||
// Name text field
|
||||
NameTextField(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value = innerValue,
|
||||
isEditable = isEditable,
|
||||
focusRequester = focusRequester,
|
||||
keyboardController = keyboardController,
|
||||
onValueChange = { innerValue = it }
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
Divider()
|
||||
|
||||
// Field type section
|
||||
FieldTypeSection(
|
||||
format = field.format,
|
||||
isEditable = isEditable,
|
||||
onTypeClick = { fieldEvent(FieldEvent.OnChangeTypeClick) }
|
||||
)
|
||||
Divider()
|
||||
|
||||
// Limit object types (only for OBJECT format)
|
||||
if (field.format == RelationFormat.OBJECT) {
|
||||
LimitTypesSection(
|
||||
objTypes = field.limitObjectTypes,
|
||||
isEditable = isEditable,
|
||||
onLimitTypesClick = { fieldEvent(FieldEvent.OnLimitTypesClick) }
|
||||
)
|
||||
Divider()
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(14.dp))
|
||||
|
||||
if (isEditable) {
|
||||
ButtonPrimary(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 20.dp),
|
||||
text = stringResource(R.string.object_type_fields_btn_save),
|
||||
onClick = {
|
||||
fieldEvent(
|
||||
FieldEvent.OnSaveButtonClicked(
|
||||
name = innerValue,
|
||||
format = field.format,
|
||||
limitObjectTypes = field.limitObjectTypes.map { it.id }
|
||||
)
|
||||
)
|
||||
},
|
||||
size = ButtonSize.Large
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun NameTextField(
|
||||
modifier: Modifier,
|
||||
value: String,
|
||||
isEditable: Boolean,
|
||||
focusRequester: FocusRequester,
|
||||
keyboardController: SoftwareKeyboardController?,
|
||||
onValueChange: (String) -> Unit
|
||||
) {
|
||||
val focusManager = LocalFocusManager.current
|
||||
|
||||
Column(modifier = modifier) {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 20.dp),
|
||||
text = stringResource(id = R.string.name),
|
||||
style = Caption1Regular,
|
||||
color = colorResource(id = R.color.text_secondary)
|
||||
)
|
||||
|
||||
BasicTextField(
|
||||
value = value,
|
||||
onValueChange = onValueChange,
|
||||
textStyle = HeadlineHeading.copy(color = colorResource(id = R.color.text_primary)),
|
||||
singleLine = true,
|
||||
enabled = isEditable,
|
||||
cursorBrush = SolidColor(colorResource(id = R.color.text_primary)),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight()
|
||||
.padding(start = 20.dp, top = 6.dp, end = 20.dp)
|
||||
.focusRequester(focusRequester)
|
||||
.onFocusChanged { /* You can handle focus changes here if needed */ },
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions {
|
||||
keyboardController?.hide()
|
||||
focusManager.clearFocus()
|
||||
onValueChange(value)
|
||||
},
|
||||
decorationBox = { innerTextField ->
|
||||
if (value.isEmpty()) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.untitled),
|
||||
style = HeadlineHeading,
|
||||
color = colorResource(id = R.color.text_tertiary),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
innerTextField()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun FieldTypeSection(
|
||||
format: RelationFormat,
|
||||
isEditable: Boolean,
|
||||
onTypeClick: () -> Unit
|
||||
) {
|
||||
val icon = format.simpleIcon()
|
||||
SectionItem(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(52.dp),
|
||||
text = stringResource(id = R.string.type)
|
||||
)
|
||||
Divider()
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(52.dp)
|
||||
.padding(horizontal = 20.dp)
|
||||
.noRippleThrottledClickable { if (isEditable) onTypeClick() }
|
||||
) {
|
||||
if (icon != null) {
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.size(24.dp)
|
||||
.align(Alignment.CenterStart),
|
||||
painter = painterResource(id = icon),
|
||||
contentDescription = "Relation format icon"
|
||||
)
|
||||
}
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 34.dp)
|
||||
.align(Alignment.CenterStart),
|
||||
text = stringResource(format.getPrettyName()),
|
||||
style = BodyRegular,
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
if (isEditable) {
|
||||
Image(
|
||||
modifier = Modifier.align(Alignment.CenterEnd),
|
||||
painter = painterResource(id = R.drawable.ic_arrow_forward_24),
|
||||
contentDescription = "Change field format icon"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LimitTypesSection(
|
||||
objTypes: List<UiFieldObjectItem>,
|
||||
isEditable: Boolean,
|
||||
onLimitTypesClick: () -> Unit
|
||||
) {
|
||||
val size = objTypes.size
|
||||
SectionItem(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(52.dp),
|
||||
text = stringResource(id = R.string.limit_object_types)
|
||||
)
|
||||
Divider()
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(52.dp)
|
||||
.padding(horizontal = 20.dp)
|
||||
.noRippleThrottledClickable { if (isEditable) onLimitTypesClick() }
|
||||
) {
|
||||
if (objTypes.isNotEmpty()) {
|
||||
Row(modifier = Modifier.align(Alignment.CenterStart)) {
|
||||
ListWidgetObjectIcon(
|
||||
modifier = Modifier.size(20.dp),
|
||||
icon = objTypes.first().icon,
|
||||
backgroundColor = R.color.transparent_black
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.padding(start = 6.dp),
|
||||
text = objTypes.first().title.take(20),
|
||||
style = BodyRegular,
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
if (size > 1) {
|
||||
Text(
|
||||
text = " +${size - 1}",
|
||||
style = BodyRegular,
|
||||
color = colorResource(id = R.color.text_primary)
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.align(Alignment.CenterStart),
|
||||
text = stringResource(R.string.none),
|
||||
style = BodyRegular,
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
if (isEditable) {
|
||||
Image(
|
||||
modifier = Modifier.align(Alignment.CenterEnd),
|
||||
painter = painterResource(id = R.drawable.ic_arrow_forward_24),
|
||||
contentDescription = "Change field object types icon"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SectionItem(modifier: Modifier, text: String) {
|
||||
Box(modifier = modifier) {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.padding(start = 20.dp, bottom = 8.dp)
|
||||
.align(Alignment.BottomStart),
|
||||
text = text,
|
||||
style = Caption1Regular,
|
||||
color = colorResource(id = R.color.text_secondary)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@DefaultPreviews
|
||||
@Composable
|
||||
private fun MyPreview() {
|
||||
EditFieldScreen(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
uiFieldEditOrNewState = UiFieldEditOrNewState.Visible.Edit(
|
||||
item = createDummyFieldDraggableItem()
|
||||
),
|
||||
fieldEvent = {}
|
||||
)
|
||||
}
|
||||
|
||||
@DefaultPreviews
|
||||
@Composable
|
||||
private fun MyPreviewOnlyPreview() {
|
||||
EditFieldScreen(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
uiFieldEditOrNewState = UiFieldEditOrNewState.Visible.ViewOnly(
|
||||
item = createDummyFieldDraggableItem(
|
||||
isEditableField = false
|
||||
)
|
||||
),
|
||||
fieldEvent = {}
|
||||
)
|
||||
}
|
|
@ -35,24 +35,19 @@ import androidx.compose.runtime.setValue
|
|||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Alignment.Companion.CenterVertically
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.composed
|
||||
import androidx.compose.ui.draw.drawBehind
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
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.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.DpOffset
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.anytypeio.anytype.core_models.RelationFormat
|
||||
import com.anytypeio.anytype.core_ui.common.DefaultPreviews
|
||||
import com.anytypeio.anytype.core_ui.common.ReorderHapticFeedback
|
||||
import com.anytypeio.anytype.core_ui.common.ReorderHapticFeedbackType
|
||||
import com.anytypeio.anytype.core_ui.common.bottomBorder
|
||||
import com.anytypeio.anytype.core_ui.common.rememberReorderHapticFeedback
|
||||
import com.anytypeio.anytype.core_ui.extensions.simpleIcon
|
||||
import com.anytypeio.anytype.core_ui.foundation.Dragger
|
||||
|
@ -68,14 +63,14 @@ import com.anytypeio.anytype.feature_object_type.R
|
|||
import com.anytypeio.anytype.feature_object_type.fields.FieldEvent
|
||||
import com.anytypeio.anytype.feature_object_type.fields.FieldEvent.*
|
||||
import com.anytypeio.anytype.feature_object_type.fields.FieldEvent.FieldItemMenu.*
|
||||
import com.anytypeio.anytype.feature_object_type.fields.UiAddFieldsScreenState
|
||||
import com.anytypeio.anytype.feature_object_type.fields.UiFieldEditOrNewState
|
||||
import com.anytypeio.anytype.feature_object_type.fields.UiFieldsListItem
|
||||
import com.anytypeio.anytype.feature_object_type.fields.UiFieldsListItem.Section
|
||||
import com.anytypeio.anytype.feature_object_type.fields.UiFieldsListState
|
||||
import com.anytypeio.anytype.feature_object_type.fields.UiLocalsFieldsInfoState
|
||||
import com.anytypeio.anytype.feature_object_type.ui.UiIconState
|
||||
import com.anytypeio.anytype.feature_object_type.ui.UiTitleState
|
||||
import com.anytypeio.anytype.feature_properties.edit.UiEditPropertyState
|
||||
import com.anytypeio.anytype.feature_properties.edit.ui.PropertyScreen
|
||||
import com.anytypeio.anytype.presentation.objects.ObjectIcon
|
||||
import kotlinx.coroutines.delay
|
||||
import sh.calvin.reorderable.ReorderableItem
|
||||
|
@ -88,9 +83,8 @@ fun FieldsMainScreen(
|
|||
uiFieldsListState: UiFieldsListState,
|
||||
uiTitleState: UiTitleState,
|
||||
uiIconState: UiIconState,
|
||||
uiFieldEditOrNewState: UiFieldEditOrNewState,
|
||||
uiFieldLocalInfoState: UiLocalsFieldsInfoState,
|
||||
uiAddFieldsScreenState: UiAddFieldsScreenState,
|
||||
uiEditPropertyState: UiEditPropertyState,
|
||||
fieldEvent: (FieldEvent) -> Unit
|
||||
) {
|
||||
|
||||
|
@ -198,10 +192,7 @@ fun FieldsMainScreen(
|
|||
item = item,
|
||||
reorderingState = reorderableLazyColumnState,
|
||||
fieldEvent = fieldEvent,
|
||||
isReorderable = false,
|
||||
onAddIconClick = {
|
||||
fieldEvent(FieldEvent.Section.OnAddToHeaderIconClick)
|
||||
}
|
||||
isReorderable = false
|
||||
)
|
||||
}
|
||||
is Section.Local,
|
||||
|
@ -226,11 +217,16 @@ fun FieldsMainScreen(
|
|||
}
|
||||
)
|
||||
|
||||
if (uiFieldEditOrNewState is UiFieldEditOrNewState.Visible) {
|
||||
EditFieldScreen(
|
||||
if (uiEditPropertyState is UiEditPropertyState.Visible) {
|
||||
PropertyScreen(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
uiFieldEditOrNewState = uiFieldEditOrNewState,
|
||||
fieldEvent = fieldEvent
|
||||
uiState = uiEditPropertyState,
|
||||
onDismissRequest = { fieldEvent(OnEditPropertyScreenDismiss) },
|
||||
onFormatClick = {},
|
||||
onLimitTypesClick = {},
|
||||
onSaveButtonClicked = {},
|
||||
onCreateNewButtonClicked = {},
|
||||
onPropertyNameUpdate = { }
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -241,13 +237,6 @@ fun FieldsMainScreen(
|
|||
fieldEvent = fieldEvent
|
||||
)
|
||||
}
|
||||
|
||||
if (uiAddFieldsScreenState is UiAddFieldsScreenState.Visible) {
|
||||
AddFieldScreen(
|
||||
state = uiAddFieldsScreenState,
|
||||
fieldEvent = fieldEvent
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns a content type string based on the item type. **/
|
||||
|
@ -599,29 +588,6 @@ private fun LazyItemScope.FieldItemDraggable(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Modifier.bottomBorder(
|
||||
strokeWidth: Dp = 0.5.dp,
|
||||
color: Color = colorResource(R.color.shape_primary)
|
||||
) = composed(
|
||||
factory = {
|
||||
val density = LocalDensity.current
|
||||
val strokeWidthPx = density.run { strokeWidth.toPx() }
|
||||
|
||||
Modifier.drawBehind {
|
||||
val width = size.width
|
||||
val height = size.height - strokeWidthPx / 2
|
||||
|
||||
drawLine(
|
||||
color = color,
|
||||
start = Offset(x = 0f, y = height),
|
||||
end = Offset(x = width, y = height),
|
||||
strokeWidth = strokeWidthPx
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun ItemDropDownMenu(
|
||||
item: UiFieldsListItem.Item,
|
||||
|
@ -757,9 +723,8 @@ fun PreviewTypeFieldsMainScreen() {
|
|||
)
|
||||
),
|
||||
fieldEvent = {},
|
||||
uiFieldEditOrNewState = UiFieldEditOrNewState.Hidden,
|
||||
uiFieldLocalInfoState = UiLocalsFieldsInfoState.Hidden,
|
||||
uiAddFieldsScreenState = UiAddFieldsScreenState.Hidden
|
||||
uiEditPropertyState = UiEditPropertyState.Hidden,
|
||||
uiFieldLocalInfoState = UiLocalsFieldsInfoState.Hidden
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
package com.anytypeio.anytype.feature_object_type.ui
|
||||
|
||||
import com.anytypeio.anytype.core_models.RelationFormat
|
||||
import com.anytypeio.anytype.feature_object_type.fields.UiFieldObjectItem
|
||||
import com.anytypeio.anytype.feature_object_type.fields.UiFieldsListItem
|
||||
import com.anytypeio.anytype.presentation.objects.ObjectIcon
|
||||
|
||||
fun createDummyFieldDraggableItem(isEditableField: Boolean = true): UiFieldsListItem.Item.Draggable {
|
||||
return UiFieldsListItem.Item.Draggable(
|
||||
id = "dummyId",
|
||||
fieldKey = "dummyKey",
|
||||
fieldTitle = "Field Title",
|
||||
format = RelationFormat.OBJECT,
|
||||
limitObjectTypes = listOf(
|
||||
UiFieldObjectItem(
|
||||
id = "dummyObjectId1",
|
||||
key = "dummyKey1",
|
||||
title = "Dummy Object Type 1",
|
||||
icon = ObjectIcon.Empty.ObjectType,
|
||||
),
|
||||
UiFieldObjectItem(
|
||||
id = "dummyObjectId1",
|
||||
key = "dummyKey1",
|
||||
title = "Dummy Object Type 1",
|
||||
icon = ObjectIcon.Empty.ObjectType,
|
||||
|
||||
),
|
||||
UiFieldObjectItem(
|
||||
id = "dummyObjectId1",
|
||||
key = "dummyKey1",
|
||||
title = "Dummy Object Type 1",
|
||||
icon = ObjectIcon.Empty.ObjectType,
|
||||
|
||||
),
|
||||
),
|
||||
isEditableField = isEditableField,
|
||||
canDelete = true
|
||||
)
|
||||
}
|
|
@ -33,8 +33,7 @@ sealed class ObjectTypeCommand {
|
|||
|
||||
data object OpenFieldsScreen : ObjectTypeCommand()
|
||||
|
||||
data class OpenAddFieldScreen(val typeId: Id, val space: Id, val isSet: Boolean = false) :
|
||||
ObjectTypeCommand()
|
||||
data class OpenEditTypePropertiesScreen(val typeId: Id, val space: Id) : ObjectTypeCommand()
|
||||
}
|
||||
|
||||
//region OBJECT TYPE HEADER (title + icon)
|
||||
|
|
|
@ -13,11 +13,10 @@ 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.resources.StringResourceProvider
|
||||
import com.anytypeio.anytype.feature_object_type.fields.UiAddFieldItem
|
||||
import com.anytypeio.anytype.feature_object_type.fields.UiFieldObjectItem
|
||||
import com.anytypeio.anytype.feature_object_type.fields.UiFieldsListItem
|
||||
import com.anytypeio.anytype.feature_object_type.fields.UiFieldsListItem.Item
|
||||
import com.anytypeio.anytype.feature_object_type.fields.UiFieldsListItem.Section
|
||||
import com.anytypeio.anytype.feature_properties.edit.UiPropertyLimitTypeItem
|
||||
import com.anytypeio.anytype.presentation.editor.cover.CoverImageHashProvider
|
||||
import com.anytypeio.anytype.presentation.mapper.objectIcon
|
||||
import com.anytypeio.anytype.presentation.relations.BasicObjectCoverWrapper
|
||||
|
@ -56,7 +55,7 @@ fun ObjectWrapper.Basic.toTemplateView(
|
|||
* Extension function to safely get a name for the relation.
|
||||
* If the name is blank, returns a default untitled title.
|
||||
*/
|
||||
private fun ObjectWrapper.Relation.getName(stringResourceProvider: StringResourceProvider): String =
|
||||
fun ObjectWrapper.Relation.getName(stringResourceProvider: StringResourceProvider): String =
|
||||
if (name.isNullOrBlank()) {
|
||||
stringResourceProvider.getUntitledObjectTitle()
|
||||
} else {
|
||||
|
@ -140,7 +139,7 @@ suspend fun buildUiFieldsList(
|
|||
}
|
||||
|
||||
return buildList {
|
||||
add(Section.Header(canAdd = true))
|
||||
add(Section.Header(canAdd = false))
|
||||
addAll(headerItems)
|
||||
|
||||
add(Section.SideBar(canAdd = true))
|
||||
|
@ -173,11 +172,11 @@ private suspend fun mapLimitObjectTypes(
|
|||
storeOfObjectTypes: StoreOfObjectTypes,
|
||||
fieldParser: FieldParser,
|
||||
urlBuilder: UrlBuilder
|
||||
): List<UiFieldObjectItem> {
|
||||
): List<UiPropertyLimitTypeItem> {
|
||||
return if (relation.format == RelationFormat.OBJECT && relation.relationFormatObjectTypes.isNotEmpty()) {
|
||||
relation.relationFormatObjectTypes.mapNotNull { key ->
|
||||
storeOfObjectTypes.getByKey(key)?.let { objType ->
|
||||
UiFieldObjectItem(
|
||||
UiPropertyLimitTypeItem(
|
||||
id = objType.id,
|
||||
key = objType.uniqueKey,
|
||||
title = fieldParser.getObjectName(objType),
|
||||
|
@ -239,16 +238,3 @@ private suspend fun mapToUiFieldsLocalListItem(
|
|||
)
|
||||
}
|
||||
|
||||
fun ObjectWrapper.Relation.mapToUiAddFieldListItem(
|
||||
stringResourceProvider: StringResourceProvider
|
||||
): UiAddFieldItem? {
|
||||
val field = this
|
||||
if (field.key == Relations.DESCRIPTION) return null
|
||||
return UiAddFieldItem(
|
||||
id = field.id,
|
||||
fieldKey = field.key,
|
||||
fieldTitle = field.getName(stringResourceProvider),
|
||||
format = field.format
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -4,14 +4,13 @@ import androidx.lifecycle.ViewModel
|
|||
import androidx.lifecycle.viewModelScope
|
||||
import com.anytypeio.anytype.analytics.base.Analytics
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.Key
|
||||
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.permissions.ObjectPermissions
|
||||
import com.anytypeio.anytype.core_models.permissions.toObjectPermissionsForTypes
|
||||
import com.anytypeio.anytype.core_ui.extensions.simpleIcon
|
||||
import com.anytypeio.anytype.domain.base.fold
|
||||
import com.anytypeio.anytype.domain.block.interactor.sets.CreateObjectSet
|
||||
import com.anytypeio.anytype.domain.event.interactor.SpaceSyncAndP2PStatusProvider
|
||||
import com.anytypeio.anytype.domain.library.StoreSearchParams
|
||||
import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
|
||||
|
@ -22,17 +21,12 @@ 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.templates.CreateTemplate
|
||||
import com.anytypeio.anytype.feature_object_type.fields.FieldEvent
|
||||
import com.anytypeio.anytype.feature_object_type.fields.UiAddFieldItem
|
||||
import com.anytypeio.anytype.feature_object_type.fields.UiAddFieldsScreenState
|
||||
import com.anytypeio.anytype.feature_object_type.fields.UiFieldEditOrNewState
|
||||
import com.anytypeio.anytype.feature_object_type.fields.UiFieldEditOrNewState.Visible.*
|
||||
import com.anytypeio.anytype.feature_object_type.fields.UiFieldsListItem
|
||||
import com.anytypeio.anytype.feature_object_type.fields.UiFieldsListState
|
||||
|
@ -50,19 +44,15 @@ import com.anytypeio.anytype.feature_object_type.ui.UiLayoutButtonState
|
|||
import com.anytypeio.anytype.feature_object_type.ui.UiLayoutTypeState
|
||||
import com.anytypeio.anytype.feature_object_type.ui.UiLayoutTypeState.*
|
||||
import com.anytypeio.anytype.feature_object_type.ui.UiSyncStatusBadgeState
|
||||
import com.anytypeio.anytype.feature_object_type.ui.UiTemplatesAddIconState
|
||||
import com.anytypeio.anytype.feature_object_type.ui.UiTemplatesButtonState
|
||||
import com.anytypeio.anytype.feature_object_type.ui.UiTemplatesModalListState
|
||||
import com.anytypeio.anytype.feature_object_type.ui.UiTitleState
|
||||
import com.anytypeio.anytype.feature_object_type.ui.buildUiFieldsList
|
||||
import com.anytypeio.anytype.feature_object_type.ui.mapToUiAddFieldListItem
|
||||
import com.anytypeio.anytype.feature_object_type.ui.toTemplateView
|
||||
import com.anytypeio.anytype.feature_properties.edit.UiEditPropertyState
|
||||
import com.anytypeio.anytype.presentation.analytics.AnalyticSpaceHelperDelegate
|
||||
import com.anytypeio.anytype.presentation.editor.cover.CoverImageHashProvider
|
||||
import com.anytypeio.anytype.presentation.editor.cover.UnsplashViewModel.Companion.DEBOUNCE_DURATION
|
||||
import com.anytypeio.anytype.presentation.extension.sendAnalyticsScreenObjectType
|
||||
import com.anytypeio.anytype.presentation.home.OpenObjectNavigation
|
||||
import com.anytypeio.anytype.presentation.home.navigation
|
||||
import com.anytypeio.anytype.presentation.mapper.objectIcon
|
||||
import com.anytypeio.anytype.presentation.search.ObjectSearchConstants.defaultKeys
|
||||
import com.anytypeio.anytype.presentation.sync.SyncStatusWidgetState
|
||||
|
@ -71,22 +61,14 @@ import com.anytypeio.anytype.presentation.sync.updateStatus
|
|||
import com.anytypeio.anytype.presentation.templates.TemplateView
|
||||
import kotlin.collections.map
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.debounce
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.drop
|
||||
import kotlinx.coroutines.flow.emitAll
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onCompletion
|
||||
import kotlinx.coroutines.flow.take
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
|
||||
|
@ -105,18 +87,15 @@ class ObjectTypeViewModel(
|
|||
private val storeOfObjectTypes: StoreOfObjectTypes,
|
||||
private val storelessSubscriptionContainer: StorelessSubscriptionContainer,
|
||||
private val spaceSyncAndP2PStatusProvider: SpaceSyncAndP2PStatusProvider,
|
||||
private val createObject: CreateObject,
|
||||
private val fieldParser: FieldParser,
|
||||
private val coverImageHashProvider: CoverImageHashProvider,
|
||||
private val deleteObjects: DeleteObjects,
|
||||
private val setObjectDetails: SetObjectDetails,
|
||||
private val createObjectSet: CreateObjectSet,
|
||||
private val stringResourceProvider: StringResourceProvider,
|
||||
private val createTemplate: CreateTemplate,
|
||||
private val duplicateObjects: DuplicateObjects,
|
||||
private val getObjectTypeConflictingFields: GetObjectTypeConflictingFields,
|
||||
private val objectTypeSetRecommendedFields: SetObjectTypeRecommendedFields,
|
||||
private val objectTypeSetHeaderRecommendedFields: SetObjectTypeHeaderRecommendedFields
|
||||
private val objectTypeSetRecommendedFields: SetObjectTypeRecommendedFields
|
||||
) : ViewModel(), AnalyticSpaceHelperDelegate by analyticSpaceHelperDelegate {
|
||||
|
||||
//region UI STATE
|
||||
|
@ -148,13 +127,11 @@ class ObjectTypeViewModel(
|
|||
val uiFieldLocalInfoState =
|
||||
MutableStateFlow<UiLocalsFieldsInfoState>(UiLocalsFieldsInfoState.Hidden)
|
||||
|
||||
//fields
|
||||
//properties list
|
||||
val uiFieldsListState = MutableStateFlow<UiFieldsListState>(UiFieldsListState.EMPTY)
|
||||
val uiFieldEditOrNewState =
|
||||
MutableStateFlow<UiFieldEditOrNewState>(UiFieldEditOrNewState.Hidden)
|
||||
|
||||
//add new field
|
||||
val uiAddFieldsState = MutableStateFlow<UiAddFieldsScreenState>(UiAddFieldsScreenState.Hidden)
|
||||
//edit property
|
||||
val uiEditPropertyScreen = MutableStateFlow<UiEditPropertyState>(UiEditPropertyState.Hidden)
|
||||
|
||||
//error
|
||||
val errorState = MutableStateFlow<UiErrorState>(UiErrorState.Hidden)
|
||||
|
@ -166,6 +143,8 @@ class ObjectTypeViewModel(
|
|||
private val _objectTypeConflictingFieldIds = MutableStateFlow<List<Id>>(emptyList())
|
||||
//endregion
|
||||
|
||||
val commands = MutableSharedFlow<ObjectTypeCommand>()
|
||||
|
||||
//region INIT AND LIFE CYCLE
|
||||
init {
|
||||
Timber.d("init, vmParams: $vmParams")
|
||||
|
@ -191,7 +170,6 @@ class ObjectTypeViewModel(
|
|||
//endregion
|
||||
|
||||
//region DATA
|
||||
|
||||
private fun proceedWithObservingObjectType() {
|
||||
viewModelScope.launch {
|
||||
combine(
|
||||
|
@ -387,6 +365,31 @@ class ObjectTypeViewModel(
|
|||
fun hideError() {
|
||||
errorState.value = UiErrorState.Hidden
|
||||
}
|
||||
|
||||
fun setupUiEditPropertyScreen(item: UiFieldsListItem.Item) {
|
||||
val permissions = _objectTypePermissionsState.value
|
||||
if (permissions?.participantCanEdit == true && item.isEditableField) {
|
||||
uiEditPropertyScreen.value = UiEditPropertyState.Visible.Edit(
|
||||
id = item.id,
|
||||
key = item.fieldKey,
|
||||
name = item.fieldTitle,
|
||||
formatName = stringResourceProvider.getPropertiesFormatPrettyString(item.format),
|
||||
formatIcon = item.format.simpleIcon(),
|
||||
format = item.format,
|
||||
limitObjectTypes = item.limitObjectTypes
|
||||
)
|
||||
} else {
|
||||
uiEditPropertyScreen.value = UiEditPropertyState.Visible.View(
|
||||
id = item.id,
|
||||
key = item.fieldKey,
|
||||
name = item.fieldTitle,
|
||||
formatName = stringResourceProvider.getPropertiesFormatPrettyString(item.format),
|
||||
formatIcon = item.format.simpleIcon(),
|
||||
format = item.format,
|
||||
limitObjectTypes = item.limitObjectTypes
|
||||
)
|
||||
}
|
||||
}
|
||||
//endregion
|
||||
|
||||
//region Ui EVENTS - TYPES
|
||||
|
@ -623,33 +626,17 @@ class ObjectTypeViewModel(
|
|||
fun onFieldEvent(event: FieldEvent) {
|
||||
Timber.d("onFieldEvent: $event")
|
||||
when (event) {
|
||||
FieldEvent.OnChangeTypeClick -> TODO()
|
||||
FieldEvent.OnFieldEditScreenDismiss -> {
|
||||
uiFieldEditOrNewState.value = UiFieldEditOrNewState.Hidden
|
||||
FieldEvent.OnEditPropertyScreenDismiss -> {
|
||||
uiEditPropertyScreen.value = UiEditPropertyState.Hidden
|
||||
}
|
||||
|
||||
is FieldEvent.OnFieldItemClick -> {
|
||||
when (event.item) {
|
||||
is UiFieldsListItem.Item -> {
|
||||
val permissions = _objectTypePermissionsState.value
|
||||
if (permissions?.participantCanEdit == true && event.item.isEditableField) {
|
||||
uiFieldEditOrNewState.value = Edit(
|
||||
event.item
|
||||
)
|
||||
} else {
|
||||
uiFieldEditOrNewState.value = ViewOnly(
|
||||
event.item
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
is UiFieldsListItem.Item -> setupUiEditPropertyScreen(item = event.item)
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
FieldEvent.OnLimitTypesClick -> TODO()
|
||||
is FieldEvent.OnSaveButtonClicked -> TODO()
|
||||
|
||||
is FieldEvent.FieldItemMenu -> proceedWithFieldItemMenuClick(event)
|
||||
FieldEvent.FieldLocalInfo.OnDismiss -> {
|
||||
uiFieldLocalInfoState.value = UiLocalsFieldsInfoState.Hidden
|
||||
|
@ -659,12 +646,15 @@ class ObjectTypeViewModel(
|
|||
uiFieldLocalInfoState.value = UiLocalsFieldsInfoState.Visible
|
||||
}
|
||||
|
||||
FieldEvent.Section.OnAddToHeaderIconClick -> {
|
||||
proceedWithAddFieldToHeaderScreen()
|
||||
}
|
||||
|
||||
FieldEvent.Section.OnAddToSidebarIconClick -> {
|
||||
proceedWithAddFieldToSidebarScreen()
|
||||
viewModelScope.launch {
|
||||
commands.emit(
|
||||
ObjectTypeCommand.OpenEditTypePropertiesScreen(
|
||||
typeId = vmParams.objectId,
|
||||
space = vmParams.spaceId.id,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
FieldEvent.DragEvent.OnDragEnd -> {
|
||||
|
@ -706,24 +696,6 @@ class ObjectTypeViewModel(
|
|||
currentList.add(toIndex, item)
|
||||
uiFieldsListState.value = UiFieldsListState(items = currentList)
|
||||
}
|
||||
|
||||
FieldEvent.OnAddFieldScreenDismiss -> {
|
||||
hideAddNewFieldScreen()
|
||||
}
|
||||
|
||||
is FieldEvent.OnAddToHeaderFieldClick -> {
|
||||
onAddToHeaderFieldClicked(item = event.item)
|
||||
hideAddNewFieldScreen()
|
||||
}
|
||||
|
||||
is FieldEvent.OnAddToSidebarFieldClick -> {
|
||||
onAddToSidebarFieldClicked(item = event.item)
|
||||
hideAddNewFieldScreen()
|
||||
}
|
||||
|
||||
is FieldEvent.OnAddFieldSearchQueryChanged -> {
|
||||
onQueryChanged(query = event.query)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -777,30 +749,6 @@ class ObjectTypeViewModel(
|
|||
proceedWithSetRecommendedFields(newRecommendedFields)
|
||||
}
|
||||
|
||||
is FieldEvent.FieldItemMenu.OnRemoveLocalClick -> TODO()
|
||||
}
|
||||
}
|
||||
//endregion
|
||||
|
||||
//region NAVIGATION
|
||||
val commands = MutableSharedFlow<ObjectTypeCommand>()
|
||||
val navigation = MutableSharedFlow<OpenObjectNavigation>()
|
||||
|
||||
private fun proceedWithNavigation(
|
||||
objectId: Id,
|
||||
objectLayout: ObjectType.Layout?
|
||||
) {
|
||||
Timber.d("proceedWithNavigation, objectId: $objectId, objectLayout: $objectLayout")
|
||||
val destination = objectLayout?.navigation(
|
||||
target = objectId,
|
||||
space = vmParams.spaceId.id
|
||||
)
|
||||
if (destination != null) {
|
||||
viewModelScope.launch {
|
||||
navigation.emit(destination)
|
||||
}
|
||||
} else {
|
||||
Timber.w("No navigation destination found for object $objectId with layout $objectLayout")
|
||||
}
|
||||
}
|
||||
//endregion
|
||||
|
@ -960,124 +908,6 @@ class ObjectTypeViewModel(
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun proceedWithSetHeaderRecommendedFields(fields: List<Id>) {
|
||||
val params = SetObjectTypeHeaderRecommendedFields.Params(
|
||||
objectTypeId = vmParams.objectId,
|
||||
fields = fields
|
||||
)
|
||||
viewModelScope.launch {
|
||||
objectTypeSetHeaderRecommendedFields.async(params).fold(
|
||||
onSuccess = {
|
||||
Timber.d("Header recommended fields set")
|
||||
},
|
||||
onFailure = {
|
||||
Timber.e(it, "Error while setting header recommended fields")
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
//endregion
|
||||
|
||||
//region ADD NEW FIELD
|
||||
private val input = MutableStateFlow("")
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
private val query = input.take(1).onCompletion {
|
||||
emitAll(
|
||||
input.drop(1).debounce(DEBOUNCE_DURATION).distinctUntilChanged()
|
||||
)
|
||||
}
|
||||
|
||||
private var addFieldSearchJob: Job? = null
|
||||
|
||||
/**
|
||||
* Loads the available fields from type, applies filtering based on a search query,
|
||||
* and then updates the UI state.
|
||||
*/
|
||||
private fun showAddFieldScreen(addToHeader: Boolean) {
|
||||
// Collect field keys that are already present in the type fields list.
|
||||
val typeFieldsKeys =
|
||||
uiFieldsListState.value.items.mapNotNull { (it as? UiFieldsListItem.Item)?.fieldKey }
|
||||
|
||||
addFieldSearchJob = viewModelScope.launch {
|
||||
// Combine the search query flow with the list of all fields.
|
||||
combine(
|
||||
query,
|
||||
storeOfRelations.trackChanges()
|
||||
) { queryText, _ ->
|
||||
// Filter out fields by query and that already exist and are not valid.
|
||||
filterFields(
|
||||
allFields = storeOfRelations.getAll(),
|
||||
typeKeys = typeFieldsKeys,
|
||||
queryText = queryText
|
||||
)
|
||||
}.collect { filteredFields ->
|
||||
val items = filteredFields.mapNotNull { field ->
|
||||
field.mapToUiAddFieldListItem(stringResourceProvider)
|
||||
}.sortedBy { it.fieldTitle }
|
||||
|
||||
uiAddFieldsState.value = UiAddFieldsScreenState.Visible(
|
||||
items = items,
|
||||
addToHeader = addToHeader
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun filterFields(
|
||||
allFields: List<ObjectWrapper.Relation>,
|
||||
typeKeys: List<Key>,
|
||||
queryText: String
|
||||
): List<ObjectWrapper.Relation> = allFields.filter { field ->
|
||||
field.key !in typeKeys &&
|
||||
field.isValidToUse &&
|
||||
(queryText.isBlank() || field.name?.contains(queryText, ignoreCase = true) == true)
|
||||
}
|
||||
|
||||
private fun onQueryChanged(query: String) {
|
||||
input.value = query
|
||||
}
|
||||
|
||||
fun hideAddNewFieldScreen() {
|
||||
input.value = ""
|
||||
addFieldSearchJob?.cancel()
|
||||
addFieldSearchJob = null
|
||||
uiAddFieldsState.value = UiAddFieldsScreenState.Hidden
|
||||
}
|
||||
|
||||
fun proceedWithAddFieldToHeaderScreen() {
|
||||
showAddFieldScreen(addToHeader = true)
|
||||
}
|
||||
|
||||
fun proceedWithAddFieldToSidebarScreen() {
|
||||
showAddFieldScreen(addToHeader = false)
|
||||
}
|
||||
|
||||
private fun updateFieldRecommendations(
|
||||
currentFields: List<String>?,
|
||||
item: UiAddFieldItem,
|
||||
updateAction: (List<String>) -> Unit
|
||||
) {
|
||||
val newFields = currentFields.orEmpty() + item.id
|
||||
updateAction(newFields)
|
||||
}
|
||||
|
||||
private fun onAddToSidebarFieldClicked(item: UiAddFieldItem) {
|
||||
updateFieldRecommendations(
|
||||
currentFields = _objTypeState.value?.recommendedRelations,
|
||||
item = item,
|
||||
updateAction = ::proceedWithSetRecommendedFields
|
||||
)
|
||||
}
|
||||
|
||||
private fun onAddToHeaderFieldClicked(item: UiAddFieldItem) {
|
||||
updateFieldRecommendations(
|
||||
currentFields = _objTypeState.value?.recommendedFeaturedRelations,
|
||||
item = item,
|
||||
updateAction = ::proceedWithSetHeaderRecommendedFields
|
||||
)
|
||||
}
|
||||
//endregion
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -3,7 +3,6 @@ package com.anytypeio.anytype.feature_object_type.viewmodel
|
|||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.anytypeio.anytype.analytics.base.Analytics
|
||||
import com.anytypeio.anytype.domain.block.interactor.sets.CreateObjectSet
|
||||
import com.anytypeio.anytype.domain.event.interactor.SpaceSyncAndP2PStatusProvider
|
||||
import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
|
@ -13,10 +12,8 @@ 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.templates.CreateTemplate
|
||||
|
@ -35,18 +32,15 @@ class ObjectTypeVMFactory @Inject constructor(
|
|||
private val storeOfObjectTypes: StoreOfObjectTypes,
|
||||
private val storelessSubscriptionContainer: StorelessSubscriptionContainer,
|
||||
private val spaceSyncAndP2PStatusProvider: SpaceSyncAndP2PStatusProvider,
|
||||
private val createObject: CreateObject,
|
||||
private val fieldParser: FieldParser,
|
||||
private val coverImageHashProvider: CoverImageHashProvider,
|
||||
private val deleteObjects: DeleteObjects,
|
||||
private val setObjectDetails: SetObjectDetails,
|
||||
private val createObjectSet: CreateObjectSet,
|
||||
private val stringResourceProvider: StringResourceProvider,
|
||||
private val createTemplate: CreateTemplate,
|
||||
private val duplicateObjects: DuplicateObjects,
|
||||
private val getObjectTypeConflictingFields: GetObjectTypeConflictingFields,
|
||||
private val objectTypeSetRecommendedFields: SetObjectTypeRecommendedFields,
|
||||
private val objectTypeSetHeaderRecommendedFields: SetObjectTypeHeaderRecommendedFields
|
||||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
|
@ -61,17 +55,14 @@ class ObjectTypeVMFactory @Inject constructor(
|
|||
storeOfObjectTypes = storeOfObjectTypes,
|
||||
storelessSubscriptionContainer = storelessSubscriptionContainer,
|
||||
spaceSyncAndP2PStatusProvider = spaceSyncAndP2PStatusProvider,
|
||||
createObject = createObject,
|
||||
fieldParser = fieldParser,
|
||||
coverImageHashProvider = coverImageHashProvider,
|
||||
deleteObjects = deleteObjects,
|
||||
setObjectDetails = setObjectDetails,
|
||||
createObjectSet = createObjectSet,
|
||||
stringResourceProvider = stringResourceProvider,
|
||||
createTemplate = createTemplate,
|
||||
duplicateObjects = duplicateObjects,
|
||||
getObjectTypeConflictingFields = getObjectTypeConflictingFields,
|
||||
objectTypeSetRecommendedFields = objectTypeSetRecommendedFields,
|
||||
objectTypeSetHeaderRecommendedFields = objectTypeSetHeaderRecommendedFields
|
||||
) as T
|
||||
}
|
|
@ -193,29 +193,6 @@ class TestFieldsMappping {
|
|||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should not filter sidebar fields by hidden`() = runTest {
|
||||
|
||||
storeOfRelations.apply {
|
||||
merge(allSpaceRelations)
|
||||
}
|
||||
|
||||
storeOfObjectTypes.apply {
|
||||
merge(listOf(testObjectType, fieldAssigneeObjType2, fieldAssigneeObjType1))
|
||||
}
|
||||
|
||||
val parsedFields = fieldParser.getObjectTypeParsedFields(
|
||||
objectType = testObjectType,
|
||||
storeOfRelations = storeOfRelations,
|
||||
objectTypeConflictingFieldsIds = listOf()
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
expected = listOf(field3, field4, field5),
|
||||
actual = parsedFields.sidebar
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should map hidden fields`() = runTest {
|
||||
|
||||
|
@ -233,8 +210,54 @@ class TestFieldsMappping {
|
|||
objectTypeConflictingFieldsIds = listOf()
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
expected = listOf(),
|
||||
actual = parsedFields.hidden
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test filter duplicates`() = runTest {
|
||||
|
||||
storeOfRelations.apply {
|
||||
merge(allSpaceRelations)
|
||||
}
|
||||
|
||||
val testObjectType = StubObjectType(
|
||||
recommendedFeaturedRelations = listOf(field1, field1, field2, field3).map { it.id },
|
||||
recommendedRelations = listOf(field1, field4, field4).map { it.id },
|
||||
recommendedFileRelations = listOf(field1, field2, field3, field4, field5, field5).map { it.id },
|
||||
recommendedHiddenRelations = listOf(field1, field2, field3, field4, field5, fieldCreatedDate, fieldCreatedDate).map { it.id },
|
||||
space = space
|
||||
)
|
||||
|
||||
storeOfObjectTypes.apply {
|
||||
merge(listOf(testObjectType, fieldAssigneeObjType2, fieldAssigneeObjType1))
|
||||
}
|
||||
|
||||
val parsedFields = fieldParser.getObjectTypeParsedFields(
|
||||
objectType = testObjectType,
|
||||
storeOfRelations = storeOfRelations,
|
||||
objectTypeConflictingFieldsIds = listOf()
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
expected = listOf(field1, field2, field3),
|
||||
actual = parsedFields.header
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
expected = listOf(field4),
|
||||
actual = parsedFields.sidebar
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
expected = listOf(field5),
|
||||
actual = parsedFields.file
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
expected = listOf(fieldCreatedDate),
|
||||
actual = parsedFields.hidden
|
||||
)
|
||||
}
|
||||
|
|
58
feature-properties/build.gradle
Normal file
58
feature-properties/build.gradle
Normal file
|
@ -0,0 +1,58 @@
|
|||
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_properties'
|
||||
}
|
||||
|
||||
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')
|
||||
|
||||
compileOnly libs.javaxInject
|
||||
|
||||
implementation libs.lifecycleViewModel
|
||||
implementation libs.lifecycleRuntime
|
||||
|
||||
implementation libs.appcompat
|
||||
implementation libs.compose
|
||||
implementation libs.fragmentCompose
|
||||
implementation libs.composeFoundation
|
||||
implementation libs.composeToolingPreview
|
||||
implementation libs.composeMaterial3
|
||||
implementation libs.navigationCompose
|
||||
|
||||
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.androidXTestCore
|
||||
testImplementation libs.mockitoKotlin
|
||||
testImplementation libs.coroutineTesting
|
||||
testImplementation libs.timberJUnit
|
||||
testImplementation libs.turbine
|
||||
}
|
2
feature-properties/src/main/AndroidManifest.xml
Normal file
2
feature-properties/src/main/AndroidManifest.xml
Normal file
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest/>
|
|
@ -0,0 +1,5 @@
|
|||
package com.anytypeio.anytype.feature_properties
|
||||
|
||||
class EditSpacePropertiesViewModel {
|
||||
//todo: implement later
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package com.anytypeio.anytype.feature_properties
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.anytypeio.anytype.domain.`object`.SetObjectDetails
|
||||
import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
|
||||
import com.anytypeio.anytype.domain.objects.StoreOfRelations
|
||||
import com.anytypeio.anytype.domain.primitives.SetObjectTypeRecommendedFields
|
||||
import com.anytypeio.anytype.domain.relations.CreateRelation
|
||||
import com.anytypeio.anytype.domain.resources.StringResourceProvider
|
||||
import com.anytypeio.anytype.feature_properties.add.EditTypePropertiesVmParams
|
||||
|
||||
class EditTypePropertiesViewModel(
|
||||
private val vmParams: EditTypePropertiesVmParams,
|
||||
private val storeOfRelations: StoreOfRelations,
|
||||
private val storeOfObjectTypes: StoreOfObjectTypes,
|
||||
private val stringResourceProvider: StringResourceProvider,
|
||||
private val createRelation: CreateRelation,
|
||||
private val setObjectDetails: SetObjectDetails,
|
||||
private val setObjectTypeRecommendedFields: SetObjectTypeRecommendedFields
|
||||
) : ViewModel() {
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
package com.anytypeio.anytype.feature_properties
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.anytypeio.anytype.domain.`object`.SetObjectDetails
|
||||
import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
|
||||
import com.anytypeio.anytype.domain.objects.StoreOfRelations
|
||||
import com.anytypeio.anytype.domain.primitives.SetObjectTypeRecommendedFields
|
||||
import com.anytypeio.anytype.domain.relations.CreateRelation
|
||||
import com.anytypeio.anytype.domain.resources.StringResourceProvider
|
||||
import com.anytypeio.anytype.feature_properties.add.EditTypePropertiesVmParams
|
||||
import javax.inject.Inject
|
||||
|
||||
class EditTypePropertiesViewModelFactory @Inject constructor(
|
||||
private val vmParams: EditTypePropertiesVmParams,
|
||||
private val storeOfRelations: StoreOfRelations,
|
||||
private val stringResourceProvider: StringResourceProvider,
|
||||
private val createRelation: CreateRelation,
|
||||
private val setObjectDetails: SetObjectDetails,
|
||||
private val storeOfObjectTypes: StoreOfObjectTypes,
|
||||
private val setObjectTypeRecommendedFields: SetObjectTypeRecommendedFields
|
||||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T =
|
||||
EditTypePropertiesViewModel(
|
||||
vmParams = vmParams,
|
||||
storeOfRelations = storeOfRelations,
|
||||
stringResourceProvider = stringResourceProvider,
|
||||
createRelation = createRelation,
|
||||
setObjectDetails = setObjectDetails,
|
||||
storeOfObjectTypes = storeOfObjectTypes,
|
||||
setObjectTypeRecommendedFields = setObjectTypeRecommendedFields
|
||||
) as T
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package com.anytypeio.anytype.feature_properties.add
|
||||
|
||||
sealed class UiEditTypePropertiesEvent {
|
||||
data class OnSearchQueryChanged(val query: String) : UiEditTypePropertiesEvent()
|
||||
data class OnCreate(val item: UiEditTypePropertiesItem.Create) : UiEditTypePropertiesEvent()
|
||||
data class OnTypeClicked(val item: UiEditTypePropertiesItem.Format) : UiEditTypePropertiesEvent()
|
||||
data class OnExistingClicked(val item: UiEditTypePropertiesItem.Default) : UiEditTypePropertiesEvent()
|
||||
data object OnCreateNewButtonClicked : UiEditTypePropertiesEvent()
|
||||
data object OnSaveButtonClicked : UiEditTypePropertiesEvent()
|
||||
data object OnEditPropertyScreenDismissed : UiEditTypePropertiesEvent()
|
||||
data class OnPropertyNameUpdate(val name: String) : UiEditTypePropertiesEvent()
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
package com.anytypeio.anytype.feature_properties.add
|
||||
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.Key
|
||||
import com.anytypeio.anytype.core_models.ObjectWrapper
|
||||
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.resources.StringResourceProvider
|
||||
|
||||
data class UiEditTypePropertiesState(
|
||||
val items: List<UiEditTypePropertiesItem>
|
||||
) {
|
||||
companion object {
|
||||
val EMPTY = UiEditTypePropertiesState(emptyList())
|
||||
|
||||
val DEFAULT_NEW_PROPERTY_FORMAT = RelationFormat.STATUS
|
||||
|
||||
//This is a list of formats that are available for creating new properties
|
||||
val PROPERTIES_FORMATS = listOf<RelationFormat>(
|
||||
RelationFormat.LONG_TEXT,
|
||||
RelationFormat.TAG,
|
||||
RelationFormat.STATUS,
|
||||
RelationFormat.NUMBER,
|
||||
RelationFormat.DATE,
|
||||
RelationFormat.FILE,
|
||||
RelationFormat.OBJECT,
|
||||
RelationFormat.CHECKBOX,
|
||||
RelationFormat.URL,
|
||||
RelationFormat.EMAIL,
|
||||
RelationFormat.PHONE
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
sealed class UiEditTypePropertiesItem {
|
||||
|
||||
abstract val id: Id
|
||||
|
||||
sealed class Section : UiEditTypePropertiesItem() {
|
||||
data class Types(
|
||||
override val id: Id = "section_properties_types_id"
|
||||
) : Section()
|
||||
|
||||
data class Existing(
|
||||
override val id: Id = "section_properties_existing_id"
|
||||
) : Section()
|
||||
}
|
||||
|
||||
data class Format(
|
||||
override val id: Id = "property_item_format_id_${format.ordinal}",
|
||||
val format: RelationFormat,
|
||||
val prettyName: String
|
||||
) : UiEditTypePropertiesItem()
|
||||
|
||||
data class Create(
|
||||
override val id: Id = ID,
|
||||
val format: RelationFormat = UiEditTypePropertiesState.Companion.DEFAULT_NEW_PROPERTY_FORMAT,
|
||||
val title: String
|
||||
) : UiEditTypePropertiesItem() {
|
||||
companion object {
|
||||
private const val ID = "create_new_property_id"
|
||||
}
|
||||
}
|
||||
|
||||
data class Default(
|
||||
override val id: Id,
|
||||
val format: RelationFormat,
|
||||
val propertyKey: Key,
|
||||
val title: String,
|
||||
) : UiEditTypePropertiesItem()
|
||||
}
|
||||
|
||||
data class EditTypePropertiesVmParams(
|
||||
val objectTypeId: Id,
|
||||
val spaceId: SpaceId
|
||||
)
|
||||
|
||||
sealed class UiEditTypePropertiesErrorState {
|
||||
data object Hidden : UiEditTypePropertiesErrorState()
|
||||
data class Show(val reason: Reason) : UiEditTypePropertiesErrorState()
|
||||
|
||||
sealed class Reason {
|
||||
data class ErrorAddingProperty(val msg: String) : Reason()
|
||||
data class ErrorCreatingProperty(val msg: String) : Reason()
|
||||
data class ErrorUpdatingProperty(val msg: String) : Reason()
|
||||
data class Other(val msg: String) : Reason()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//region MAPPING
|
||||
fun ObjectWrapper.Relation.mapToStateItem(
|
||||
stringResourceProvider: StringResourceProvider
|
||||
): UiEditTypePropertiesItem.Default? {
|
||||
val field = this
|
||||
if (field.key == Relations.DESCRIPTION) return null
|
||||
return UiEditTypePropertiesItem.Default(
|
||||
id = field.id,
|
||||
propertyKey = field.key,
|
||||
title = field.getName(stringResourceProvider),
|
||||
format = field.format
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension function to safely get a name for the relation.
|
||||
* If the name is blank, returns a default untitled title.
|
||||
*/
|
||||
fun ObjectWrapper.Relation.getName(stringResourceProvider: StringResourceProvider): String =
|
||||
if (name.isNullOrBlank()) {
|
||||
stringResourceProvider.getUntitledObjectTitle()
|
||||
} else {
|
||||
name!!
|
||||
}
|
||||
//endregion
|
|
@ -0,0 +1,16 @@
|
|||
package com.anytypeio.anytype.feature_properties.add.ui
|
||||
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.runtime.Composable
|
||||
import com.anytypeio.anytype.feature_properties.add.UiEditTypePropertiesEvent
|
||||
import com.anytypeio.anytype.feature_properties.add.UiEditTypePropertiesState
|
||||
import com.anytypeio.anytype.feature_properties.edit.UiEditPropertyState
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun AddFieldScreen(
|
||||
state: UiEditTypePropertiesState,
|
||||
uiStateEditProperty: UiEditPropertyState,
|
||||
event: (UiEditTypePropertiesEvent) -> Unit
|
||||
) {
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package com.anytypeio.anytype.feature_properties.edit
|
||||
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.Key
|
||||
import com.anytypeio.anytype.core_models.RelationFormat
|
||||
import com.anytypeio.anytype.presentation.objects.ObjectIcon
|
||||
|
||||
sealed class UiEditPropertyState {
|
||||
data object Hidden : UiEditPropertyState()
|
||||
sealed class Visible : UiEditPropertyState() {
|
||||
|
||||
data class Edit(
|
||||
val id: Id,
|
||||
val key: Key,
|
||||
val name: String,
|
||||
val formatName: String,
|
||||
val formatIcon: Int?,
|
||||
val format: RelationFormat,
|
||||
val limitObjectTypes: List<UiPropertyLimitTypeItem> = emptyList()
|
||||
) : Visible()
|
||||
|
||||
data class New(
|
||||
val name: String,
|
||||
val formatName: String,
|
||||
val formatIcon: Int?,
|
||||
val format: RelationFormat,
|
||||
val limitObjectTypes: List<UiPropertyLimitTypeItem> = emptyList()
|
||||
) : Visible()
|
||||
|
||||
data class View(
|
||||
val id: Id,
|
||||
val key: Key,
|
||||
val name: String,
|
||||
val formatName: String,
|
||||
val formatIcon: Int?,
|
||||
val format: RelationFormat,
|
||||
val limitObjectTypes: List<UiPropertyLimitTypeItem> = emptyList()
|
||||
) : Visible()
|
||||
}
|
||||
}
|
||||
|
||||
data class UiPropertyLimitTypeItem(
|
||||
val id: Id, val key: Key, val title: String, val icon: ObjectIcon
|
||||
)
|
|
@ -0,0 +1,20 @@
|
|||
package com.anytypeio.anytype.feature_properties.edit.ui
|
||||
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.anytypeio.anytype.feature_properties.edit.UiEditPropertyState
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun PropertyScreen(
|
||||
modifier: Modifier,
|
||||
uiState: UiEditPropertyState.Visible,
|
||||
onSaveButtonClicked: () -> Unit = {},
|
||||
onFormatClick: () -> Unit = {},
|
||||
onLimitTypesClick: () -> Unit = {},
|
||||
onCreateNewButtonClicked: () -> Unit = {},
|
||||
onDismissRequest: () -> Unit,
|
||||
onPropertyNameUpdate: (String) -> Unit
|
||||
) {
|
||||
}
|
|
@ -332,11 +332,11 @@
|
|||
<string name="relation_format_status">Select</string>
|
||||
<string name="relation_format_file">File & Media</string>
|
||||
<string name="relation_format_checkbox">Checkbox</string>
|
||||
<string name="relation_format_url">Url</string>
|
||||
<string name="relation_format_url">URL</string>
|
||||
<string name="relation_format_email">Email</string>
|
||||
<string name="relation_format_phone">Phone</string>
|
||||
<string name="relation_format_phone">Phone number</string>
|
||||
<string name="relation_format_emoji">Emoji</string>
|
||||
<string name="relation_format_object">Object</string>
|
||||
<string name="relation_format_object">Object relation</string>
|
||||
<string name="relation_format_relation">Relation</string>
|
||||
<string name="viewer_sorting_prefix">Then</string>
|
||||
<string name="enter_value">Enter value</string>
|
||||
|
@ -344,7 +344,7 @@
|
|||
<string name="add_sort">Add a sort</string>
|
||||
<string name="apply">Apply</string>
|
||||
<string name="add_filter">Add a filter</string>
|
||||
<string name="relation_format_tag">Multiselect</string>
|
||||
<string name="relation_format_tag">Multi-Select</string>
|
||||
<string name="unknown_number_of_objects">Unknown number of objects</string>
|
||||
<string name="sort_ascending">Ascending</string>
|
||||
<string name="sort_descending">Descending</string>
|
||||
|
@ -366,7 +366,7 @@
|
|||
|
||||
<string name="unknown_object_type">Unknown type</string>
|
||||
<string name="relation_format_short_text">Short text</string>
|
||||
<string name="relation_format_long_text">Long text</string>
|
||||
<string name="relation_format_long_text">Text</string>
|
||||
<string name="set_new_relation">Set new relation</string>
|
||||
<string name="relation_deleted">Deleted relation</string>
|
||||
|
||||
|
@ -918,6 +918,7 @@
|
|||
|
||||
<string name="generic_error">Something went wrong. Please try again.</string>
|
||||
<string name="type">Type</string>
|
||||
<string name="format">Format</string>
|
||||
<string name="space_info">Space info</string>
|
||||
<string name="creation_date">Creation date</string>
|
||||
<string name="created_by">Created by</string>
|
||||
|
@ -1964,4 +1965,18 @@ Please provide specific details of your needs here.</string>
|
|||
<string name="space_settings_icon_title">Edit picture</string>
|
||||
<string name="space_settings_save_button">Save</string>
|
||||
|
||||
<string name="object_type_add_property_screen_title">Add property</string>
|
||||
<string name="object_type_add_property_screen_section">Properties types</string>
|
||||
<string name="object_type_add_property_screen_create">Create property \'%1$s\'"</string>
|
||||
<string name="object_type_add_property_screen_section_types">Properties types"</string>
|
||||
<string name="object_type_add_property_screen_section_existing">Existing properties"</string>
|
||||
<string name="object_type_add_property_screen_search_hint">Search or create new"</string>
|
||||
|
||||
<string name="new_property_hint">New property</string>
|
||||
<string name="edit_property_limit_objects">Limit objects to</string>
|
||||
|
||||
<string name="add_property_error_create_new">Error while creating new property</string>
|
||||
<string name="add_property_error_update">Error while updating property</string>
|
||||
<string name="add_property_error_add">Error while adding property to type</string>
|
||||
|
||||
</resources>
|
|
@ -1,6 +1,7 @@
|
|||
package com.anytypeio.anytype.presentation.util
|
||||
|
||||
import android.content.Context
|
||||
import com.anytypeio.anytype.core_models.RelationFormat
|
||||
import com.anytypeio.anytype.core_models.RelativeDate
|
||||
import com.anytypeio.anytype.domain.resources.StringResourceProvider
|
||||
import com.anytypeio.anytype.presentation.R
|
||||
|
@ -31,4 +32,23 @@ class StringResourceProviderImpl @Inject constructor(private val context: Contex
|
|||
override fun getSetOfObjectsTitle(): String {
|
||||
return context.getString(R.string.object_set_of_title)
|
||||
}
|
||||
|
||||
override fun getPropertiesFormatPrettyString(format: RelationFormat): String {
|
||||
return when (format) {
|
||||
RelationFormat.LONG_TEXT, RelationFormat.SHORT_TEXT -> context.getString(R.string.relation_format_long_text)
|
||||
RelationFormat.NUMBER -> context.getString(R.string.relation_format_number)
|
||||
RelationFormat.STATUS -> context.getString(R.string.relation_format_status)
|
||||
RelationFormat.TAG -> context.getString(R.string.relation_format_tag)
|
||||
RelationFormat.DATE -> context.getString(R.string.relation_format_date)
|
||||
RelationFormat.FILE -> context.getString(R.string.relation_format_file)
|
||||
RelationFormat.CHECKBOX -> context.getString(R.string.relation_format_checkbox)
|
||||
RelationFormat.URL -> context.getString(R.string.relation_format_url)
|
||||
RelationFormat.EMAIL -> context.getString(R.string.relation_format_email)
|
||||
RelationFormat.PHONE -> context.getString(R.string.relation_format_phone)
|
||||
RelationFormat.EMOJI -> context.getString(R.string.relation_format_emoji)
|
||||
RelationFormat.OBJECT -> context.getString(R.string.relation_format_object)
|
||||
RelationFormat.RELATIONS -> context.getString(R.string.relation_format_relation)
|
||||
RelationFormat.UNDEFINED -> context.getString(R.string.undefined)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -67,3 +67,4 @@ include ':feature-chats'
|
|||
include ':feature-all-content'
|
||||
include ':feature-date'
|
||||
include ':feature-object-type'
|
||||
include ':feature-properties'
|
||||
|
|
|
@ -96,7 +96,7 @@ fun StubObjectType(
|
|||
uniqueKey: String? = MockDataFactory.randomUuid(),
|
||||
name: String = MockDataFactory.randomString(),
|
||||
objectType: String = MockDataFactory.randomString(),
|
||||
layout: Double = ObjectType.Layout.BASIC.code.toDouble(),
|
||||
layout: Double = ObjectType.Layout.OBJECT_TYPE.code.toDouble(),
|
||||
smartBlockTypes: List<Double> = emptyList(),
|
||||
isDeleted: Boolean? = null,
|
||||
isArchived: Boolean? = null,
|
||||
|
@ -109,6 +109,7 @@ fun StubObjectType(
|
|||
recommendedRelations: List<String> = emptyList(),
|
||||
recommendedHiddenRelations: List<String> = emptyList(),
|
||||
recommendedFeaturedRelations: List<String> = emptyList(),
|
||||
recommendedFileRelations: List<String> = emptyList(),
|
||||
space: Id? = null
|
||||
): ObjectWrapper.Type = ObjectWrapper.Type(
|
||||
map = mapOf(
|
||||
|
@ -129,6 +130,7 @@ fun StubObjectType(
|
|||
Relations.RECOMMENDED_RELATIONS to recommendedRelations,
|
||||
Relations.RECOMMENDED_HIDDEN_RELATIONS to recommendedHiddenRelations,
|
||||
Relations.RECOMMENDED_FEATURED_RELATIONS to recommendedFeaturedRelations,
|
||||
Relations.RECOMMENDED_FILE_RELATIONS to recommendedFileRelations,
|
||||
Relations.SPACE_ID to space
|
||||
)
|
||||
)
|
Loading…
Add table
Add a link
Reference in a new issue