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:
parent
9c7f6ed8ab
commit
17eef47ca5
16 changed files with 589 additions and 114 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"/>
|
||||
|
|
|
@ -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-"
|
||||
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
)
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
),
|
||||
|
|
|
@ -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>
|
|
@ -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 {
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue