mirror of
https://github.com/anyproto/anytype-kotlin.git
synced 2025-06-08 05:47:05 +09:00
DROID-1125 Editor | Enhancement | Show object type relations (#7)
This commit is contained in:
parent
4265c951fa
commit
45aa366de6
43 changed files with 524 additions and 354 deletions
|
@ -74,6 +74,7 @@ import com.anytypeio.anytype.domain.page.Redo
|
|||
import com.anytypeio.anytype.domain.page.Undo
|
||||
import com.anytypeio.anytype.domain.page.bookmark.CreateBookmarkBlock
|
||||
import com.anytypeio.anytype.domain.page.bookmark.SetupBookmark
|
||||
import com.anytypeio.anytype.domain.relations.AddRelationToObject
|
||||
import com.anytypeio.anytype.domain.relations.SetRelationKey
|
||||
import com.anytypeio.anytype.domain.search.SearchObjects
|
||||
import com.anytypeio.anytype.domain.sets.FindObjectSetForType
|
||||
|
@ -270,6 +271,8 @@ open class EditorTestSetup {
|
|||
|
||||
lateinit var interceptFileLimitEvents: InterceptFileLimitEvents
|
||||
|
||||
lateinit var addRelationToObject: AddRelationToObject
|
||||
|
||||
val root: String = "rootId123"
|
||||
val workspaceId = MockDataFactory.randomString()
|
||||
|
||||
|
@ -367,7 +370,7 @@ open class EditorTestSetup {
|
|||
)
|
||||
|
||||
featureToggles = mock<DefaultFeatureToggles>()
|
||||
|
||||
addRelationToObject = AddRelationToObject(repo)
|
||||
|
||||
workspaceManager = WorkspaceManager.DefaultWorkspaceManager()
|
||||
runBlocking {
|
||||
|
@ -462,7 +465,8 @@ open class EditorTestSetup {
|
|||
workspaceManager = workspaceManager,
|
||||
getObjectTypes = getObjectTypes,
|
||||
objectToCollection = objectToCollection,
|
||||
interceptFileLimitEvents = interceptFileLimitEvents
|
||||
interceptFileLimitEvents = interceptFileLimitEvents,
|
||||
addRelationToObject = addRelationToObject
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ import com.anytypeio.anytype.domain.config.Gateway
|
|||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.domain.`object`.UpdateDetail
|
||||
import com.anytypeio.anytype.domain.objects.StoreOfRelations
|
||||
import com.anytypeio.anytype.domain.relations.AddRelationToObject
|
||||
import com.anytypeio.anytype.domain.relations.AddToFeaturedRelations
|
||||
import com.anytypeio.anytype.domain.relations.DeleteRelationFromObject
|
||||
import com.anytypeio.anytype.domain.relations.RemoveFromFeaturedRelations
|
||||
|
@ -80,6 +81,9 @@ class ObjectRelationListTest {
|
|||
@Mock
|
||||
lateinit var relationListProvider: RelationListProvider
|
||||
|
||||
@Mock
|
||||
lateinit var addRelationToObject: AddRelationToObject
|
||||
|
||||
@Mock
|
||||
lateinit var lockedStateProvider: LockedStateProvider
|
||||
|
||||
|
@ -111,7 +115,8 @@ class ObjectRelationListTest {
|
|||
removeFromFeaturedRelations = removeFromFeaturedRelations,
|
||||
deleteRelationFromObject = deleteRelationFromObject,
|
||||
analytics = analytics,
|
||||
storeOfRelations = storeOfRelations
|
||||
storeOfRelations = storeOfRelations,
|
||||
addRelationToObject = addRelationToObject
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ class DefaultFeatureToggles @Inject constructor(
|
|||
override val isLogFromMiddlewareLibrary =
|
||||
BuildConfig.LOG_FROM_MW_LIBRARY && buildProvider.isDebug()
|
||||
override val isLogMiddlewareInteraction =
|
||||
BuildConfig.LOG_DASHBOARD_REDUCER && buildProvider.isDebug()
|
||||
BuildConfig.LOG_MW_INTERACTION && buildProvider.isDebug()
|
||||
override val isLogDashboardReducer =
|
||||
BuildConfig.LOG_DASHBOARD_REDUCER && buildProvider.isDebug()
|
||||
override val isLogEditorViewModelEvents =
|
||||
|
|
|
@ -271,7 +271,8 @@ object EditorSessionModule {
|
|||
workspaceManager: WorkspaceManager,
|
||||
getObjectTypes: GetObjectTypes,
|
||||
objectToCollection: ConvertObjectToCollection,
|
||||
interceptFileLimitEvents: InterceptFileLimitEvents
|
||||
interceptFileLimitEvents: InterceptFileLimitEvents,
|
||||
addRelationToObject: AddRelationToObject
|
||||
): EditorViewModelFactory = EditorViewModelFactory(
|
||||
openPage = openPage,
|
||||
closeObject = closePage,
|
||||
|
@ -307,7 +308,8 @@ object EditorSessionModule {
|
|||
workspaceManager = workspaceManager,
|
||||
getObjectTypes = getObjectTypes,
|
||||
objectToCollection = objectToCollection,
|
||||
interceptFileLimitEvents = interceptFileLimitEvents
|
||||
interceptFileLimitEvents = interceptFileLimitEvents,
|
||||
addRelationToObject = addRelationToObject
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import com.anytypeio.anytype.domain.block.repo.BlockRepository
|
|||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.domain.`object`.UpdateDetail
|
||||
import com.anytypeio.anytype.domain.objects.StoreOfRelations
|
||||
import com.anytypeio.anytype.domain.relations.AddRelationToObject
|
||||
import com.anytypeio.anytype.domain.relations.AddToFeaturedRelations
|
||||
import com.anytypeio.anytype.domain.relations.DeleteRelationFromObject
|
||||
import com.anytypeio.anytype.domain.relations.RemoveFromFeaturedRelations
|
||||
|
@ -47,7 +48,8 @@ object ObjectRelationListModule {
|
|||
removeFromFeaturedRelations: RemoveFromFeaturedRelations,
|
||||
deleteRelationFromObject: DeleteRelationFromObject,
|
||||
analytics: Analytics,
|
||||
storeOfRelations: StoreOfRelations
|
||||
storeOfRelations: StoreOfRelations,
|
||||
addRelationToObject: AddRelationToObject
|
||||
): ObjectRelationListViewModelFactory {
|
||||
return ObjectRelationListViewModelFactory(
|
||||
lockedStateProvider = lockedStateProvider,
|
||||
|
@ -59,7 +61,8 @@ object ObjectRelationListModule {
|
|||
removeFromFeaturedRelations = removeFromFeaturedRelations,
|
||||
deleteRelationFromObject = deleteRelationFromObject,
|
||||
analytics = analytics,
|
||||
storeOfRelations = storeOfRelations
|
||||
storeOfRelations = storeOfRelations,
|
||||
addRelationToObject = addRelationToObject
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -140,7 +140,7 @@ open class RelationTextValueFragment :
|
|||
vm.onStart(
|
||||
ctx = ctx,
|
||||
relationKey = relationKey,
|
||||
recordId = objectId,
|
||||
objectId = objectId,
|
||||
isLocked = isLocked,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -65,7 +65,7 @@ sealed class ObjectWrapper {
|
|||
|
||||
val url: String? by default
|
||||
|
||||
val featuredRelations: List<String>? by default
|
||||
val featuredRelations: List<Key> get() = getValues(Relations.FEATURED_RELATIONS)
|
||||
|
||||
val smartBlockTypes: List<Double>
|
||||
get() = when (val value = map[Relations.SMARTBLOCKTYPES]) {
|
||||
|
@ -153,6 +153,7 @@ sealed class ObjectWrapper {
|
|||
val isArchived: Boolean? by default
|
||||
val iconEmoji: String? by default
|
||||
val isDeleted: Boolean? by default
|
||||
val recommendedRelations: List<Id> get() = getValues(Relations.RECOMMENDED_RELATIONS)
|
||||
}
|
||||
|
||||
data class Relation(override val map: Struct) : ObjectWrapper() {
|
||||
|
|
|
@ -57,6 +57,7 @@ object Relations {
|
|||
const val FILE_MIME_TYPE = "fileMimeType"
|
||||
|
||||
const val RECOMMENDED_LAYOUT = "recommendedLayout"
|
||||
const val RECOMMENDED_RELATIONS = "recommendedRelations"
|
||||
|
||||
val systemRelationKeys = listOf(
|
||||
"id",
|
||||
|
|
|
@ -32,31 +32,6 @@ fun Context.avatarColor(
|
|||
return color
|
||||
}
|
||||
|
||||
fun Context.formatIcon(
|
||||
format: ColumnView.Format
|
||||
): Drawable = drawable(
|
||||
when (format) {
|
||||
ColumnView.Format.SHORT_TEXT -> R.drawable.ic_text
|
||||
ColumnView.Format.LONG_TEXT -> R.drawable.ic_text
|
||||
ColumnView.Format.NUMBER -> R.drawable.ic_number
|
||||
ColumnView.Format.STATUS -> R.drawable.ic_select
|
||||
ColumnView.Format.DATE -> R.drawable.ic_date
|
||||
ColumnView.Format.FILE -> R.drawable.ic_file
|
||||
ColumnView.Format.CHECKBOX -> R.drawable.ic_checkbox
|
||||
ColumnView.Format.URL -> R.drawable.ic_url
|
||||
ColumnView.Format.EMAIL -> R.drawable.ic_email
|
||||
ColumnView.Format.PHONE -> R.drawable.ic_phone
|
||||
ColumnView.Format.EMOJI -> R.drawable.ic_person
|
||||
//todo Add proper icon for object
|
||||
ColumnView.Format.OBJECT -> R.drawable.ic_multiselect
|
||||
ColumnView.Format.TAG -> R.drawable.ic_multiselect
|
||||
else -> {
|
||||
// TODO
|
||||
R.drawable.circle_solid_default
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
fun ColumnView.Format.relationIcon(isMedium: Boolean = false): Int = when (this) {
|
||||
ColumnView.Format.SHORT_TEXT, ColumnView.Format.LONG_TEXT -> {
|
||||
if (isMedium)
|
||||
|
|
|
@ -229,9 +229,9 @@ sealed class ListRelationViewHolder(
|
|||
}
|
||||
}
|
||||
|
||||
fun setIsRemovable(isRemoveable: Boolean) {
|
||||
fun setIsRemovable(isRemovable: Boolean) {
|
||||
itemView.findViewById<View>(R.id.actionsLeftContainer).apply {
|
||||
if (isRemoveable) visible() else gone()
|
||||
if (isRemovable) visible() else gone()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ class StyleMenuHolder(
|
|||
tvTitle.setText(R.string.slash_widget_style_checkbox)
|
||||
tvSubtitle.visible()
|
||||
tvSubtitle.setText(R.string.slash_widget_style_checkbox_subtitle)
|
||||
ivIcon.setImageResource(R.drawable.ic_slash_style_checkbox)
|
||||
ivIcon.setImageResource(R.drawable.ic_checkbox_checked)
|
||||
}
|
||||
is SlashItem.Style.Type.Heading -> {
|
||||
tvTitle.setText(R.string.slash_widget_style_heading)
|
||||
|
|
|
@ -254,11 +254,11 @@ class DocumentRelationAdapter(
|
|||
}
|
||||
RelationListViewModel.Model.Section.Featured -> R.layout.item_relation_list_section
|
||||
RelationListViewModel.Model.Section.Other -> R.layout.item_relation_list_section
|
||||
is RelationListViewModel.Model.Section.TypeFrom -> R.layout.item_relation_list_section
|
||||
else -> throw IllegalStateException("Unexpected item type: $item")
|
||||
}
|
||||
|
||||
fun update(update: List<RelationListViewModel.Model>) {
|
||||
Timber.d("Updating adapter: $update")
|
||||
val differ = Differ(old = items, new = update)
|
||||
val result = DiffUtil.calculateDiff(differ, false)
|
||||
items = update
|
||||
|
@ -276,6 +276,10 @@ class DocumentRelationAdapter(
|
|||
itemView.findViewById<TextView>(R.id.tvSectionName)
|
||||
.setText(R.string.other_relations)
|
||||
}
|
||||
is RelationListViewModel.Model.Section.TypeFrom -> {
|
||||
val text = itemView.resources.getString(R.string.from_type, section.typeName)
|
||||
itemView.findViewById<TextView>(R.id.tvSectionName).text = text
|
||||
}
|
||||
else -> throw IllegalStateException("Unexpected item type: $section")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="25dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="25">
|
||||
<path
|
||||
android:pathData="M7,5.9341C5.8954,5.9341 5,6.8295 5,7.9341V17.9341C5,19.0387 5.8954,19.9341 7,19.9341H17C18.1046,19.9341 19,19.0387 19,17.9341V7.9341C19,6.8295 18.1046,5.9341 17,5.9341H7ZM15.3686,9.4829L10.4988,14.3527L8.6314,12.493C8.3183,12.1812 7.8118,12.1823 7.5,12.4954C7.1882,12.8084 7.1893,13.315 7.5023,13.6267L10.5012,16.6131L16.5,10.6143C16.8124,10.3019 16.8124,9.7953 16.5,9.4829C16.1876,9.1705 15.681,9.1705 15.3686,9.4829Z"
|
||||
android:fillColor="#2C2B27"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
14
core-ui/src/main/res/drawable/ic_checkbox_checked.xml
Normal file
14
core-ui/src/main/res/drawable/ic_checkbox_checked.xml
Normal file
|
@ -0,0 +1,14 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="18dp"
|
||||
android:height="19dp"
|
||||
android:viewportWidth="18"
|
||||
android:viewportHeight="19">
|
||||
<path
|
||||
android:pathData="M0,9.938C0,4.967 4.029,0.938 9,0.938C13.971,0.938 18,4.967 18,9.938C18,14.908 13.971,18.938 9,18.938C4.029,18.938 0,14.908 0,9.938Z"
|
||||
android:fillColor="@color/palette_system_sky"/>
|
||||
<path
|
||||
android:pathData="M5,9.438L8.5,13.438L13.5,5.438"
|
||||
android:strokeWidth="1.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="@color/glyph_white"/>
|
||||
</vector>
|
|
@ -1,25 +0,0 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="25dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="25">
|
||||
<path
|
||||
android:pathData="M7,5.9497H17C18.1046,5.9497 19,6.8451 19,7.9497V17.9497C19,19.0543 18.1046,19.9497 17,19.9497H7C5.8954,19.9497 5,19.0543 5,17.9497V7.9497C5,6.8451 5.8954,5.9497 7,5.9497ZM7,9.9497V17.9497H17V9.9497H7Z"
|
||||
android:fillColor="#2C2B27"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M11.5,11.9497L12.5,11.9497A0.5,0.5 0,0 1,13 12.4497L13,13.4497A0.5,0.5 0,0 1,12.5 13.9497L11.5,13.9497A0.5,0.5 0,0 1,11 13.4497L11,12.4497A0.5,0.5 0,0 1,11.5 11.9497z"
|
||||
android:fillColor="#2C2B27"/>
|
||||
<path
|
||||
android:pathData="M8.5,11.9497L9.5,11.9497A0.5,0.5 0,0 1,10 12.4497L10,13.4497A0.5,0.5 0,0 1,9.5 13.9497L8.5,13.9497A0.5,0.5 0,0 1,8 13.4497L8,12.4497A0.5,0.5 0,0 1,8.5 11.9497z"
|
||||
android:fillColor="#2C2B27"/>
|
||||
<path
|
||||
android:pathData="M14.5,11.9497L15.5,11.9497A0.5,0.5 0,0 1,16 12.4497L16,13.4497A0.5,0.5 0,0 1,15.5 13.9497L14.5,13.9497A0.5,0.5 0,0 1,14 13.4497L14,12.4497A0.5,0.5 0,0 1,14.5 11.9497z"
|
||||
android:fillColor="#2C2B27"/>
|
||||
<path
|
||||
android:pathData="M8.5,14.9497L9.5,14.9497A0.5,0.5 0,0 1,10 15.4497L10,16.4497A0.5,0.5 0,0 1,9.5 16.9497L8.5,16.9497A0.5,0.5 0,0 1,8 16.4497L8,15.4497A0.5,0.5 0,0 1,8.5 14.9497z"
|
||||
android:fillColor="#2C2B27"/>
|
||||
<path
|
||||
android:pathData="M11.5,14.9497L12.5,14.9497A0.5,0.5 0,0 1,13 15.4497L13,16.4497A0.5,0.5 0,0 1,12.5 16.9497L11.5,16.9497A0.5,0.5 0,0 1,11 16.4497L11,15.4497A0.5,0.5 0,0 1,11.5 14.9497z"
|
||||
android:fillColor="#2C2B27"/>
|
||||
</vector>
|
|
@ -1,10 +0,0 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="25dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="25">
|
||||
<path
|
||||
android:pathData="M4,9.1162V17.9604C4,18.5127 4.4477,18.9604 5,18.9604H19C19.5523,18.9604 20,18.5127 20,17.9604V9.1162L12,15.5162L4,9.1162ZM19.7333,7.2806C19.5507,7.0837 19.2897,6.9605 19,6.9605H5C4.7103,6.9605 4.4493,7.0837 4.2667,7.2806L12,13.4672L19.7333,7.2806Z"
|
||||
android:fillColor="#2C2B27"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
|
@ -1,17 +0,0 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="25dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="25">
|
||||
<path
|
||||
android:pathData="M7,7.9679H9V16.4829C9,18.4159 10.567,19.9829 12.5,19.9829C14.433,19.9829 16,18.4159 16,16.4829V9.4695C16,9.1934 16.2239,8.9695 16.5,8.9695H17.5C17.7761,8.9695 18,9.1934 18,9.4695V16.4829V16.4998H18C17.9909,19.5296 15.5319,21.9829 12.5,21.9829C9.4681,21.9829 7.0091,19.5296 7,16.4998H7V16.4829V7.9679Z"
|
||||
android:fillColor="#2C2B27"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M13,7.9526H15V15.4829C15,16.8636 13.8807,17.9829 12.5,17.9829C11.1193,17.9829 10,16.8636 10,15.4829V9.4568C10,9.1807 10.2239,8.9568 10.5,8.9568H11.5C11.7761,8.9568 12,9.1807 12,9.4568V15.4829C12,15.7591 12.2239,15.9829 12.5,15.9829C12.7761,15.9829 13,15.7591 13,15.4829V7.9526Z"
|
||||
android:fillColor="#2C2B27"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M15,7.9829H13C13,6.8783 12.1046,5.9829 11,5.9829C9.8954,5.9829 9,6.8783 9,7.9829H7C7,5.7738 8.7909,3.9829 11,3.9829C13.2091,3.9829 15,5.7738 15,7.9829Z"
|
||||
android:fillColor="#2C2B27"/>
|
||||
</vector>
|
|
@ -1,24 +0,0 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="25dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="25">
|
||||
<path
|
||||
android:pathData="M17.9521,6.9663L17.9521,6.9663A1,1 0,0 0,16.9521 7.9663L16.9521,7.9663A1,1 0,0 0,17.9521 8.9663L17.9521,8.9663A1,1 0,0 0,18.9521 7.9663L18.9521,7.9663A1,1 0,0 0,17.9521 6.9663z"
|
||||
android:fillColor="#2C2B27"/>
|
||||
<path
|
||||
android:pathData="M15.0156,6.9663L5.5156,6.9663A0.5,0.5 0,0 0,5.0156 7.4663L5.0156,8.4663A0.5,0.5 0,0 0,5.5156 8.9663L15.0156,8.9663A0.5,0.5 0,0 0,15.5156 8.4663L15.5156,7.4663A0.5,0.5 0,0 0,15.0156 6.9663z"
|
||||
android:fillColor="#2C2B27"/>
|
||||
<path
|
||||
android:pathData="M17.9521,16.9629L17.9521,16.9629A1,1 0,0 0,16.9521 17.9629L16.9521,17.9629A1,1 0,0 0,17.9521 18.9629L17.9521,18.9629A1,1 0,0 0,18.9521 17.9629L18.9521,17.9629A1,1 0,0 0,17.9521 16.9629z"
|
||||
android:fillColor="#2C2B27"/>
|
||||
<path
|
||||
android:pathData="M15.0156,16.9629L5.5156,16.9629A0.5,0.5 0,0 0,5.0156 17.4629L5.0156,18.4629A0.5,0.5 0,0 0,5.5156 18.9629L15.0156,18.9629A0.5,0.5 0,0 0,15.5156 18.4629L15.5156,17.4629A0.5,0.5 0,0 0,15.0156 16.9629z"
|
||||
android:fillColor="#2C2B27"/>
|
||||
<path
|
||||
android:pathData="M17.9521,11.9629L17.9521,11.9629A1,1 0,0 0,16.9521 12.9629L16.9521,12.9629A1,1 0,0 0,17.9521 13.9629L17.9521,13.9629A1,1 0,0 0,18.9521 12.9629L18.9521,12.9629A1,1 0,0 0,17.9521 11.9629z"
|
||||
android:fillColor="#2C2B27"/>
|
||||
<path
|
||||
android:pathData="M15.0156,11.9629L5.5156,11.9629A0.5,0.5 0,0 0,5.0156 12.4629L5.0156,13.4629A0.5,0.5 0,0 0,5.5156 13.9629L15.0156,13.9629A0.5,0.5 0,0 0,15.5156 13.4629L15.5156,12.4629A0.5,0.5 0,0 0,15.0156 11.9629z"
|
||||
android:fillColor="#2C2B27"/>
|
||||
</vector>
|
|
@ -1,18 +0,0 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="25dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="25">
|
||||
<path
|
||||
android:pathData="M6.5,8.9663L18.5,8.9663A0.5,0.5 0,0 1,19 9.4663L19,10.4663A0.5,0.5 0,0 1,18.5 10.9663L6.5,10.9663A0.5,0.5 0,0 1,6 10.4663L6,9.4663A0.5,0.5 0,0 1,6.5 8.9663z"
|
||||
android:fillColor="#2C2B27"/>
|
||||
<path
|
||||
android:pathData="M5.5,14.9663L17.5,14.9663A0.5,0.5 0,0 1,18 15.4663L18,16.4663A0.5,0.5 0,0 1,17.5 16.9663L5.5,16.9663A0.5,0.5 0,0 1,5 16.4663L5,15.4663A0.5,0.5 0,0 1,5.5 14.9663z"
|
||||
android:fillColor="#2C2B27"/>
|
||||
<path
|
||||
android:pathData="M9.9463,6.3605C9.9961,6.1304 10.1996,5.9663 10.435,5.9663H11.3824C11.7004,5.9663 11.9377,6.2593 11.8715,6.5704L9.1054,19.5704C9.0562,19.8012 8.8524,19.9663 8.6163,19.9663H7.6199C7.3011,19.9663 7.0637,19.672 7.1312,19.3605L9.9463,6.3605Z"
|
||||
android:fillColor="#2C2B27"/>
|
||||
<path
|
||||
android:pathData="M14.9463,6.3605C14.9961,6.1304 15.1996,5.9663 15.435,5.9663H16.3824C16.7004,5.9663 16.9377,6.2593 16.8715,6.5704L14.1054,19.5704C14.0562,19.8012 13.8524,19.9663 13.6163,19.9663H12.6199C12.3011,19.9663 12.0637,19.672 12.1312,19.3605L14.9463,6.3605Z"
|
||||
android:fillColor="#2C2B27"/>
|
||||
</vector>
|
|
@ -1,10 +0,0 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="25dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="25">
|
||||
<path
|
||||
android:pathData="M20,12.9663C20,17.3846 16.4183,20.9663 12,20.9663C7.5817,20.9663 4,17.3846 4,12.9663C4,8.548 7.5817,4.9663 12,4.9663C16.4183,4.9663 20,8.548 20,12.9663ZM14.562,10.4663C14.562,11.847 13.4427,12.9663 12.062,12.9663C10.6813,12.9663 9.562,11.847 9.562,10.4663C9.562,9.0856 10.6813,7.9663 12.062,7.9663C13.4427,7.9663 14.562,9.0856 14.562,10.4663ZM12.062,13.9663C9.9998,13.9663 8.1806,15.0067 7.1006,16.5913C8.1806,18.1759 9.9998,19.2163 12.0621,19.2163C14.1243,19.2163 15.9436,18.1759 17.0236,16.5913C15.9436,15.0068 14.1243,13.9663 12.062,13.9663Z"
|
||||
android:fillColor="#2C2B27"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
|
@ -1,10 +0,0 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="25dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="25">
|
||||
<path
|
||||
android:pathData="M6.2114,5.3199C6.4067,5.1246 6.7232,5.1246 6.9185,5.3199L9.9433,8.3447C10.1386,8.54 10.1386,8.8566 9.9433,9.0518L8.1756,10.8196C8.1756,10.8196 7.8064,12.6268 10.0729,14.8933C12.3395,17.1599 14.1467,16.7907 14.1467,16.7907L15.9145,15.0229C16.1097,14.8277 16.4263,14.8277 16.6216,15.0229L19.6464,18.0478C19.8417,18.243 19.8417,18.5596 19.6464,18.7549L17.8786,20.5227L17.7956,20.6057L17.6796,20.7217C17.5654,20.836 17.4051,20.8881 17.2471,20.8548C16.1789,20.6294 11.911,19.5598 8.6587,16.3076C5.4065,13.0553 4.3369,8.7874 4.1115,7.7192C4.0781,7.5612 4.1303,7.4009 4.2446,7.2867L4.2981,7.2332L4.4436,7.0876L6.2114,5.3199Z"
|
||||
android:fillColor="#2C2B27"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<stroke android:color="#DFDDD0" android:width="1dp" />
|
||||
<stroke android:color="@color/glyph_active" android:width="1dp" />
|
||||
</shape>
|
|
@ -5,8 +5,8 @@
|
|||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M12,12m-11,0a11,11 0,1 1,22 0a11,11 0,1 1,-22 0"
|
||||
android:fillColor="#F55522"/>
|
||||
android:fillColor="@color/palette_system_red"/>
|
||||
<path
|
||||
android:pathData="M6,12C6,11.5858 6.3358,11.25 6.75,11.25H17.25C17.6642,11.25 18,11.5858 18,12C18,12.4142 17.6642,12.75 17.25,12.75H6.75C6.3358,12.75 6,12.4142 6,12Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
android:fillColor="@color/glyph_white"/>
|
||||
</vector>
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="25dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="25">
|
||||
<path
|
||||
android:pathData="M12,20.9941C16.4183,20.9941 20,17.4124 20,12.9941C20,8.5759 16.4183,4.9941 12,4.9941C7.5817,4.9941 4,8.5759 4,12.9941C4,17.4124 7.5817,20.9941 12,20.9941ZM8.4522,11.4941C8.1397,11.8066 8.1397,12.3131 8.4522,12.6255L11.9522,16.1255L15.4522,12.6255C15.7646,12.3131 15.7646,11.8066 15.4522,11.4941C15.1397,11.1817 14.6332,11.1817 14.3208,11.4941L11.9522,13.8628L9.5835,11.4941C9.2711,11.1817 8.7646,11.1817 8.4522,11.4941Z"
|
||||
android:fillColor="#2C2B27"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
|
@ -1,14 +0,0 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M12.043,3L12.043,3A9,9 0,0 1,21.043 12L21.043,12A9,9 0,0 1,12.043 21L12.043,21A9,9 0,0 1,3.043 12L3.043,12A9,9 0,0 1,12.043 3z"
|
||||
android:fillColor="#2AA7EE"/>
|
||||
<path
|
||||
android:pathData="M7,12L11,16L16.5,7.5"
|
||||
android:strokeWidth="1.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#ffffff"/>
|
||||
</vector>
|
|
@ -1,10 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="18dp"
|
||||
android:height="18dp"
|
||||
android:viewportWidth="18"
|
||||
android:viewportHeight="18">
|
||||
android:width="10dp"
|
||||
android:height="13dp"
|
||||
android:viewportWidth="10"
|
||||
android:viewportHeight="13">
|
||||
<path
|
||||
android:pathData="M6.75,7.5H11.25V5.5C11.25,4.257 10.243,3.25 9,3.25C7.757,3.25 6.75,4.257 6.75,5.5V7.5ZM12.5,7.5C13.328,7.5 14,8.172 14,9V13.5C14,14.328 13.328,15 12.5,15H5.5C4.672,15 4,14.328 4,13.5V9C4,8.172 4.672,7.5 5.5,7.5L5.5,5.5C5.5,3.567 7.067,2 9,2C10.933,2 12.5,3.567 12.5,5.5V7.5Z"
|
||||
android:fillColor="#ABA99B"
|
||||
android:pathData="M2.75,5.5H7.25V3.5C7.25,2.257 6.243,1.25 5,1.25C3.757,1.25 2.75,2.257 2.75,3.5V5.5ZM8.5,5.5C9.328,5.5 10,6.172 10,7V11.5C10,12.328 9.328,13 8.5,13H1.5C0.672,13 0,12.328 0,11.5V7C0,6.172 0.672,5.5 1.5,5.5L1.5,3.5C1.5,1.567 3.067,0 5,0C6.933,0 8.5,1.567 8.5,3.5V5.5Z"
|
||||
android:fillColor="#A7A7A7"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="25dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="25">
|
||||
<path
|
||||
android:pathData="M5.5,6.9941L18.5,6.9941A0.5,0.5 0,0 1,19 7.4941L19,8.4941A0.5,0.5 0,0 1,18.5 8.9941L5.5,8.9941A0.5,0.5 0,0 1,5 8.4941L5,7.4941A0.5,0.5 0,0 1,5.5 6.9941z"
|
||||
android:fillColor="#2C2B27"/>
|
||||
<path
|
||||
android:pathData="M5.5,16.9941L14.5,16.9941A0.5,0.5 0,0 1,15 17.4941L15,18.4941A0.5,0.5 0,0 1,14.5 18.9941L5.5,18.9941A0.5,0.5 0,0 1,5 18.4941L5,17.4941A0.5,0.5 0,0 1,5.5 16.9941z"
|
||||
android:fillColor="#2C2B27"/>
|
||||
<path
|
||||
android:pathData="M5.5,11.9941L18.5,11.9941A0.5,0.5 0,0 1,19 12.4941L19,13.4941A0.5,0.5 0,0 1,18.5 13.9941L5.5,13.9941A0.5,0.5 0,0 1,5 13.4941L5,12.4941A0.5,0.5 0,0 1,5.5 11.9941z"
|
||||
android:fillColor="#2C2B27"/>
|
||||
</vector>
|
|
@ -1,14 +0,0 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FFB522"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M16.9572,9.4572L10.5001,15.9143L7.043,12.4572L8.4572,11.043L10.5001,13.0859L15.543,8.043L16.9572,9.4572Z" />
|
||||
<path
|
||||
android:fillColor="#FFB522"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M12,21C16.9706,21 21,16.9706 21,12C21,7.0294 16.9706,3 12,3C7.0294,3 3,7.0294 3,12C3,16.9706 7.0294,21 12,21ZM12,23C18.0751,23 23,18.0751 23,12C23,5.9249 18.0751,1 12,1C5.9249,1 1,5.9249 1,12C1,18.0751 5.9249,23 12,23Z" />
|
||||
</vector>
|
|
@ -1,13 +0,0 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="25dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="25">
|
||||
<path
|
||||
android:pathData="M17.4367,10.4566C18.2287,9.6646 18.2287,8.3805 17.4367,7.5884C16.6447,6.7964 15.3605,6.7964 14.5685,7.5884L11.3418,10.8152L9.9077,9.3811L13.1344,6.1543C14.7185,4.5703 17.2867,4.5703 18.8708,6.1543C20.4549,7.7384 20.4549,10.3067 18.8708,11.8907L15.6441,15.1175L14.21,13.6834L17.4367,10.4566ZM5.2469,19.7783C3.6628,18.1942 3.6628,15.6259 5.2469,14.0419L8.4736,10.8152L9.9077,12.2493L6.681,15.476C5.8889,16.268 5.8889,17.5521 6.681,18.3442C7.473,19.1362 8.7571,19.1362 9.5492,18.3442L12.7759,15.1175L14.21,16.5515L10.9833,19.7783C9.3992,21.3623 6.8309,21.3623 5.2469,19.7783Z"
|
||||
android:fillColor="#2C2B27"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M13.5154,10.1205C13.9114,9.7245 14.5535,9.7245 14.9495,10.1205C15.3455,10.5165 15.3455,11.1586 14.9495,11.5546L10.6472,15.8569C10.2512,16.2529 9.6091,16.2529 9.2131,15.8569C8.8171,15.4609 8.8171,14.8188 9.2131,14.4228L13.5154,10.1205Z"
|
||||
android:fillColor="#2C2B27"/>
|
||||
</vector>
|
|
@ -344,6 +344,7 @@
|
|||
<string name="create_option">Create option \"%1$s\"</string>
|
||||
<string name="featured_relations">Featured relations</string>
|
||||
<string name="other_relations">Other relations</string>
|
||||
<string name="from_type">From type %1$s</string>
|
||||
<string name="hint_add_description">Add description</string>
|
||||
|
||||
<string name="slash_widget_main_style">Style</string>
|
||||
|
|
|
@ -15,7 +15,9 @@ import kotlinx.coroutines.sync.withLock
|
|||
interface StoreOfRelations {
|
||||
val size: Int
|
||||
suspend fun getByKey(key: Key): ObjectWrapper.Relation?
|
||||
suspend fun getByKeys(keys: List<Key>): List<ObjectWrapper.Relation>
|
||||
suspend fun getById(id: Id): ObjectWrapper.Relation?
|
||||
suspend fun getById(ids: List<Id>): List<ObjectWrapper.Relation>
|
||||
suspend fun getAll(): List<ObjectWrapper.Relation>
|
||||
suspend fun merge(relations: List<ObjectWrapper.Relation>)
|
||||
suspend fun amend(target: Id, diff: Map<Id, Any?>)
|
||||
|
@ -50,10 +52,18 @@ class DefaultStoreOfRelations : StoreOfRelations {
|
|||
null
|
||||
}
|
||||
|
||||
override suspend fun getByKeys(keys: List<Key>): List<ObjectWrapper.Relation> = mutex.withLock {
|
||||
keys.mapNotNull { key -> keysToIds[key]?.let { store[it] } }
|
||||
}
|
||||
|
||||
override suspend fun getById(id: Id): ObjectWrapper.Relation? = mutex.withLock {
|
||||
store[id]
|
||||
}
|
||||
|
||||
override suspend fun getById(ids: List<Id>): List<ObjectWrapper.Relation> = mutex.withLock {
|
||||
ids.mapNotNull { id -> store[id] }
|
||||
}
|
||||
|
||||
override suspend fun getAll(): List<ObjectWrapper.Relation> = mutex.withLock {
|
||||
store.values.toList()
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ import com.anytypeio.anytype.core_models.Payload
|
|||
import com.anytypeio.anytype.core_models.Position
|
||||
import com.anytypeio.anytype.core_models.Relation
|
||||
import com.anytypeio.anytype.core_models.RelationFormat
|
||||
import com.anytypeio.anytype.core_models.RelationLink
|
||||
import com.anytypeio.anytype.core_models.Relations
|
||||
import com.anytypeio.anytype.core_models.SyncStatus
|
||||
import com.anytypeio.anytype.core_models.TextBlock
|
||||
|
@ -80,6 +81,7 @@ import com.anytypeio.anytype.domain.page.CreateBlockLinkWithObject
|
|||
import com.anytypeio.anytype.domain.page.CreateObject
|
||||
import com.anytypeio.anytype.domain.page.CreateObjectAsMentionOrLink
|
||||
import com.anytypeio.anytype.domain.page.OpenPage
|
||||
import com.anytypeio.anytype.domain.relations.AddRelationToObject
|
||||
import com.anytypeio.anytype.domain.search.SearchObjects
|
||||
import com.anytypeio.anytype.domain.sets.FindObjectSetForType
|
||||
import com.anytypeio.anytype.domain.status.InterceptThreadStatus
|
||||
|
@ -213,9 +215,12 @@ import com.anytypeio.anytype.presentation.objects.ObjectIcon
|
|||
import com.anytypeio.anytype.presentation.objects.ObjectTypeView
|
||||
import com.anytypeio.anytype.presentation.objects.SupportedLayouts
|
||||
import com.anytypeio.anytype.presentation.objects.getObjectTypeViewsForSBPage
|
||||
import com.anytypeio.anytype.presentation.objects.getProperType
|
||||
import com.anytypeio.anytype.presentation.objects.toView
|
||||
import com.anytypeio.anytype.presentation.relations.ObjectRelationView
|
||||
import com.anytypeio.anytype.presentation.relations.view
|
||||
import com.anytypeio.anytype.presentation.relations.getNotIncludedRecommendedRelations
|
||||
import com.anytypeio.anytype.presentation.relations.getObjectRelations
|
||||
import com.anytypeio.anytype.presentation.relations.views
|
||||
import com.anytypeio.anytype.presentation.search.ObjectSearchConstants
|
||||
import com.anytypeio.anytype.presentation.search.ObjectSearchViewModel
|
||||
import com.anytypeio.anytype.presentation.util.CopyFileStatus
|
||||
|
@ -277,7 +282,8 @@ class EditorViewModel(
|
|||
private val tableDelegate: EditorTableDelegate,
|
||||
private val workspaceManager: WorkspaceManager,
|
||||
private val getObjectTypes: GetObjectTypes,
|
||||
private val interceptFileLimitEvents: InterceptFileLimitEvents
|
||||
private val interceptFileLimitEvents: InterceptFileLimitEvents,
|
||||
private val addRelationToObject: AddRelationToObject
|
||||
) : ViewStateViewModel<ViewState>(),
|
||||
PickerListener,
|
||||
SupportNavigation<EventWrapper<AppNavigation.Command>>,
|
||||
|
@ -4793,25 +4799,75 @@ class EditorViewModel(
|
|||
}
|
||||
|
||||
private fun getRelations(action: (List<SlashRelationView.Item>) -> Unit) {
|
||||
val relationLinks = orchestrator.stores.relationLinks.current()
|
||||
val details = orchestrator.stores.details.current()
|
||||
val detail = details.details[context]
|
||||
val values = detail?.map ?: emptyMap()
|
||||
val objectDetails = details.details[context]?.map ?: emptyMap()
|
||||
val objectWrapper = ObjectWrapper.Basic(objectDetails)
|
||||
val objectType = objectWrapper.getProperType()
|
||||
val objectTypeWrapper = ObjectWrapper.Type(details.details[objectType]?.map ?: emptyMap())
|
||||
val relationLinks = orchestrator.stores.relationLinks.current()
|
||||
|
||||
viewModelScope.launch {
|
||||
val update = relationLinks.mapNotNull { relationLink ->
|
||||
val relation =
|
||||
storeOfRelations.getByKey(relationLink.key) ?: return@mapNotNull null
|
||||
val relationView = relation.view(
|
||||
details = details,
|
||||
values = values,
|
||||
urlBuilder = urlBuilder
|
||||
) ?: return@mapNotNull null
|
||||
SlashRelationView.Item(relationView)
|
||||
}
|
||||
val objectRelationViews = getObjectRelationsView(
|
||||
ctx = context,
|
||||
objectDetails = objectDetails,
|
||||
relationLinks = relationLinks,
|
||||
details = details,
|
||||
objectWrapper = objectWrapper
|
||||
)
|
||||
|
||||
val recommendedRelationViews = getRecommendedRelations(
|
||||
ctx = context,
|
||||
objectDetails = objectDetails,
|
||||
relationLinks = relationLinks,
|
||||
objectTypeWrapper = objectTypeWrapper,
|
||||
details = details
|
||||
)
|
||||
val update =
|
||||
(objectRelationViews + recommendedRelationViews).map { SlashRelationView.Item(it) }
|
||||
|
||||
action.invoke(update)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getObjectRelationsView(
|
||||
ctx: Id,
|
||||
objectDetails: Map<Key, Any?>,
|
||||
relationLinks: List<RelationLink>,
|
||||
details: Block.Details,
|
||||
objectWrapper: ObjectWrapper.Basic
|
||||
): List<ObjectRelationView> {
|
||||
return getObjectRelations(
|
||||
systemRelations = listOf(),
|
||||
relationLinks = relationLinks,
|
||||
storeOfRelations = storeOfRelations
|
||||
).views(
|
||||
context = ctx,
|
||||
details = details,
|
||||
values = objectDetails,
|
||||
urlBuilder = urlBuilder,
|
||||
featured = objectWrapper.featuredRelations
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun getRecommendedRelations(
|
||||
ctx: Id,
|
||||
objectDetails: Map<Key, Any?>,
|
||||
relationLinks: List<RelationLink>,
|
||||
objectTypeWrapper: ObjectWrapper.Type,
|
||||
details: Block.Details
|
||||
): List<ObjectRelationView> {
|
||||
return getNotIncludedRecommendedRelations(
|
||||
relationLinks = relationLinks,
|
||||
recommendedRelations = objectTypeWrapper.recommendedRelations,
|
||||
storeOfRelations = storeOfRelations
|
||||
).views(
|
||||
context = ctx,
|
||||
details = details,
|
||||
values = objectDetails,
|
||||
urlBuilder = urlBuilder
|
||||
)
|
||||
}
|
||||
|
||||
private fun proceedWithObjectTypes(objectTypes: List<ObjectTypeView>) {
|
||||
onSlashWidgetStateChanged(
|
||||
SlashWidgetState.UpdateItems.empty().copy(
|
||||
|
@ -6706,6 +6762,7 @@ class EditorViewModel(
|
|||
private fun proceedWithRelationBlockClicked(
|
||||
relationView: ObjectRelationView
|
||||
) {
|
||||
Timber.d("proceedWithRelationBlockClicked, relationView:${relationView}")
|
||||
val relationId = relationView.id
|
||||
viewModelScope.launch {
|
||||
val relation = storeOfRelations.getById(relationId)
|
||||
|
@ -6718,50 +6775,95 @@ class EditorViewModel(
|
|||
Timber.d("No interaction allowed with this relation")
|
||||
return@launch
|
||||
}
|
||||
when (relation.format) {
|
||||
RelationFormat.SHORT_TEXT,
|
||||
RelationFormat.LONG_TEXT,
|
||||
RelationFormat.URL,
|
||||
RelationFormat.PHONE,
|
||||
RelationFormat.NUMBER,
|
||||
RelationFormat.EMAIL -> {
|
||||
dispatch(
|
||||
Command.OpenObjectRelationScreen.Value.Text(
|
||||
ctx = context,
|
||||
target = context,
|
||||
relationKey = relation.key,
|
||||
isLocked = mode == EditorMode.Locked
|
||||
)
|
||||
if (checkRelationIsInObject(relationView)) {
|
||||
openRelationValueScreen(
|
||||
relation = relation,
|
||||
relationView = relationView
|
||||
)
|
||||
} else {
|
||||
proceedWithAddingRelationToObject(context, relationView) {
|
||||
openRelationValueScreen(
|
||||
relation = relation,
|
||||
relationView = relationView
|
||||
)
|
||||
}
|
||||
RelationFormat.CHECKBOX -> {
|
||||
check(relationView is ObjectRelationView.Checkbox)
|
||||
proceedWithSetObjectDetails(
|
||||
}
|
||||
openRelationValueScreen(relation, relationView)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkRelationIsInObject(
|
||||
view: ObjectRelationView
|
||||
): Boolean {
|
||||
val relationLinks = orchestrator.stores.relationLinks.current()
|
||||
return relationLinks.any { it.key == view.key }
|
||||
}
|
||||
|
||||
private suspend fun proceedWithAddingRelationToObject(
|
||||
ctx: Id,
|
||||
view: ObjectRelationView,
|
||||
action: () -> Unit
|
||||
) {
|
||||
val params = AddRelationToObject.Params(
|
||||
ctx = ctx,
|
||||
relationKey = view.key
|
||||
)
|
||||
addRelationToObject.run(params).process(
|
||||
failure = { Timber.e(it, "Error while adding relation to object") },
|
||||
success = {
|
||||
dispatcher.send(it)
|
||||
action.invoke()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun openRelationValueScreen(
|
||||
relation: ObjectWrapper.Relation,
|
||||
relationView: ObjectRelationView
|
||||
) {
|
||||
when (relation.format) {
|
||||
RelationFormat.SHORT_TEXT,
|
||||
RelationFormat.LONG_TEXT,
|
||||
RelationFormat.URL,
|
||||
RelationFormat.PHONE,
|
||||
RelationFormat.NUMBER,
|
||||
RelationFormat.EMAIL -> {
|
||||
dispatch(
|
||||
Command.OpenObjectRelationScreen.Value.Text(
|
||||
ctx = context,
|
||||
key = relation.key,
|
||||
value = !relationView.isChecked
|
||||
target = context,
|
||||
relationKey = relation.key,
|
||||
isLocked = mode == EditorMode.Locked
|
||||
)
|
||||
}
|
||||
RelationFormat.DATE -> {
|
||||
dispatch(
|
||||
Command.OpenObjectRelationScreen.Value.Date(
|
||||
ctx = context,
|
||||
target = context,
|
||||
relationKey = relation.key
|
||||
)
|
||||
)
|
||||
}
|
||||
RelationFormat.CHECKBOX -> {
|
||||
check(relationView is ObjectRelationView.Checkbox)
|
||||
proceedWithSetObjectDetails(
|
||||
ctx = context,
|
||||
key = relation.key,
|
||||
value = !relationView.isChecked
|
||||
)
|
||||
}
|
||||
RelationFormat.DATE -> {
|
||||
dispatch(
|
||||
Command.OpenObjectRelationScreen.Value.Date(
|
||||
ctx = context,
|
||||
target = context,
|
||||
relationKey = relation.key
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
dispatch(
|
||||
Command.OpenObjectRelationScreen.Value.Default(
|
||||
ctx = context,
|
||||
target = context,
|
||||
relationKey = relation.key,
|
||||
targetObjectTypes = relation.relationFormatObjectTypes,
|
||||
isLocked = mode == EditorMode.Locked
|
||||
)
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
dispatch(
|
||||
Command.OpenObjectRelationScreen.Value.Default(
|
||||
ctx = context,
|
||||
target = context,
|
||||
relationKey = relation.key,
|
||||
targetObjectTypes = relation.relationFormatObjectTypes,
|
||||
isLocked = mode == EditorMode.Locked
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import com.anytypeio.anytype.domain.page.CreateBlockLinkWithObject
|
|||
import com.anytypeio.anytype.domain.page.CreateObject
|
||||
import com.anytypeio.anytype.domain.page.CreateObjectAsMentionOrLink
|
||||
import com.anytypeio.anytype.domain.page.OpenPage
|
||||
import com.anytypeio.anytype.domain.relations.AddRelationToObject
|
||||
import com.anytypeio.anytype.domain.search.SearchObjects
|
||||
import com.anytypeio.anytype.domain.sets.FindObjectSetForType
|
||||
import com.anytypeio.anytype.domain.status.InterceptThreadStatus
|
||||
|
@ -77,7 +78,8 @@ open class EditorViewModelFactory(
|
|||
private val workspaceManager: WorkspaceManager,
|
||||
private val getObjectTypes: GetObjectTypes,
|
||||
private val objectToCollection: ConvertObjectToCollection,
|
||||
private val interceptFileLimitEvents: InterceptFileLimitEvents
|
||||
private val interceptFileLimitEvents: InterceptFileLimitEvents,
|
||||
private val addRelationToObject: AddRelationToObject
|
||||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
|
@ -117,7 +119,8 @@ open class EditorViewModelFactory(
|
|||
tableDelegate = tableDelegate,
|
||||
workspaceManager = workspaceManager,
|
||||
getObjectTypes = getObjectTypes,
|
||||
interceptFileLimitEvents = interceptFileLimitEvents
|
||||
interceptFileLimitEvents = interceptFileLimitEvents,
|
||||
addRelationToObject = addRelationToObject
|
||||
) as T
|
||||
}
|
||||
}
|
|
@ -2090,7 +2090,7 @@ class DefaultBlockViewRenderer @Inject constructor(
|
|||
val obj = ObjectWrapper.Basic(details.details[ctx]?.map ?: emptyMap())
|
||||
val views = mapFeaturedRelations(
|
||||
ctx = ctx,
|
||||
keys = obj.featuredRelations ?: emptyList(),
|
||||
keys = obj.featuredRelations,
|
||||
details = details
|
||||
)
|
||||
return BlockView.FeaturedRelation(
|
||||
|
|
|
@ -246,7 +246,7 @@ fun List<ObjectWrapper.Basic>.toRelationFileValueView(
|
|||
}
|
||||
|
||||
private fun ObjectWrapper.Basic.getProperLayout() = layout ?: ObjectType.Layout.BASIC
|
||||
private fun ObjectWrapper.Basic.getProperType() = type.firstOrNull()
|
||||
fun ObjectWrapper.Basic.getProperType() = type.firstOrNull()
|
||||
private fun ObjectWrapper.Basic.getProperFileExt() = fileExt.orEmpty()
|
||||
private fun ObjectWrapper.Basic.getProperFileMime() = fileMimeType.orEmpty()
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import com.anytypeio.anytype.core_models.Payload
|
|||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.domain.`object`.UpdateDetail
|
||||
import com.anytypeio.anytype.domain.objects.StoreOfRelations
|
||||
import com.anytypeio.anytype.domain.relations.AddRelationToObject
|
||||
import com.anytypeio.anytype.domain.relations.AddToFeaturedRelations
|
||||
import com.anytypeio.anytype.domain.relations.DeleteRelationFromObject
|
||||
import com.anytypeio.anytype.domain.relations.RemoveFromFeaturedRelations
|
||||
|
@ -24,7 +25,8 @@ class ObjectRelationListViewModelFactory(
|
|||
private val removeFromFeaturedRelations: RemoveFromFeaturedRelations,
|
||||
private val deleteRelationFromObject: DeleteRelationFromObject,
|
||||
private val analytics: Analytics,
|
||||
private val storeOfRelations: StoreOfRelations
|
||||
private val storeOfRelations: StoreOfRelations,
|
||||
private val addRelationToObject: AddRelationToObject
|
||||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
|
@ -39,7 +41,8 @@ class ObjectRelationListViewModelFactory(
|
|||
removeFromFeaturedRelations = removeFromFeaturedRelations,
|
||||
deleteRelationFromObject = deleteRelationFromObject,
|
||||
analytics = analytics,
|
||||
storeOfRelations = storeOfRelations
|
||||
storeOfRelations = storeOfRelations,
|
||||
addRelationToObject = addRelationToObject
|
||||
) as T
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ import com.anytypeio.anytype.core_models.*
|
|||
import com.anytypeio.anytype.core_models.Relations.NUMBER_DEFAULT_VALUE
|
||||
import com.anytypeio.anytype.core_utils.const.DateConst
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.domain.objects.StoreOfRelations
|
||||
import com.anytypeio.anytype.presentation.extension.hasValue
|
||||
import com.anytypeio.anytype.presentation.number.NumberParser
|
||||
import com.anytypeio.anytype.presentation.sets.*
|
||||
|
@ -313,4 +314,42 @@ fun ColumnView.getDateRelationFormat(): String {
|
|||
} else {
|
||||
format
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a list of distinct [ObjectWrapper.Relation] relations of Object using the given relation links filtered by system relations.
|
||||
*
|
||||
* @param relationLinks The list of relation links (@see [RelationLink] class) used to identify the relations.
|
||||
* @param systemRelations The list of keys of the system relations. Final list will be filtered by this list.
|
||||
* @param storeOfRelations The store of relations to retrieve the relations from.
|
||||
*
|
||||
* @return A list of distinct [ObjectWrapper.Relation] object relations.
|
||||
*/
|
||||
suspend fun getObjectRelations(
|
||||
relationLinks: List<RelationLink>,
|
||||
systemRelations: List<Key>,
|
||||
storeOfRelations: StoreOfRelations
|
||||
): List<ObjectWrapper.Relation> {
|
||||
val systemRelationKeys = systemRelations.toSet()
|
||||
val objectRelationKeys = relationLinks.map { it.key }.filterNot { it in systemRelationKeys }
|
||||
return storeOfRelations.getByKeys(objectRelationKeys).distinctBy { it.key }
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a list of filtered [ObjectWrapper.Relation] recommended relations of Object.
|
||||
*
|
||||
* @param relationLinks The list of relation links (@see [RelationLink] class) used to identify the relations.
|
||||
* @param recommendedRelations The list of ids of the recommended relations.
|
||||
* @param storeOfRelations The store of relations to retrieve the relations from.
|
||||
*
|
||||
* @return A list of distinct [ObjectWrapper.Relation] object recommended relations.
|
||||
*/
|
||||
suspend fun getNotIncludedRecommendedRelations(
|
||||
relationLinks: List<RelationLink>,
|
||||
recommendedRelations: List<Id>,
|
||||
storeOfRelations: StoreOfRelations
|
||||
): List<ObjectWrapper.Relation> {
|
||||
val relationLinkKeys = relationLinks.map { it.key }.toSet()
|
||||
return storeOfRelations.getById(recommendedRelations)
|
||||
.filterNot { recommended -> recommended.key in relationLinkKeys }
|
||||
}
|
|
@ -6,15 +6,19 @@ import com.anytypeio.anytype.analytics.base.EventsDictionary.objectRelationFeatu
|
|||
import com.anytypeio.anytype.analytics.base.EventsDictionary.objectRelationUnfeature
|
||||
import com.anytypeio.anytype.analytics.base.EventsDictionary.relationsScreenShow
|
||||
import com.anytypeio.anytype.analytics.base.sendEvent
|
||||
import com.anytypeio.anytype.core_models.Block
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.Key
|
||||
import com.anytypeio.anytype.core_models.ObjectWrapper
|
||||
import com.anytypeio.anytype.core_models.Payload
|
||||
import com.anytypeio.anytype.core_models.RelationFormat
|
||||
import com.anytypeio.anytype.core_models.RelationLink
|
||||
import com.anytypeio.anytype.core_models.Relations
|
||||
import com.anytypeio.anytype.core_utils.diff.DefaultObjectDiffIdentifier
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.domain.`object`.UpdateDetail
|
||||
import com.anytypeio.anytype.domain.objects.StoreOfRelations
|
||||
import com.anytypeio.anytype.domain.relations.AddRelationToObject
|
||||
import com.anytypeio.anytype.domain.relations.AddToFeaturedRelations
|
||||
import com.anytypeio.anytype.domain.relations.DeleteRelationFromObject
|
||||
import com.anytypeio.anytype.domain.relations.RemoveFromFeaturedRelations
|
||||
|
@ -23,6 +27,7 @@ import com.anytypeio.anytype.presentation.common.BaseViewModel
|
|||
import com.anytypeio.anytype.presentation.extension.sendAnalyticsRelationDeleteEvent
|
||||
import com.anytypeio.anytype.presentation.extension.sendAnalyticsRelationValueEvent
|
||||
import com.anytypeio.anytype.presentation.objects.LockedStateProvider
|
||||
import com.anytypeio.anytype.presentation.objects.getProperType
|
||||
import com.anytypeio.anytype.presentation.relations.model.RelationOperationError
|
||||
import com.anytypeio.anytype.presentation.relations.providers.RelationListProvider
|
||||
import com.anytypeio.anytype.presentation.util.Dispatcher
|
||||
|
@ -30,7 +35,7 @@ import kotlinx.coroutines.Job
|
|||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
|
||||
|
@ -44,7 +49,8 @@ class RelationListViewModel(
|
|||
private val removeFromFeaturedRelations: RemoveFromFeaturedRelations,
|
||||
private val deleteRelationFromObject: DeleteRelationFromObject,
|
||||
private val analytics: Analytics,
|
||||
private val storeOfRelations: StoreOfRelations
|
||||
private val storeOfRelations: StoreOfRelations,
|
||||
private val addRelationToObject: AddRelationToObject
|
||||
) : BaseViewModel() {
|
||||
|
||||
val isEditMode = MutableStateFlow(false)
|
||||
|
@ -56,6 +62,7 @@ class RelationListViewModel(
|
|||
val views = MutableStateFlow<List<Model>>(emptyList())
|
||||
|
||||
fun onStartListMode(ctx: Id) {
|
||||
Timber.d("onStartListMode, ctx: $ctx")
|
||||
isInAddMode.value = false
|
||||
viewModelScope.sendEvent(
|
||||
analytics = analytics,
|
||||
|
@ -67,39 +74,112 @@ class RelationListViewModel(
|
|||
relationListProvider.links,
|
||||
relationListProvider.details
|
||||
) { _, relationLinks, details ->
|
||||
val relations = relationLinks.mapNotNull { storeOfRelations.getByKey(it.key) }
|
||||
val detail = details.details[ctx]
|
||||
val values = detail?.map ?: emptyMap()
|
||||
val featured = detail?.featuredRelations ?: emptyList()
|
||||
relations.views(
|
||||
context = ctx,
|
||||
details = details,
|
||||
values = values,
|
||||
urlBuilder = urlBuilder,
|
||||
featured = featured
|
||||
).map { view ->
|
||||
Model.Item(
|
||||
view = view,
|
||||
isRemovable = isPossibleToRemoveRelation(relationKey = view.key)
|
||||
)
|
||||
}
|
||||
}.map { views ->
|
||||
val result = mutableListOf<Model>().apply {
|
||||
val (isFeatured, other) = views.partition { it.view.featured }
|
||||
if (isFeatured.isNotEmpty()) {
|
||||
add(Model.Section.Featured)
|
||||
addAll(isFeatured)
|
||||
}
|
||||
if (other.isNotEmpty()) {
|
||||
add(Model.Section.Other)
|
||||
addAll(other)
|
||||
}
|
||||
}
|
||||
result
|
||||
constructViews(ctx, relationLinks, details)
|
||||
}.collect { views.value = it }
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun constructViews(
|
||||
ctx: Id,
|
||||
relationLinks: List<RelationLink>,
|
||||
details: Block.Details
|
||||
): List<Model> {
|
||||
|
||||
val objectDetails = details.details[ctx]?.map ?: emptyMap()
|
||||
val objectWrapper = ObjectWrapper.Basic(objectDetails)
|
||||
val objectType = objectWrapper.getProperType()
|
||||
val objectTypeWrapper = ObjectWrapper.Type(details.details[objectType]?.map ?: emptyMap())
|
||||
|
||||
val objectRelationViews = getObjectRelationsView(
|
||||
ctx = ctx,
|
||||
objectDetails = objectDetails,
|
||||
relationLinks = relationLinks,
|
||||
details = details,
|
||||
objectWrapper = objectWrapper
|
||||
)
|
||||
|
||||
val recommendedRelationViews = getRecommendedRelations(
|
||||
ctx = ctx,
|
||||
objectDetails = objectDetails,
|
||||
relationLinks = relationLinks,
|
||||
objectTypeWrapper = objectTypeWrapper,
|
||||
details = details
|
||||
)
|
||||
|
||||
return buildFinalList(objectRelationViews, recommendedRelationViews, objectTypeWrapper)
|
||||
}
|
||||
|
||||
private suspend fun getObjectRelationsView(
|
||||
ctx: Id,
|
||||
objectDetails: Map<Key, Any?>,
|
||||
relationLinks: List<RelationLink>,
|
||||
details: Block.Details,
|
||||
objectWrapper: ObjectWrapper.Basic
|
||||
): List<Model.Item> {
|
||||
return getObjectRelations(
|
||||
systemRelations = listOf(),
|
||||
relationLinks = relationLinks,
|
||||
storeOfRelations = storeOfRelations
|
||||
).views(
|
||||
context = ctx,
|
||||
details = details,
|
||||
values = objectDetails,
|
||||
urlBuilder = urlBuilder,
|
||||
featured = objectWrapper.featuredRelations
|
||||
).map { view ->
|
||||
Model.Item(
|
||||
view = view,
|
||||
isRemovable = isPossibleToRemoveRelation(relationKey = view.key)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getRecommendedRelations(
|
||||
ctx: Id,
|
||||
objectDetails: Map<Key, Any?>,
|
||||
relationLinks: List<RelationLink>,
|
||||
objectTypeWrapper: ObjectWrapper.Type,
|
||||
details: Block.Details
|
||||
): List<Model.Item> {
|
||||
return getNotIncludedRecommendedRelations(
|
||||
relationLinks = relationLinks,
|
||||
recommendedRelations = objectTypeWrapper.recommendedRelations,
|
||||
storeOfRelations = storeOfRelations
|
||||
).views(
|
||||
context = ctx,
|
||||
details = details,
|
||||
values = objectDetails,
|
||||
urlBuilder = urlBuilder
|
||||
).map { view ->
|
||||
Model.Item(
|
||||
view = view,
|
||||
isRecommended = true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildFinalList(
|
||||
objectRelations: List<Model.Item>,
|
||||
recommendedRelations: List<Model.Item>,
|
||||
objectTypeWrapper: ObjectWrapper.Type
|
||||
): MutableList<Model> {
|
||||
return mutableListOf<Model>().apply {
|
||||
val (isFeatured, other) = objectRelations.partition { it.view.featured }
|
||||
if (isFeatured.isNotEmpty()) {
|
||||
add(Model.Section.Featured)
|
||||
addAll(isFeatured)
|
||||
}
|
||||
if (other.isNotEmpty()) {
|
||||
add(Model.Section.Other)
|
||||
addAll(other)
|
||||
}
|
||||
if (recommendedRelations.isNotEmpty()) {
|
||||
add(Model.Section.TypeFrom(objectTypeWrapper.name.orEmpty()))
|
||||
addAll(recommendedRelations)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onStartAddMode(ctx: Id) {
|
||||
isInAddMode.value = true
|
||||
getRelations(ctx)
|
||||
|
@ -113,20 +193,66 @@ class RelationListViewModel(
|
|||
}
|
||||
|
||||
fun onRelationClicked(ctx: Id, target: Id?, view: ObjectRelationView) {
|
||||
if (isInAddMode.value) {
|
||||
onRelationClickedAddMode(target = target, view = view)
|
||||
} else {
|
||||
onRelationClickedListMode(ctx = ctx, view = view)
|
||||
Timber.d("onRelationClicked, ctx: $ctx, target: $target, view: $view")
|
||||
val isLocked = resolveIsLockedState(ctx)
|
||||
if (isLocked) {
|
||||
sendToast(RelationOperationError.LOCKED_OBJECT_MODIFICATION_ERROR)
|
||||
return
|
||||
}
|
||||
viewModelScope.launch {
|
||||
if (isInAddMode.value) {
|
||||
onRelationClickedAddMode(target = target, view = view)
|
||||
} else {
|
||||
if (checkRelationIsInObject(view)) {
|
||||
onRelationClickedListMode(ctx, view)
|
||||
} else {
|
||||
proceedWithAddingRelationToObject(ctx, view) {
|
||||
onRelationClickedListMode(ctx, view)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onCheckboxClicked(ctx: Id, view: ObjectRelationView) {
|
||||
Timber.d("onCheckboxClicked, ctx: $ctx, view: $view")
|
||||
val isLocked = resolveIsLockedState(ctx)
|
||||
if (isLocked) {
|
||||
sendToast(RelationOperationError.LOCKED_OBJECT_MODIFICATION_ERROR)
|
||||
} else {
|
||||
proceedWithUpdatingFeaturedRelations(view, ctx)
|
||||
return
|
||||
}
|
||||
viewModelScope.launch {
|
||||
if (checkRelationIsInObject(view)) {
|
||||
proceedWithUpdatingFeaturedRelations(view, ctx)
|
||||
} else {
|
||||
proceedWithAddingRelationToObject(ctx, view) {
|
||||
proceedWithUpdatingFeaturedRelations(view, ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun checkRelationIsInObject(view: ObjectRelationView): Boolean {
|
||||
val relationLinks = relationListProvider.links.stateIn(viewModelScope).value
|
||||
return relationLinks.any { it.key == view.key }
|
||||
}
|
||||
|
||||
private suspend fun proceedWithAddingRelationToObject(
|
||||
ctx: Id,
|
||||
view: ObjectRelationView,
|
||||
success: () -> Unit
|
||||
) {
|
||||
val params = AddRelationToObject.Params(
|
||||
ctx = ctx,
|
||||
relationKey = view.key
|
||||
)
|
||||
addRelationToObject.run(params).process(
|
||||
failure = { Timber.e(it, "Error while adding relation to object") },
|
||||
success = {
|
||||
dispatcher.send(it)
|
||||
success.invoke()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun proceedWithUpdatingFeaturedRelations(
|
||||
|
@ -198,7 +324,7 @@ class RelationListViewModel(
|
|||
} else {
|
||||
isEditMode.value = !isEditMode.value
|
||||
views.value = views.value.map { view ->
|
||||
if (view is Model.Item) {
|
||||
if (view is Model.Item && !view.isRecommended) {
|
||||
view.copy(isRemovable = isPossibleToRemoveRelation(relationKey = view.view.key))
|
||||
} else {
|
||||
view
|
||||
|
@ -211,19 +337,17 @@ class RelationListViewModel(
|
|||
return isEditMode.value && !Relations.systemRelationKeys.contains(relationKey)
|
||||
}
|
||||
|
||||
private fun onRelationClickedAddMode(
|
||||
private suspend fun onRelationClickedAddMode(
|
||||
target: Id?,
|
||||
view: ObjectRelationView
|
||||
) {
|
||||
checkNotNull(target)
|
||||
viewModelScope.launch {
|
||||
commands.emit(
|
||||
Command.SetRelationKey(
|
||||
blockId = target,
|
||||
key = view.key
|
||||
)
|
||||
commands.emit(
|
||||
Command.SetRelationKey(
|
||||
blockId = target,
|
||||
key = view.key
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun onRelationClickedListMode(ctx: Id, view: ObjectRelationView) {
|
||||
|
@ -331,13 +455,14 @@ class RelationListViewModel(
|
|||
|
||||
private fun getRelations(ctx: Id) {
|
||||
viewModelScope.launch {
|
||||
val relations = relationListProvider.getLinks().mapNotNull { storeOfRelations.getByKey(it.key) }
|
||||
val relations =
|
||||
relationListProvider.getLinks().mapNotNull { storeOfRelations.getByKey(it.key) }
|
||||
val details = relationListProvider.getDetails()
|
||||
val values = details.details[ctx]?.map ?: emptyMap()
|
||||
views.value = relations.views(
|
||||
details = details,
|
||||
values = values,
|
||||
urlBuilder = urlBuilder
|
||||
details = details,
|
||||
values = values,
|
||||
urlBuilder = urlBuilder
|
||||
).map { Model.Item(it) }
|
||||
}
|
||||
}
|
||||
|
@ -373,11 +498,16 @@ class RelationListViewModel(
|
|||
object Other : Section() {
|
||||
override val identifier: String get() = "Section_Other"
|
||||
}
|
||||
|
||||
data class TypeFrom(val typeName: String) : Section() {
|
||||
override val identifier: String get() = "Section_TypeFrom"
|
||||
}
|
||||
}
|
||||
|
||||
data class Item(
|
||||
val view: ObjectRelationView,
|
||||
val isRemovable: Boolean = false
|
||||
val isRemovable: Boolean = false,
|
||||
val isRecommended: Boolean = false
|
||||
) : Model() {
|
||||
override val identifier: String get() = view.identifier
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@ fun ObjectState.DataView.header(
|
|||
val wrapper = ObjectWrapper.Basic(
|
||||
map = details[ctx]?.map ?: emptyMap()
|
||||
)
|
||||
val featured = wrapper.featuredRelations ?: emptyList()
|
||||
val featured = wrapper.featuredRelations
|
||||
SetOrCollectionHeaderState.Default(
|
||||
title = title(
|
||||
ctx = ctx,
|
||||
|
|
|
@ -25,6 +25,7 @@ import com.anytypeio.anytype.presentation.relations.providers.ObjectValueProvide
|
|||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.launch
|
||||
|
@ -49,6 +50,7 @@ class RelationTextValueViewModel(
|
|||
name: String,
|
||||
value: Long?,
|
||||
) {
|
||||
Timber.d("onDateStart, name:[$name], value:[$value]")
|
||||
title.value = name
|
||||
views.value = listOf(
|
||||
RelationTextValueView.Number(
|
||||
|
@ -60,14 +62,16 @@ class RelationTextValueViewModel(
|
|||
fun onStart(
|
||||
ctx: Id,
|
||||
relationKey: Key,
|
||||
recordId: String,
|
||||
objectId: Id,
|
||||
isLocked: Boolean = false
|
||||
) {
|
||||
Timber.d("onStart, ctx:[$ctx], relationKey:[$relationKey], object:[$objectId], isLocked:[$isLocked]")
|
||||
jobs += viewModelScope.launch {
|
||||
val pipeline = combine(
|
||||
relations.observe(relationKey),
|
||||
values.subscribe(ctx = ctx, target = recordId)
|
||||
values.subscribe(ctx = ctx, target = objectId)
|
||||
) { relation, values ->
|
||||
Timber.d("combine, relation:[$relation], values:[$values]")
|
||||
val obj = ObjectWrapper.Basic(values)
|
||||
val value = values[relationKey]?.toString()
|
||||
val isValueReadOnly = values[Relations.IS_READ_ONLY] as? Boolean ?: false
|
||||
|
@ -146,7 +150,7 @@ class RelationTextValueViewModel(
|
|||
}
|
||||
else -> throw IllegalArgumentException("Wrong format:${relation.format}")
|
||||
}
|
||||
}
|
||||
}.catch { Timber.e(it, "Error getting relation") }
|
||||
pipeline.collect()
|
||||
}
|
||||
}
|
||||
|
@ -159,6 +163,7 @@ class RelationTextValueViewModel(
|
|||
target: Id,
|
||||
action: RelationValueAction
|
||||
) {
|
||||
Timber.d("onAction, target:[$target], action:[$action]")
|
||||
when (action) {
|
||||
is RelationValueAction.Email.Copy -> {
|
||||
viewModelScope.launch {
|
||||
|
|
|
@ -76,6 +76,7 @@ import com.anytypeio.anytype.domain.page.Undo
|
|||
import com.anytypeio.anytype.domain.page.UpdateTitle
|
||||
import com.anytypeio.anytype.domain.page.bookmark.CreateBookmarkBlock
|
||||
import com.anytypeio.anytype.domain.page.bookmark.SetupBookmark
|
||||
import com.anytypeio.anytype.domain.relations.AddRelationToObject
|
||||
import com.anytypeio.anytype.domain.relations.SetRelationKey
|
||||
import com.anytypeio.anytype.domain.search.SearchObjects
|
||||
import com.anytypeio.anytype.domain.sets.FindObjectSetForType
|
||||
|
@ -3882,6 +3883,8 @@ open class EditorViewModelTest {
|
|||
|
||||
lateinit var getObjectTypes: GetObjectTypes
|
||||
|
||||
lateinit var addRelationToObject: AddRelationToObject
|
||||
|
||||
fun givenViewModel(urlBuilder: UrlBuilder = builder) {
|
||||
|
||||
val storage = Editor.Storage()
|
||||
|
@ -3889,6 +3892,7 @@ open class EditorViewModelTest {
|
|||
val memory = Editor.Memory(
|
||||
selections = SelectionStateHolder.Default()
|
||||
)
|
||||
addRelationToObject = AddRelationToObject(repo)
|
||||
objectToSet = ConvertObjectToSet(repo, dispatchers)
|
||||
updateDetail = UpdateDetail(repo)
|
||||
setDocCoverImage = SetDocCoverImage(repo)
|
||||
|
@ -3988,7 +3992,8 @@ open class EditorViewModelTest {
|
|||
tableDelegate = tableDelegate,
|
||||
workspaceManager = workspaceManager,
|
||||
getObjectTypes = getObjectTypes,
|
||||
interceptFileLimitEvents = interceptFileLimitEvents
|
||||
interceptFileLimitEvents = interceptFileLimitEvents,
|
||||
addRelationToObject = addRelationToObject
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -64,6 +64,7 @@ import com.anytypeio.anytype.domain.page.Redo
|
|||
import com.anytypeio.anytype.domain.page.Undo
|
||||
import com.anytypeio.anytype.domain.page.bookmark.CreateBookmarkBlock
|
||||
import com.anytypeio.anytype.domain.page.bookmark.SetupBookmark
|
||||
import com.anytypeio.anytype.domain.relations.AddRelationToObject
|
||||
import com.anytypeio.anytype.domain.relations.SetRelationKey
|
||||
import com.anytypeio.anytype.domain.search.SearchObjects
|
||||
import com.anytypeio.anytype.domain.sets.FindObjectSetForType
|
||||
|
@ -323,6 +324,9 @@ open class EditorPresentationTestSetup {
|
|||
@Mock
|
||||
lateinit var moveTableColumn: MoveTableColumn
|
||||
|
||||
@Mock
|
||||
lateinit var addRelationToObject: AddRelationToObject
|
||||
|
||||
@Mock
|
||||
lateinit var setTableRowHeader: SetTableRowHeader
|
||||
|
||||
|
@ -462,7 +466,8 @@ open class EditorPresentationTestSetup {
|
|||
tableDelegate = tableDelegate,
|
||||
workspaceManager = workspaceManager,
|
||||
getObjectTypes = getObjectTypes,
|
||||
interceptFileLimitEvents = interceptFileLimitEvents
|
||||
interceptFileLimitEvents = interceptFileLimitEvents,
|
||||
addRelationToObject = addRelationToObject
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -3,8 +3,14 @@ package com.anytypeio.anytype.presentation.relations
|
|||
import com.anytypeio.anytype.core_models.Block
|
||||
import com.anytypeio.anytype.core_models.DVFilter
|
||||
import com.anytypeio.anytype.core_models.Relation
|
||||
import com.anytypeio.anytype.core_models.RelationFormat
|
||||
import com.anytypeio.anytype.core_models.RelationLink
|
||||
import com.anytypeio.anytype.core_models.Relations
|
||||
import com.anytypeio.anytype.domain.objects.DefaultStoreOfRelations
|
||||
import com.anytypeio.anytype.presentation.MockTypicalDocumentFactory
|
||||
import com.anytypeio.anytype.test_utils.MockDataFactory
|
||||
import kotlinx.coroutines.test.advanceUntilIdle
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
|
@ -95,4 +101,46 @@ class RelationExtensionsTest {
|
|||
|
||||
assertEquals(expected, result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should filter recommended by object relations`() = runTest {
|
||||
// Given
|
||||
val mockFactory = MockTypicalDocumentFactory
|
||||
|
||||
// relations in StoreOfRelations
|
||||
val relations =
|
||||
List(2) { mockFactory.relationObject(name = "relation-$it") } +
|
||||
List(2) { mockFactory.relationObject(name = "recommendedRelation-$it") }
|
||||
|
||||
val relationLinks = listOf(
|
||||
RelationLink(
|
||||
key = relations[0].key,
|
||||
format = RelationFormat.OBJECT
|
||||
),
|
||||
RelationLink(
|
||||
key = relations[1].key,
|
||||
format = RelationFormat.NUMBER
|
||||
),
|
||||
RelationLink(
|
||||
key = relations[3].key,
|
||||
format = RelationFormat.CHECKBOX
|
||||
),
|
||||
)
|
||||
|
||||
val recommendedRelations = listOf(relations[2].id, relations[3].id)
|
||||
|
||||
val storeOfRelations = DefaultStoreOfRelations().apply { merge(relations) }
|
||||
advanceUntilIdle()
|
||||
|
||||
// When
|
||||
val result = getNotIncludedRecommendedRelations(
|
||||
relationLinks = relationLinks,
|
||||
recommendedRelations = recommendedRelations,
|
||||
storeOfRelations = storeOfRelations
|
||||
)
|
||||
|
||||
// Then
|
||||
val expected = listOf(relations[2])
|
||||
kotlin.test.assertEquals(expected, result)
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue