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

DROID-2856 All content | Types & relations tabs (#1639)

This commit is contained in:
Konstantin Ivanov 2024-10-07 17:58:40 +02:00 committed by GitHub
parent 9c7f6ed8ab
commit 17eef47ca5
Signed by: github
GPG key ID: B5690EEEBB952194
16 changed files with 589 additions and 114 deletions

View file

@ -15,11 +15,13 @@ import com.anytypeio.anytype.domain.launch.GetDefaultObjectType
import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
import com.anytypeio.anytype.domain.misc.LocaleProvider
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.`object`.SetObjectDetails
import com.anytypeio.anytype.domain.objects.SetObjectListIsArchived
import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
import com.anytypeio.anytype.domain.page.CreateObject
import com.anytypeio.anytype.domain.search.SearchObjects
import com.anytypeio.anytype.domain.search.SubscriptionEventChannel
import com.anytypeio.anytype.domain.workspace.RemoveObjectsFromWorkspace
import com.anytypeio.anytype.domain.workspace.SpaceManager
import com.anytypeio.anytype.feature_allcontent.presentation.AllContentViewModel
import com.anytypeio.anytype.feature_allcontent.presentation.AllContentViewModelFactory
@ -138,6 +140,22 @@ object AllContentModule {
dispatchers: AppCoroutineDispatchers
): SetObjectListIsArchived = SetObjectListIsArchived(repo, dispatchers)
@JvmStatic
@Provides
@PerScreen
fun provideUpdateDetailUseCase(
repository: BlockRepository,
dispatchers: AppCoroutineDispatchers
): SetObjectDetails = SetObjectDetails(repository, dispatchers)
@Provides
@PerScreen
@JvmStatic
fun removeObjectFromWorkspace(
repo: BlockRepository,
dispatchers: AppCoroutineDispatchers
): RemoveObjectsFromWorkspace = RemoveObjectsFromWorkspace(repo, dispatchers)
@Module
interface Declarations {
@PerScreen

View file

@ -13,12 +13,16 @@ import com.anytypeio.anytype.ui.auth.account.DeletedAccountFragment
import com.anytypeio.anytype.ui.editor.EditorFragment
import com.anytypeio.anytype.ui.editor.EditorModalFragment
import com.anytypeio.anytype.ui.library.LibraryFragment
import com.anytypeio.anytype.ui.relations.RelationCreateFromScratchForObjectFragment
import com.anytypeio.anytype.ui.relations.RelationEditFragment
import com.anytypeio.anytype.ui.search.GlobalSearchFragment
import com.anytypeio.anytype.ui.sets.ObjectSetFragment
import com.anytypeio.anytype.ui.settings.RemoteFilesManageFragment
import com.anytypeio.anytype.ui.templates.EditorTemplateFragment.Companion.TYPE_TEMPLATE_EDIT
import com.anytypeio.anytype.ui.templates.EditorTemplateFragment.Companion.TYPE_TEMPLATE_SELECT
import com.anytypeio.anytype.ui.templates.TemplateSelectFragment
import com.anytypeio.anytype.ui.types.create.CreateObjectTypeFragment
import com.anytypeio.anytype.ui.types.create.TypeCreationScreen
import com.anytypeio.anytype.ui.types.edit.TypeEditFragment
import com.anytypeio.anytype.ui.widgets.collection.CollectionFragment
import timber.log.Timber
@ -260,4 +264,41 @@ class Navigator : AppNavigation {
)
)
}
override fun openTypeCreationScreen(name: String) {
navController?.navigate(
resId = R.id.openTypeCreationScreen,
args = CreateObjectTypeFragment.args(
typeName = name
)
)
}
override fun openRelationCreationScreen(id: Id, name: String, space: Id) {
navController?.navigate(
resId = R.id.openRelationCreationScreen,
args = RelationCreateFromScratchForObjectFragment.args(
ctx = id,
query = name,
space = space
)
)
}
override fun openRelationEditingScreen(
typeName: String,
id: Id,
iconUnicode: Int,
readOnly: Boolean
) {
navController?.navigate(
resId = R.id.openRelationEditingScreen,
args = RelationEditFragment.args(
typeName = typeName,
id = id,
iconUnicode = iconUnicode,
readOnly = readOnly
)
)
}
}

View file

@ -8,6 +8,7 @@ import android.view.ViewGroup
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.core.os.bundleOf
import androidx.fragment.app.setFragmentResultListener
import androidx.fragment.app.viewModels
import androidx.fragment.compose.content
import androidx.lifecycle.compose.collectAsStateWithLifecycle
@ -29,12 +30,22 @@ import com.anytypeio.anytype.feature_allcontent.presentation.AllContentViewModel
import com.anytypeio.anytype.feature_allcontent.presentation.AllContentViewModelFactory
import com.anytypeio.anytype.feature_allcontent.ui.AllContentNavigation.ALL_CONTENT_MAIN
import com.anytypeio.anytype.feature_allcontent.ui.AllContentWrapperScreen
import com.anytypeio.anytype.presentation.library.LibraryViewModel
import com.anytypeio.anytype.presentation.objects.ObjectIcon
import com.anytypeio.anytype.presentation.widgets.collection.Subscription
import com.anytypeio.anytype.ui.base.navigation
import com.anytypeio.anytype.ui.objects.creation.SelectObjectTypeFragment
import com.anytypeio.anytype.ui.relations.REQUEST_KEY_MODIFY_RELATION
import com.anytypeio.anytype.ui.relations.REQUEST_KEY_UNINSTALL_RELATION
import com.anytypeio.anytype.ui.relations.REQUEST_UNINSTALL_RELATION_ARG_ID
import com.anytypeio.anytype.ui.relations.REQUEST_UNINSTALL_RELATION_ARG_NAME
import com.anytypeio.anytype.ui.search.GlobalSearchFragment
import com.anytypeio.anytype.ui.settings.typography
import com.anytypeio.anytype.ui.types.edit.REQUEST_KEY_MODIFY_TYPE
import com.anytypeio.anytype.ui.types.edit.REQUEST_KEY_UNINSTALL_TYPE
import com.anytypeio.anytype.ui.types.edit.REQUEST_UNINSTALL_TYPE_ARG_ICON
import com.anytypeio.anytype.ui.types.edit.REQUEST_UNINSTALL_TYPE_ARG_ID
import com.anytypeio.anytype.ui.types.edit.REQUEST_UNINSTALL_TYPE_ARG_NAME
import javax.inject.Inject
import timber.log.Timber
@ -47,6 +58,35 @@ class AllContentFragment : BaseComposeFragment() {
private val space get() = argString(ARG_SPACE)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setFragmentResultListener(REQUEST_KEY_UNINSTALL_TYPE) { _, bundle ->
val id = requireNotNull(bundle.getString(REQUEST_UNINSTALL_TYPE_ARG_ID))
val name = requireNotNull(bundle.getString(REQUEST_UNINSTALL_TYPE_ARG_NAME))
vm.uninstallObject(id, LibraryViewModel.LibraryItem.TYPE, name)
}
setFragmentResultListener(REQUEST_KEY_MODIFY_TYPE) { _, bundle ->
val id = requireNotNull(bundle.getString(REQUEST_UNINSTALL_TYPE_ARG_ID))
val name = requireNotNull(bundle.getString(REQUEST_UNINSTALL_TYPE_ARG_NAME))
val icon = requireNotNull(bundle.getString(REQUEST_UNINSTALL_TYPE_ARG_ICON))
vm.updateObject(id, name, icon)
}
setFragmentResultListener(REQUEST_KEY_UNINSTALL_RELATION) { _, bundle ->
val id = requireNotNull(bundle.getString(REQUEST_UNINSTALL_RELATION_ARG_ID))
val name = requireNotNull(bundle.getString(REQUEST_UNINSTALL_RELATION_ARG_NAME))
vm.uninstallObject(id, LibraryViewModel.LibraryItem.RELATION, name)
}
setFragmentResultListener(REQUEST_KEY_MODIFY_RELATION) { _, bundle ->
val id = requireNotNull(bundle.getString(REQUEST_UNINSTALL_RELATION_ARG_ID))
val name = requireNotNull(bundle.getString(REQUEST_UNINSTALL_RELATION_ARG_NAME))
vm.updateObject(
id = id,
name = name,
icon = null
)
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
@ -70,6 +110,7 @@ class AllContentFragment : BaseComposeFragment() {
Timber.e(e, "Error while exiting to vault from all content")
}
}
is AllContentViewModel.Command.Back -> {
runCatching {
findNavController().popBackStack()
@ -77,6 +118,7 @@ class AllContentFragment : BaseComposeFragment() {
Timber.e(e, "Error while exiting back from all content")
}
}
is AllContentViewModel.Command.OpenGlobalSearch -> {
runCatching {
findNavController().navigate(
@ -89,6 +131,7 @@ class AllContentFragment : BaseComposeFragment() {
Timber.e(e, "Error while opening global search screen from all content")
}
}
is AllContentViewModel.Command.NavigateToEditor -> {
runCatching {
navigation().openDocument(
@ -100,6 +143,7 @@ class AllContentFragment : BaseComposeFragment() {
Timber.e(it, "Failed to open document from all content")
}
}
is AllContentViewModel.Command.NavigateToSetOrCollection -> {
runCatching {
navigation().openObjectSet(
@ -111,9 +155,35 @@ class AllContentFragment : BaseComposeFragment() {
Timber.e(it, "Failed to open object set from all content")
}
}
is AllContentViewModel.Command.SendToast -> {
is AllContentViewModel.Command.SendToast.UnexpectedLayout -> {
val message =
"${getString(R.string.all_content_error_unexpected_layout)}: ${command.layout}"
toast(message)
}
is AllContentViewModel.Command.SendToast.RelationRemoved -> {
val message =
"${getString(R.string.all_content_toast_relation_removed)}: ${command.name}"
toast(message)
}
is AllContentViewModel.Command.SendToast.TypeRemoved -> {
val message =
"${getString(R.string.all_content_toast_type_removed)}: ${command.name}"
toast(message)
}
is AllContentViewModel.Command.SendToast.Error -> {
toast(command.message)
}
is AllContentViewModel.Command.SendToast.ObjectArchived -> {
val message =
"${getString(R.string.all_content_toast_archived)}: ${command.name}"
toast(message)
}
is AllContentViewModel.Command.NavigateToBin -> {
runCatching {
navigation().launchCollections(
@ -125,6 +195,7 @@ class AllContentFragment : BaseComposeFragment() {
Timber.e(it, "Failed to open bin from all content")
}
}
is AllContentViewModel.Command.OpenTypeEditing -> {
runCatching {
navigation().openTypeEditingScreen(
@ -138,6 +209,44 @@ class AllContentFragment : BaseComposeFragment() {
Timber.e(it, "Failed to open type editing screen from all content")
}
}
is AllContentViewModel.Command.OpenTypeCreation -> {
runCatching {
navigation().openTypeCreationScreen(
name = command.name
)
}.onFailure {
toast("Failed to open type creation screen")
Timber.e(it, "Failed to open type creation screen from all content")
}
}
is AllContentViewModel.Command.OpenRelationCreation -> {
runCatching {
navigation().openRelationCreationScreen(
id = command.id,
name = command.name,
space = command.space
)
}.onFailure {
toast("Failed to open relation creation screen")
Timber.e(it, "Failed to open relation creation screen from all content")
}
}
is AllContentViewModel.Command.OpenRelationEditing -> {
runCatching {
navigation().openRelationEditingScreen(
typeName = command.typeName,
id = command.id,
iconUnicode = command.iconUnicode,
readOnly = command.readOnly
)
}.onFailure {
toast("Failed to open relation editing screen")
Timber.e(it, "Failed to open relation editing screen from all content")
}
}
}
}
}
@ -163,8 +272,8 @@ class AllContentFragment : BaseComposeFragment() {
canPaginate = vm.canPaginate.collectAsStateWithLifecycle().value,
onUpdateLimitSearch = vm::updateLimit,
uiContentState = vm.uiContentState.collectAsStateWithLifecycle().value,
onTypeClicked = vm::onTypeClicked,
onHomeClicked = vm::onHomeClicked,
onTypeClicked = vm::onTypeClicked,
onHomeClicked = vm::onHomeClicked,
onGlobalSearchClicked = vm::onGlobalSearchClicked,
onAddDocClicked = vm::onAddDockClicked,
onCreateObjectLongClicked = {
@ -176,7 +285,7 @@ class AllContentFragment : BaseComposeFragment() {
vm.onCreateObjectOfTypeClicked(it)
}
}
dialog.show(childFragmentManager,null)
dialog.show(childFragmentManager, null)
},
onBackClicked = vm::onBackClicked,
moveToBin = vm::proceedWithMoveToBin,
@ -186,7 +295,8 @@ class AllContentFragment : BaseComposeFragment() {
}.onFailure {
Timber.e(it, "Error while opening space switcher from all-content screen")
}
}
},
onRelationClicked = vm::onRelationClicked
)
}
}

View file

@ -71,7 +71,8 @@ fun RelationEditScreen(vm: RelationEditViewModel, preparedName: String, readOnly
vm.updateRelationDetails(name = inputValue.value.trim())
},
imeOptions = ImeOptions.Done,
shouldMoveCursor = preparedName.trim().isNotEmpty()
shouldMoveCursor = preparedName.trim().isNotEmpty(),
isEditable = readOnly.not()
)
}
@ -126,7 +127,8 @@ fun RelationEditWidget(
state: RelationEditState,
imeOptions: ImeOptions = ImeOptions.Default,
onImeDoneClick: (name: String) -> Unit = {},
shouldMoveCursor: Boolean
shouldMoveCursor: Boolean,
isEditable: Boolean = true
) {
val focusRequester = remember { FocusRequester() }
@ -149,6 +151,7 @@ fun RelationEditWidget(
nameValid.value = this.isNotEmpty()
}
},
enabled = isEditable,
modifier = Modifier
.focusRequester(focusRequester)
.padding(start = PaddingStart)

View file

@ -414,6 +414,15 @@
<action
android:id="@+id/openTypeEditingScreen"
app:destination="@id/typeEditingFragment" />
<action
android:id="@+id/openTypeCreationScreen"
app:destination="@id/typeCreationFragment" />
<action
android:id="@+id/openRelationCreationScreen"
app:destination="@id/relationCreationFragment" />
<action
android:id="@+id/openRelationEditingScreen"
app:destination="@id/relationEditingFragment" />
<action
android:id="@+id/actionOpenVault"
app:destination="@id/vaultScreen"
@ -422,6 +431,10 @@
<action
android:id="@+id/actionOpenGlobalSearch"
app:destination="@id/globalSearchScreen" />
<action
android:id="@+id/actionOpenTypeCreationScreen"
app:destination="@id/typeCreationFragment" />
<action
android:id="@+id/actionOpenSpaceSwitcher"
app:destination="@id/selectSpaceScreen"/>

View file

@ -26,6 +26,7 @@ object ObjectTypeIds {
const val RELATION_OPTION = "ot-relationOption"
const val SPACE = "ot-space"
const val CHAT_DERIVED = "ot-chatDerived"
const val CHAT = "ot-chat"
const val DEFAULT_OBJECT_TYPE_PREFIX = "ot-"

View file

@ -1,7 +1,6 @@
package com.anytypeio.anytype.feature_allcontent.models
import androidx.compose.runtime.Immutable
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.DVSortType
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.Key
@ -9,6 +8,7 @@ import com.anytypeio.anytype.core_models.MarketplaceObjectTypeIds
import com.anytypeio.anytype.core_models.ObjectType
import com.anytypeio.anytype.core_models.ObjectTypeUniqueKeys
import com.anytypeio.anytype.core_models.ObjectWrapper
import com.anytypeio.anytype.core_models.RelationFormat
import com.anytypeio.anytype.core_models.Relations
import com.anytypeio.anytype.core_models.Relations.SOURCE_OBJECT
import com.anytypeio.anytype.core_models.ext.DateParser
@ -26,7 +26,7 @@ import com.anytypeio.anytype.presentation.objects.getProperType
//region STATE
@Immutable
enum class AllContentTab {
PAGES, LISTS, MEDIA, BOOKMARKS, FILES, TYPES
PAGES, LISTS, MEDIA, BOOKMARKS, FILES, TYPES, RELATIONS
}
sealed class AllContentMenuMode {
@ -67,6 +67,13 @@ sealed class AllContentSort {
override val canGroupByDate: Boolean = true,
override val isSelected: Boolean = false
) : AllContentSort()
data class ByDateUsed(
override val relationKey: RelationKey = RelationKey(Relations.LAST_USED_DATE),
override val sortType: DVSortType = DVSortType.DESC,
override val canGroupByDate: Boolean = false,
override val isSelected: Boolean = false
) : AllContentSort()
}
//endregion
@ -119,7 +126,7 @@ sealed class UiContentItem {
val layout: ObjectType.Layout? = null,
val icon: ObjectIcon = ObjectIcon.None,
val lastModifiedDate: Long = 0L,
val createdDate: Long = 0L,
val createdDate: Long = 0L
) : UiContentItem()
data class Type(
@ -133,6 +140,15 @@ sealed class UiContentItem {
val dependentData: DependentData = DependentData.None
) : UiContentItem()
data class Relation(
override val id: Id,
val name: String,
val format: RelationFormat,
val sourceObject: Id? = null,
val readOnly: Boolean = true,
val editable: Boolean = true,
) : UiContentItem()
companion object {
const val TODAY_ID = "TodayId"
const val YESTERDAY_ID = "YesterdayId"
@ -247,11 +263,29 @@ fun ObjectWrapper.Basic.toAllContentType(
)
}
fun List<ObjectWrapper.Basic>.toUiContentRelations(): List<UiContentItem.Relation> {
return map { it.toAllContentRelation() }
}
fun ObjectWrapper.Basic.toAllContentRelation(): UiContentItem.Relation {
val relation = ObjectWrapper.Relation(map)
val obj = this
return UiContentItem.Relation(
id = relation.id,
name = obj.name.orEmpty(),
format = relation.format,
sourceObject = map[SOURCE_OBJECT]?.toString(),
readOnly = obj.restrictions.contains(ObjectRestriction.DELETE),
editable = !obj.restrictions.contains(ObjectRestriction.DETAILS)
)
}
fun AllContentSort.toAnalyticsSortType(): Pair<String, String> {
return when (this) {
is AllContentSort.ByName -> "Name" to sortType.toAnalyticsSortType()
is AllContentSort.ByDateUpdated -> "Updated" to sortType.toAnalyticsSortType()
is AllContentSort.ByDateCreated -> "Created" to sortType.toAnalyticsSortType()
is AllContentSort.ByDateUsed -> "Used" to sortType.toAnalyticsSortType()
}
}
@ -271,6 +305,7 @@ fun AllContentTab.toAnalyticsTabType(): String {
AllContentTab.BOOKMARKS -> "Bookmarks"
AllContentTab.FILES -> "Files"
AllContentTab.TYPES -> "Types"
AllContentTab.RELATIONS -> "Relations"
}
}

View file

@ -10,6 +10,9 @@ import com.anytypeio.anytype.core_models.ObjectTypeUniqueKeys
import com.anytypeio.anytype.core_models.RelationFormat
import com.anytypeio.anytype.core_models.Relations
import com.anytypeio.anytype.domain.library.StoreSearchParams
import com.anytypeio.anytype.presentation.search.ObjectSearchConstants.defaultKeys
import com.anytypeio.anytype.presentation.search.ObjectSearchConstants.defaultKeysObjectType
import com.anytypeio.anytype.presentation.search.ObjectSearchConstants.defaultRelationKeys
val allContentTabLayouts = mapOf(
AllContentTab.PAGES to listOf(
@ -46,6 +49,11 @@ fun createSubscriptionParams(
limit: Int,
subscriptionId: String
): StoreSearchParams {
val keys = when (activeTab) {
AllContentTab.TYPES -> defaultKeysObjectType
AllContentTab.RELATIONS -> defaultRelationKeys
else -> defaultKeys
}
val (filters, sorts) = activeTab.filtersForSubscribe(
spaces = listOf(spaceId),
activeSort = activeSort,
@ -55,33 +63,7 @@ fun createSubscriptionParams(
return StoreSearchParams(
filters = filters,
sorts = sorts,
keys = listOf(
Relations.ID,
Relations.SPACE_ID,
Relations.TARGET_SPACE_ID,
Relations.UNIQUE_KEY,
Relations.NAME,
Relations.ICON_IMAGE,
Relations.ICON_EMOJI,
Relations.ICON_OPTION,
Relations.TYPE,
Relations.LAYOUT,
Relations.IS_ARCHIVED,
Relations.IS_DELETED,
Relations.IS_HIDDEN,
Relations.SNIPPET,
Relations.DONE,
Relations.IDENTITY_PROFILE_LINK,
Relations.RESTRICTIONS,
Relations.SIZE_IN_BYTES,
Relations.FILE_MIME_TYPE,
Relations.FILE_EXT,
Relations.LAST_OPENED_DATE,
Relations.LAST_MODIFIED_DATE,
Relations.CREATED_DATE,
Relations.LINKS,
Relations.BACKLINKS
),
keys = keys,
limit = limit,
subscription = subscriptionId
)
@ -145,6 +127,38 @@ fun AllContentTab.filtersForSubscribe(
value = ObjectTypeIds.CHAT_DERIVED
)
)
add(
DVFilter(
relation = Relations.UNIQUE_KEY,
condition = DVFilterCondition.NOT_EQUAL,
value = ObjectTypeIds.CHAT
)
)
}
val sorts = listOf(activeSort.toDVSort())
return filters to sorts
}
AllContentTab.RELATIONS -> {
val filters = buildList {
addAll(buildDeletedFilter())
add(buildSpaceIdFilter(spaces))
if (limitedObjectIds.isNotEmpty()) {
add(buildLimitedObjectIdsFilter(limitedObjectIds = limitedObjectIds))
}
add(
DVFilter(
relation = Relations.LAYOUT,
condition = DVFilterCondition.EQUAL,
value = ObjectType.Layout.RELATION.code.toDouble()
),
)
add(
DVFilter(
relation = Relations.UNIQUE_KEY,
condition = DVFilterCondition.NOT_EQUAL,
value = ObjectTypeIds.CHAT_DERIVED
)
)
}
val sorts = listOf(activeSort.toDVSort())
return filters to sorts
@ -248,5 +262,12 @@ fun AllContentSort.toDVSort(): DVSort {
relationFormat = RelationFormat.LONG_TEXT,
includeTime = false
)
is AllContentSort.ByDateUsed -> DVSort(
relationKey = relationKey.key,
type = sortType,
relationFormat = RelationFormat.DATE,
includeTime = true,
)
}
}

View file

@ -9,16 +9,20 @@ import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.ObjectWrapper
import com.anytypeio.anytype.core_models.Relations
import com.anytypeio.anytype.core_models.primitives.SpaceId
import com.anytypeio.anytype.core_ui.extensions.simpleIcon
import com.anytypeio.anytype.core_utils.ext.orNull
import com.anytypeio.anytype.domain.all_content.RestoreAllContentState
import com.anytypeio.anytype.domain.all_content.UpdateAllContentState
import com.anytypeio.anytype.domain.base.fold
import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
import com.anytypeio.anytype.domain.misc.LocaleProvider
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.`object`.SetObjectDetails
import com.anytypeio.anytype.domain.objects.SetObjectListIsArchived
import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
import com.anytypeio.anytype.domain.page.CreateObject
import com.anytypeio.anytype.domain.search.SearchObjects
import com.anytypeio.anytype.domain.workspace.RemoveObjectsFromWorkspace
import com.anytypeio.anytype.feature_allcontent.models.AllContentMenuMode
import com.anytypeio.anytype.feature_allcontent.models.AllContentSort
import com.anytypeio.anytype.feature_allcontent.models.AllContentTab
@ -35,6 +39,7 @@ import com.anytypeio.anytype.feature_allcontent.models.toAnalyticsModeType
import com.anytypeio.anytype.feature_allcontent.models.toAnalyticsSortType
import com.anytypeio.anytype.feature_allcontent.models.toAnalyticsTabType
import com.anytypeio.anytype.feature_allcontent.models.toUiContentItems
import com.anytypeio.anytype.feature_allcontent.models.toUiContentRelations
import com.anytypeio.anytype.feature_allcontent.models.toUiContentTypes
import com.anytypeio.anytype.presentation.analytics.AnalyticSpaceHelperDelegate
import com.anytypeio.anytype.presentation.extension.sendAnalyticsAllContentChangeMode
@ -47,6 +52,7 @@ import com.anytypeio.anytype.presentation.extension.sendAnalyticsAllContentToBin
import com.anytypeio.anytype.presentation.extension.sendAnalyticsObjectCreateEvent
import com.anytypeio.anytype.presentation.home.OpenObjectNavigation
import com.anytypeio.anytype.presentation.home.navigation
import com.anytypeio.anytype.presentation.library.LibraryViewModel.LibraryItem
import com.anytypeio.anytype.presentation.objects.getCreateObjectParams
import java.time.Instant
import java.time.LocalDate
@ -77,6 +83,7 @@ import timber.log.Timber
* ViewState: @see [UiContentState]
* Factory: @see [AllContentViewModelFactory]
* Screen: @see [com.anytypeio.anytype.feature_allcontent.ui.AllContentWrapperScreen]
* Models: @see [com.anytypeio.anytype.feature_allcontent.models.UiContentState]
*/
class AllContentViewModel(
@ -91,7 +98,9 @@ class AllContentViewModel(
private val searchObjects: SearchObjects,
private val localeProvider: LocaleProvider,
private val createObject: CreateObject,
private val setObjectListIsArchived: SetObjectListIsArchived
private val setObjectListIsArchived: SetObjectListIsArchived,
private val setObjectDetails: SetObjectDetails,
private val removeObjectsFromWorkspace: RemoveObjectsFromWorkspace
) : ViewModel(), AnalyticSpaceHelperDelegate by analyticSpaceHelperDelegate {
private val searchResultIds = MutableStateFlow<List<Id>>(emptyList())
@ -251,29 +260,43 @@ class AllContentViewModel(
activeSort: AllContentSort,
activeTab: AllContentTab
): List<UiContentItem> {
if (activeTab == AllContentTab.TYPES) {
val items = objectWrappers.toUiContentTypes(
urlBuilder = urlBuilder
)
return items
} else {
val items = objectWrappers.toUiContentItems(
space = vmParams.spaceId,
urlBuilder = urlBuilder,
objectTypes = storeOfObjectTypes.getAll()
)
return when (activeSort) {
is AllContentSort.ByDateCreated -> {
groupItemsByDate(items = items, isSortByDateCreated = true)
}
return when (activeTab) {
AllContentTab.TYPES -> {
val items = objectWrappers.toUiContentTypes(
urlBuilder = urlBuilder
)
items
}
is AllContentSort.ByDateUpdated -> {
groupItemsByDate(items = items, isSortByDateCreated = false)
}
AllContentTab.RELATIONS -> {
val items = objectWrappers.toUiContentRelations()
items
}
is AllContentSort.ByName -> {
items
else -> {
val items = objectWrappers.toUiContentItems(
space = vmParams.spaceId,
urlBuilder = urlBuilder,
objectTypes = storeOfObjectTypes.getAll()
)
val result = when (activeSort) {
is AllContentSort.ByDateCreated -> {
groupItemsByDate(items = items, isSortByDateCreated = true)
}
is AllContentSort.ByDateUpdated -> {
groupItemsByDate(items = items, isSortByDateCreated = false)
}
is AllContentSort.ByName -> {
items
}
is AllContentSort.ByDateUsed -> {
items
}
}
result
}
}
}
@ -364,34 +387,9 @@ class AllContentViewModel(
) { mode, sort, tabs ->
Triple(mode, sort, tabs)
}.collectLatest { (mode, sort, tabs) ->
val uiMode = if (tabs.selectedTab == AllContentTab.TYPES) {
listOf()
} else {
listOf(
AllContentMenuMode.AllContent(isSelected = mode == UiTitleState.AllContent),
AllContentMenuMode.Unlinked(isSelected = mode == UiTitleState.OnlyUnlinked)
)
}
val uiMode = tabs.selectedTab.menu(mode)
val container = MenuSortsItem.Container(sort = sort)
val uiSorts = if (tabs.selectedTab == AllContentTab.TYPES) {
listOf(
MenuSortsItem.Sort(
sort = AllContentSort.ByName(isSelected = sort is AllContentSort.ByName)
)
)
} else {
listOf(
MenuSortsItem.Sort(
sort = AllContentSort.ByName(isSelected = sort is AllContentSort.ByName)
),
MenuSortsItem.Sort(
sort = AllContentSort.ByDateUpdated(isSelected = sort is AllContentSort.ByDateUpdated)
),
MenuSortsItem.Sort(
sort = AllContentSort.ByDateCreated(isSelected = sort is AllContentSort.ByDateCreated)
)
)
}
val uiSorts = tabs.selectedTab.sorts(activeSort = sort)
val uiSortTypes = listOf(
MenuSortsItem.SortType(
sort = sort,
@ -414,13 +412,72 @@ class AllContentViewModel(
}
}
fun AllContentTab.sorts(activeSort: AllContentSort): List<MenuSortsItem.Sort> {
return when (this) {
AllContentTab.TYPES -> {
listOf(
MenuSortsItem.Sort(
sort = AllContentSort.ByName(isSelected = activeSort is AllContentSort.ByName)
),
MenuSortsItem.Sort(
sort = AllContentSort.ByDateUsed(isSelected = activeSort is AllContentSort.ByDateUsed)
)
)
}
AllContentTab.RELATIONS -> {
listOf(
MenuSortsItem.Sort(
sort = AllContentSort.ByName(isSelected = activeSort is AllContentSort.ByName)
)
)
}
else -> {
listOf(
MenuSortsItem.Sort(
sort = AllContentSort.ByName(isSelected = activeSort is AllContentSort.ByName)
),
MenuSortsItem.Sort(
sort = AllContentSort.ByDateUpdated(isSelected = activeSort is AllContentSort.ByDateUpdated)
),
MenuSortsItem.Sort(
sort = AllContentSort.ByDateCreated(isSelected = activeSort is AllContentSort.ByDateCreated)
)
)
}
}
}
fun AllContentTab.menu(uiTitleState: UiTitleState): List<AllContentMenuMode> {
return when (this) {
AllContentTab.TYPES, AllContentTab.RELATIONS -> listOf()
else -> {
listOf(
AllContentMenuMode.AllContent(isSelected = uiTitleState == UiTitleState.AllContent),
AllContentMenuMode.Unlinked(isSelected = uiTitleState == UiTitleState.OnlyUnlinked)
)
}
}
}
fun AllContentTab.updateInitialState() {
return when (this) {
AllContentTab.TYPES -> {
sortState.value = AllContentSort.ByName()
userInput.value = DEFAULT_QUERY
uiTitleState.value = UiTitleState.AllContent
}
AllContentTab.RELATIONS -> {
sortState.value = AllContentSort.ByName()
userInput.value = DEFAULT_QUERY
uiTitleState.value = UiTitleState.AllContent
}
else -> {}
}
}
fun onTabClicked(tab: AllContentTab) {
Timber.d("onTabClicked: $tab")
if (tab == AllContentTab.TYPES) {
sortState.value = AllContentSort.ByName()
userInput.value = DEFAULT_QUERY
uiTitleState.value = UiTitleState.AllContent
}
tab.updateInitialState()
shouldScrollToTopItems = true
resetLimit()
uiItemsState.value = emptyList()
@ -463,6 +520,9 @@ class AllContentViewModel(
is AllContentSort.ByName -> {
sort.copy(isSelected = true)
}
is AllContentSort.ByDateUsed -> {
sort.copy(isSelected = true)
}
}
shouldScrollToTopItems = true
uiItemsState.value = emptyList()
@ -554,7 +614,8 @@ class AllContentViewModel(
}
is OpenObjectNavigation.UnexpectedLayoutError -> {
commands.emit(Command.SendToast("Unexpected layout: ${navigation.layout}"))
Timber.e("Unexpected layout: ${navigation.layout}")
commands.emit(Command.SendToast.UnexpectedLayout(navigation.layout?.name.orEmpty()))
}
}
}
@ -616,12 +677,26 @@ class AllContentViewModel(
}
fun onTypeClicked(item: UiContentItem.Type) {
Timber.d("onTypeClicked: ${item.id}")
Timber.d("onTypeClicked: $item")
viewModelScope.launch {
commands.emit(Command.OpenTypeEditing(item))
}
}
fun onRelationClicked(item: UiContentItem.Relation) {
Timber.d("onRelationClicked: $item")
viewModelScope.launch {
commands.emit(
Command.OpenRelationEditing(
typeName = item.name,
id = item.id,
iconUnicode = item.format.simpleIcon() ?: 0,
readOnly = item.readOnly
)
)
}
}
fun onStart() {
Timber.d("onStart")
setupUiStateFlow()
@ -648,9 +723,11 @@ class AllContentViewModel(
setObjectListIsArchived.async(params).fold(
onSuccess = { ids ->
Timber.d("Successfully archived object: $ids")
commands.emit(Command.SendToast.ObjectArchived(item.name))
},
onFailure = { e ->
Timber.e(e, "Error while archiving object")
commands.emit(Command.SendToast.Error("Error while archiving object"))
}
)
}
@ -685,12 +762,72 @@ class AllContentViewModel(
val useHistory: Boolean = true
)
//region Types and Relations action
fun updateObject(id: String, name: String, icon: String?) {
viewModelScope.launch {
setObjectDetails.execute(
SetObjectDetails.Params(
ctx = id,
details = mapOf(
Relations.NAME to name,
Relations.ICON_EMOJI to icon.orNull(),
)
)
).fold(
onFailure = {
Timber.e(it, "Error while updating object details")
},
onSuccess = {
// do nothing
}
)
}
}
fun uninstallObject(id: Id, type: LibraryItem, name: String) {
viewModelScope.launch {
removeObjectsFromWorkspace.execute(
RemoveObjectsFromWorkspace.Params(listOf(id))
).fold(
onFailure = {
Timber.e(it, "Error while uninstalling object")
commands.emit(Command.SendToast.Error("Error while uninstalling object"))
},
onSuccess = {
when (type) {
LibraryItem.TYPE -> {
commands.emit(Command.SendToast.TypeRemoved(name))
}
LibraryItem.RELATION -> {
commands.emit(Command.SendToast.RelationRemoved(name))
}
}
}
)
}
}
//endregion
sealed class Command {
data class NavigateToEditor(val id: Id, val space: Id) : Command()
data class NavigateToSetOrCollection(val id: Id, val space: Id) : Command()
data class NavigateToBin(val space: Id) : Command()
data class SendToast(val message: String) : Command()
sealed class SendToast: Command() {
data class Error(val message: String) : SendToast()
data class RelationRemoved(val name: String) : SendToast()
data class TypeRemoved(val name: String) : SendToast()
data class UnexpectedLayout(val layout: String) : SendToast()
data class ObjectArchived(val name: String) : SendToast()
}
data class OpenTypeEditing(val item: UiContentItem.Type) : Command()
data class OpenTypeCreation(val name: String): Command()
data class OpenRelationEditing(
val typeName: String,
val id: Id,
val iconUnicode: Int,
val readOnly: Boolean
) : Command()
data class OpenRelationCreation(val id: Id, val name: String, val space: Id): Command()
data object OpenGlobalSearch : Command()
data object ExitToVault : Command()
data object Back : Command()

View file

@ -8,10 +8,12 @@ import com.anytypeio.anytype.domain.all_content.UpdateAllContentState
import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
import com.anytypeio.anytype.domain.misc.LocaleProvider
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.`object`.SetObjectDetails
import com.anytypeio.anytype.domain.objects.SetObjectListIsArchived
import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
import com.anytypeio.anytype.domain.page.CreateObject
import com.anytypeio.anytype.domain.search.SearchObjects
import com.anytypeio.anytype.domain.workspace.RemoveObjectsFromWorkspace
import com.anytypeio.anytype.feature_allcontent.presentation.AllContentViewModel.VmParams
import com.anytypeio.anytype.presentation.analytics.AnalyticSpaceHelperDelegate
import javax.inject.Inject
@ -28,7 +30,9 @@ class AllContentViewModelFactory @Inject constructor(
private val searchObjects: SearchObjects,
private val localeProvider: LocaleProvider,
private val createObject: CreateObject,
private val setObjectListIsArchived: SetObjectListIsArchived
private val setObjectListIsArchived: SetObjectListIsArchived,
private val setObjectDetails: SetObjectDetails,
private val removeObjectsFromWorkspace: RemoveObjectsFromWorkspace,
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T =
@ -44,6 +48,8 @@ class AllContentViewModelFactory @Inject constructor(
searchObjects = searchObjects,
localeProvider = localeProvider,
createObject = createObject,
setObjectListIsArchived = setObjectListIsArchived
setObjectListIsArchived = setObjectListIsArchived,
setObjectDetails = setObjectDetails,
removeObjectsFromWorkspace = removeObjectsFromWorkspace,
) as T
}

View file

@ -91,6 +91,7 @@ fun AllContentMenu(
is AllContentSort.ByName -> item.sort.copy(sortType = item.sortType)
is AllContentSort.ByDateCreated -> item.sort.copy(sortType = item.sortType)
is AllContentSort.ByDateUpdated -> item.sort.copy(sortType = item.sortType)
is AllContentSort.ByDateUsed -> item.sort.copy(sortType = item.sortType)
}
onSortClick(updatedSort)
}
@ -201,6 +202,7 @@ private fun AllContentSort.title(): String = stringResource(
is AllContentSort.ByDateCreated -> R.string.all_content_sort_date_created
is AllContentSort.ByDateUpdated -> R.string.all_content_sort_date_updated
is AllContentSort.ByName -> R.string.all_content_sort_name
is AllContentSort.ByDateUsed -> R.string.all_content_sort_date_used
}
)
@ -208,7 +210,7 @@ private fun AllContentSort.title(): String = stringResource(
private fun DVSortType.title(sort: AllContentSort): String = when (this) {
DVSortType.ASC -> {
when (sort) {
is AllContentSort.ByDateCreated, is AllContentSort.ByDateUpdated -> stringResource(
is AllContentSort.ByDateCreated, is AllContentSort.ByDateUpdated, is AllContentSort.ByDateUsed -> stringResource(
id = R.string.all_content_sort_date_asc
)
@ -218,7 +220,9 @@ private fun DVSortType.title(sort: AllContentSort): String = when (this) {
DVSortType.DESC -> {
when (sort) {
is AllContentSort.ByDateCreated, is AllContentSort.ByDateUpdated -> stringResource(
is AllContentSort.ByDateCreated,
is AllContentSort.ByDateUpdated,
is AllContentSort.ByDateUsed -> stringResource(
id = R.string.all_content_sort_date_desc
)

View file

@ -6,6 +6,7 @@ import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
@ -52,6 +53,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
@ -63,6 +65,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.anytypeio.anytype.core_models.primitives.SpaceId
import com.anytypeio.anytype.core_ui.common.DefaultPreviews
import com.anytypeio.anytype.core_ui.extensions.simpleIcon
import com.anytypeio.anytype.core_ui.foundation.DismissBackground
import com.anytypeio.anytype.core_ui.foundation.Divider
import com.anytypeio.anytype.core_ui.foundation.components.BottomNavigationMenu
@ -111,6 +114,7 @@ fun AllContentWrapperScreen(
onSortClick: (AllContentSort) -> Unit,
onItemClicked: (UiContentItem.Item) -> Unit,
onTypeClicked: (UiContentItem.Type) -> Unit,
onRelationClicked: (UiContentItem.Relation) -> Unit,
onBinClick: () -> Unit,
canPaginate: Boolean,
onUpdateLimitSearch: () -> Unit,
@ -163,7 +167,8 @@ fun AllContentWrapperScreen(
onCreateObjectLongClicked = onCreateObjectLongClicked,
onBackClicked = onBackClicked,
onBackLongClicked = onBackLongClicked,
moveToBin = moveToBin
moveToBin = moveToBin,
onRelationClicked = onRelationClicked
)
}
@ -180,6 +185,7 @@ fun AllContentMainScreen(
onSortClick: (AllContentSort) -> Unit,
onItemClicked: (UiContentItem.Item) -> Unit,
onTypeClicked: (UiContentItem.Type) -> Unit,
onRelationClicked: (UiContentItem.Relation) -> Unit,
onBinClick: () -> Unit,
lazyListState: LazyListState,
uiContentState: UiContentState,
@ -295,7 +301,8 @@ fun AllContentMainScreen(
onTypeClicked = onTypeClicked,
uiContentState = uiContentState,
lazyListState = lazyListState,
moveToBin = moveToBin
moveToBin = moveToBin,
onRelationClicked = onRelationClicked
)
}
}
@ -332,6 +339,7 @@ private fun ContentItems(
uiItemsState: List<UiContentItem>,
onItemClicked: (UiContentItem.Item) -> Unit,
onTypeClicked: (UiContentItem.Type) -> Unit,
onRelationClicked: (UiContentItem.Relation) -> Unit,
uiContentState: UiContentState,
lazyListState: LazyListState,
moveToBin: (UiContentItem.Item) -> Unit
@ -350,6 +358,7 @@ private fun ContentItems(
is UiContentItem.Group -> "group"
is UiContentItem.Item -> "item"
is UiContentItem.Type -> "type"
is UiContentItem.Relation -> "relation"
}
}
) { index ->
@ -400,6 +409,19 @@ private fun ContentItems(
item = item
)
}
is UiContentItem.Relation -> {
Relation(
modifier = Modifier
.padding(horizontal = 16.dp)
.bottomBorder()
.animateItem()
.noRippleClickable {
onRelationClicked(item)
},
item = item
)
}
}
}
if (uiContentState is UiContentState.Paging) {
@ -414,6 +436,9 @@ private fun ContentItems(
}
}
}
item {
Spacer(modifier = Modifier.height(200.dp))
}
}
LaunchedEffect(key1 = uiContentState) {
@ -478,7 +503,8 @@ fun PreviewMainScreen() {
onCreateObjectLongClicked = {},
onBackClicked = {},
moveToBin = {},
onBackLongClicked = {}
onBackLongClicked = {},
onRelationClicked = {}
)
}
@ -561,6 +587,40 @@ private fun Type(
}
}
@Composable
private fun Relation(
modifier: Modifier,
item: UiContentItem.Relation
) {
Row(
modifier = modifier.fillMaxWidth(),
verticalAlignment = CenterVertically
) {
Box(
modifier = Modifier
.padding(start = 0.dp, top = 14.dp, end = 12.dp, bottom = 14.dp)
.wrapContentSize()
) {
item.format.simpleIcon()?.let {
Image(
painter = painterResource(id = it),
contentDescription = "Relation format icon",
modifier = Modifier.size(24.dp)
)
}
}
val name = item.name.trim().ifBlank { stringResource(R.string.untitled) }
Text(
text = name,
style = PreviewTitle1Medium,
color = colorResource(id = R.color.text_primary),
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
}
@Composable
fun AllContentItemIcon(
icon: ObjectIcon?,
@ -714,7 +774,7 @@ fun SwipeToDismissListItems(
false
}
},
positionalThreshold = { it * .30f }
positionalThreshold = { it * .5f }
)
LaunchedEffect(key1 = isRemoved) {

View file

@ -8,7 +8,7 @@ import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.snapping.rememberSnapFlingBehavior
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
@ -18,6 +18,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
@ -215,7 +216,6 @@ fun AllContentTabs(
modifier = Modifier
.fillMaxWidth()
.height(40.dp),
horizontalArrangement = Arrangement.spacedBy(20.dp),
verticalAlignment = Alignment.CenterVertically,
contentPadding = PaddingValues(start = 20.dp, end = 20.dp)
) {
@ -242,15 +242,21 @@ private fun AllContentTabText(
isSelected: Boolean,
onClick: () -> Unit
) {
Text(
Box(
modifier = Modifier
.wrapContentSize()
.wrapContentWidth()
.height(40.dp)
.noRippleClickable { onClick() },
text = getTabText(tab),
style = Title2,
color = if (isSelected) colorResource(id = R.color.glyph_button) else colorResource(id = R.color.glyph_active),
maxLines = 1
)
contentAlignment = Alignment.Center
) {
Text(
modifier = Modifier.padding(horizontal = 10.dp),
text = getTabText(tab),
style = Title2,
color = if (isSelected) colorResource(id = R.color.glyph_button) else colorResource(id = R.color.glyph_active),
maxLines = 1
)
}
}
@Composable
@ -262,6 +268,7 @@ private fun getTabText(tab: AllContentTab): String {
AllContentTab.BOOKMARKS -> stringResource(id = R.string.all_content_title_tab_bookmarks)
AllContentTab.TYPES -> stringResource(id = R.string.all_content_title_tab_objetc_types)
AllContentTab.LISTS -> stringResource(id = R.string.all_content_title_tab_lists)
AllContentTab.RELATIONS -> stringResource(id = R.string.all_content_title_tab_relations)
}
}
@ -275,7 +282,8 @@ private fun AllContentTabsPreview() {
AllContentTab.FILES,
AllContentTab.MEDIA,
AllContentTab.BOOKMARKS,
AllContentTab.TYPES
AllContentTab.TYPES,
AllContentTab.RELATIONS
),
selectedTab = AllContentTab.MEDIA
),

View file

@ -1760,6 +1760,7 @@ Please provide specific details of your needs here.</string>
<string name="all_content_title_tab_bookmarks">Bookmarks</string>
<string name="all_content_title_tab_files">Files</string>
<string name="all_content_title_tab_objetc_types">Types</string>
<string name="all_content_title_tab_relations">Relations</string>
<string name="all_content_sort_by">Sort by</string>
<string name="all_content_sort_name_desc">Z → A</string>
@ -1768,6 +1769,7 @@ Please provide specific details of your needs here.</string>
<string name="all_content_sort_date_asc">Oldest first</string>
<string name="all_content_sort_date_updated">Date updated</string>
<string name="all_content_sort_date_created">Date created</string>
<string name="all_content_sort_date_used">Date last used</string>
<string name="all_content_sort_name">Name</string>
<string name="all_content">All objects</string>
@ -1791,4 +1793,11 @@ Please provide specific details of your needs here.</string>
<string name="all_content_error_title">Something went wrong.</string>
<string name="onboarding_my_first_space">My First Space</string>
<string name="all_content_error_unexpected_layout">Error: unexpected layout</string>
<string name="all_content_toast_relation_added">Relation added</string>
<string name="all_content_toast_relation_removed">Relation removed</string>
<string name="all_content_toast_type_added">Object type added</string>
<string name="all_content_toast_type_removed">Object type removed</string>
<string name="all_content_toast_archived">Object archived</string>
</resources>

View file

@ -53,6 +53,9 @@ interface AppNavigation {
fun openAllContent(space: Id)
fun openTypeEditingScreen(id: Id, name: String, icon: String, readOnly: Boolean)
fun openTypeCreationScreen(name: String)
fun openRelationCreationScreen(id: Id, name: String, space: Id)
fun openRelationEditingScreen(typeName: String, id: Id, iconUnicode: Int, readOnly: Boolean)
sealed class Command {

View file

@ -651,7 +651,11 @@ object ObjectSearchConstants {
Relations.FILE_MIME_TYPE,
Relations.FILE_EXT,
Relations.LAST_OPENED_DATE,
Relations.LAST_MODIFIED_DATE
Relations.LAST_MODIFIED_DATE,
Relations.CREATED_DATE,
Relations.LINKS,
Relations.BACKLINKS,
Relations.LAST_USED_DATE
)
val defaultOptionKeys = listOf(
@ -711,7 +715,8 @@ object ObjectSearchConstants {
Relations.RELATION_OPTION_COLOR,
Relations.RELATION_DEFAULT_VALUE,
Relations.RELATION_FORMAT_OBJECT_TYPES,
Relations.RELATION_READ_ONLY_VALUE
Relations.RELATION_READ_ONLY_VALUE,
Relations.LAST_USED_DATE
)
val defaultFilesKeys = defaultKeys + listOf(
@ -927,7 +932,8 @@ object ObjectSearchConstants {
Relations.RECOMMENDED_LAYOUT,
Relations.DEFAULT_TEMPLATE_ID,
Relations.SPACE_ID,
Relations.RESTRICTIONS
Relations.RESTRICTIONS,
Relations.LAST_USED_DATE
)
//endregion