mirror of
https://github.com/anyproto/anytype-kotlin.git
synced 2025-06-08 05:47:05 +09:00
Relations | Fix | Filter create option for tags that already exist (#2237)
* Relations | Fix | Filter create option for tags that already exist * Tech | Fix | Change compile android tests task * Tech | Fix | add build folders ignore to global .gitignore * Tech | Fix | Extract MockDataFactory.kt from test-utils * Tech | Fix | Move return to the same line as checkNotNull * Tech | Fix | Delete unused analytics property * Relations | Fix | Add AddObjectRelationValueViewModelTest.kt test query with tags behaviour * Tech | Fix | Rename :test:utils to :test:android-utils * Tech | Enhancement | implement fake providers for AddObjectRelationViewModerl
This commit is contained in:
parent
8e678c95d4
commit
58da3badfe
37 changed files with 770 additions and 469 deletions
2
.github/workflows/check.yml
vendored
2
.github/workflows/check.yml
vendored
|
@ -18,7 +18,7 @@ jobs:
|
|||
amplitude_secret_debug: ${{ secrets.ANYTYPE_AMPLITUDE_DEBUG_SECRET }}
|
||||
run: ./middleware2.sh $token_secret $user_secret $amplitude_secret $amplitude_secret_debug
|
||||
- name: Compile android test sources
|
||||
run: ./gradlew compileDebugAndroidTestSources
|
||||
run: ./gradlew compileExperimentalDebugAndroidTestSources
|
||||
- name: Run unit tests
|
||||
run: ./gradlew testDebugUnitTest -Dpre-dex=false
|
||||
- name: Android test report
|
||||
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -8,7 +8,7 @@
|
|||
/github.properties
|
||||
/apikeys.properties
|
||||
.DS_Store
|
||||
/build
|
||||
**/build
|
||||
/libs
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
|
|
|
@ -221,7 +221,7 @@ dependencies {
|
|||
testImplementation unitTestDependencies.mockitoKotlin
|
||||
|
||||
//Acceptance tests dependencies
|
||||
androidTestImplementation project(':test-utils')
|
||||
androidTestImplementation project(':test:android-utils')
|
||||
androidTestImplementation acceptanceTesting.mockitoAndroid
|
||||
androidTestImplementation unitTestDependencies.mockitoKotlin
|
||||
androidTestImplementation acceptanceTesting.espressoCore
|
||||
|
|
|
@ -117,7 +117,6 @@ class AddRelationStatusValueTest {
|
|||
addStatusToDataViewRecord = addStatusToDataViewRecord,
|
||||
urlBuilder = urlBuilder,
|
||||
dispatcher = dispatcher,
|
||||
analytics = analytics
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -116,7 +116,6 @@ class AddRelationTagValueTest {
|
|||
addStatusToDataViewRecord = addStatusToDataViewRecord,
|
||||
urlBuilder = urlBuilder,
|
||||
dispatcher = dispatcher,
|
||||
analytics = analytics
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -52,7 +52,6 @@ object AddObjectRelationValueModule {
|
|||
addTagToDataViewRecord: AddTagToDataViewRecord,
|
||||
addStatusToDataViewRecord: AddStatusToDataViewRecord,
|
||||
urlBuilder: UrlBuilder,
|
||||
analytics: Analytics
|
||||
): RelationOptionValueDVAddViewModel.Factory = RelationOptionValueDVAddViewModel.Factory(
|
||||
relations = relations,
|
||||
values = values,
|
||||
|
@ -63,7 +62,6 @@ object AddObjectRelationValueModule {
|
|||
addDataViewRelationOption = addDataViewRelationOption,
|
||||
addTagToDataViewRecord = addTagToDataViewRecord,
|
||||
addStatusToDataViewRecord = addStatusToDataViewRecord,
|
||||
analytics = analytics
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
|
|
1
core-models/.gitignore
vendored
1
core-models/.gitignore
vendored
|
@ -1 +0,0 @@
|
|||
/build
|
|
@ -1,13 +1,11 @@
|
|||
apply plugin: 'kotlin'
|
||||
apply plugin: 'org.jetbrains.dokka'
|
||||
plugins {
|
||||
id "kotlin"
|
||||
id "org.jetbrains.dokka"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation mainApplication.kotlin
|
||||
|
||||
def applicationDependencies = rootProject.ext.mainApplication
|
||||
def unitTestDependencies = rootProject.ext.unitTesting
|
||||
|
||||
implementation applicationDependencies.kotlin
|
||||
|
||||
testImplementation unitTestDependencies.kotlinTest
|
||||
testImplementation unitTestDependencies.mockitoKotlin
|
||||
testImplementation unitTesting.kotlinTest
|
||||
testImplementation unitTesting.mockitoKotlin
|
||||
}
|
|
@ -76,7 +76,7 @@ dependencies {
|
|||
debugImplementation applicationDependencies.composeTooling
|
||||
|
||||
testImplementation acceptanceTesting.fragmentTesting
|
||||
testImplementation project(':test-utils')
|
||||
testImplementation project(':test:android-utils')
|
||||
testImplementation acceptanceTesting.espressoCore
|
||||
testImplementation unitTestDependencies.junit
|
||||
testImplementation unitTestDependencies.kotlinTest
|
||||
|
|
|
@ -18,8 +18,7 @@ inline fun <reified T> Fragment.argOrNull(key: String): T? {
|
|||
|
||||
fun Fragment.argString(key: String): String {
|
||||
val value = requireArguments().getString(key)
|
||||
checkNotNull(value) { "Value missing for $key" }
|
||||
return value
|
||||
return checkNotNull(value) { "Value missing for $key" }
|
||||
}
|
||||
|
||||
fun Fragment.argStringOrNull(key: String): String? {
|
||||
|
@ -36,8 +35,7 @@ fun Fragment.argLong(key: String): Long {
|
|||
|
||||
fun <T : Parcelable> Fragment.argList(key: String): ArrayList<T> {
|
||||
val value = requireArguments().getParcelableArrayList<T>(key)
|
||||
checkNotNull(value)
|
||||
return value
|
||||
return checkNotNull(value)
|
||||
}
|
||||
|
||||
fun <T> CoroutineScope.subscribe(flow: Flow<T>, body: suspend (T) -> Unit): Job =
|
||||
|
|
|
@ -55,4 +55,5 @@ dependencies {
|
|||
testImplementation unitTestDependencies.robolectricLatest
|
||||
testImplementation unitTestDependencies.timberJUnit
|
||||
testImplementation unitTestDependencies.turbine
|
||||
testImplementation project(":test:core-models-stub")
|
||||
}
|
||||
|
|
|
@ -1,37 +1,25 @@
|
|||
package com.anytypeio.anytype.presentation.relations
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.anytypeio.anytype.analytics.base.Analytics
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.ObjectWrapper
|
||||
import com.anytypeio.anytype.core_models.Payload
|
||||
import com.anytypeio.anytype.core_models.Relation
|
||||
import com.anytypeio.anytype.core_utils.ext.typeOf
|
||||
import com.anytypeio.anytype.domain.`object`.ObjectTypesProvider
|
||||
import com.anytypeio.anytype.domain.`object`.UpdateDetail
|
||||
import com.anytypeio.anytype.domain.dataview.interactor.AddDataViewRelationOption
|
||||
import com.anytypeio.anytype.domain.dataview.interactor.AddStatusToDataViewRecord
|
||||
import com.anytypeio.anytype.domain.dataview.interactor.AddTagToDataViewRecord
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.domain.relations.AddObjectRelationOption
|
||||
import com.anytypeio.anytype.presentation.common.BaseViewModel
|
||||
import com.anytypeio.anytype.presentation.editor.editor.ThemeColor
|
||||
import com.anytypeio.anytype.presentation.extension.sendAnalyticsRelationValueEvent
|
||||
import com.anytypeio.anytype.presentation.objects.ObjectIcon
|
||||
import com.anytypeio.anytype.presentation.objects.getProperName
|
||||
import com.anytypeio.anytype.presentation.relations.providers.ObjectDetailProvider
|
||||
import com.anytypeio.anytype.presentation.relations.providers.ObjectRelationProvider
|
||||
import com.anytypeio.anytype.presentation.relations.providers.ObjectValueProvider
|
||||
import com.anytypeio.anytype.presentation.sets.RelationValueBaseViewModel.RelationValueView
|
||||
import com.anytypeio.anytype.presentation.util.Dispatcher
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
|
||||
|
||||
abstract class AddObjectRelationValueViewModel(
|
||||
|
@ -40,7 +28,6 @@ abstract class AddObjectRelationValueViewModel(
|
|||
protected val relations: ObjectRelationProvider,
|
||||
protected val types: ObjectTypesProvider,
|
||||
protected val urlBuilder: UrlBuilder,
|
||||
analytics: Analytics
|
||||
) : BaseViewModel() {
|
||||
|
||||
private val jobs = mutableListOf<Job>()
|
||||
|
@ -60,34 +47,49 @@ abstract class AddObjectRelationValueViewModel(
|
|||
init {
|
||||
viewModelScope.launch {
|
||||
views.combine(query) { all, query ->
|
||||
if (query.isEmpty())
|
||||
all
|
||||
else
|
||||
mutableListOf<RelationValueView>().apply {
|
||||
add(RelationValueView.Create(query))
|
||||
addAll(
|
||||
all.filter { view ->
|
||||
when(view) {
|
||||
is RelationValueView.Status -> {
|
||||
view.name.contains(query, true)
|
||||
}
|
||||
is RelationValueView.Tag -> {
|
||||
view.name.contains(query, true)
|
||||
}
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
println("views=$all, query=$query")
|
||||
filterRelationsBy(query, all)
|
||||
}.collect { ui.value = it }
|
||||
}
|
||||
}
|
||||
|
||||
private fun filterRelationsBy(
|
||||
query: String,
|
||||
all: List<RelationValueView>
|
||||
): List<RelationValueView> {
|
||||
return if (query.isEmpty())
|
||||
all
|
||||
else {
|
||||
val result = mutableListOf<RelationValueView>()
|
||||
val filteredRelationViews = all.filter { view ->
|
||||
when (view) {
|
||||
is RelationValueView.Status -> {
|
||||
view.name.contains(query, true)
|
||||
}
|
||||
is RelationValueView.Tag -> {
|
||||
view.name.contains(query, true)
|
||||
}
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
val searchedExistentTag =
|
||||
filteredRelationViews.filterIsInstance<RelationValueView.Tag>()
|
||||
.any { view -> view.name == query }
|
||||
|
||||
if (!searchedExistentTag) {
|
||||
result.add(RelationValueView.Create(query))
|
||||
}
|
||||
result.addAll(filteredRelationViews)
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
fun onStart(target: Id, relationId: Id) {
|
||||
val s1 = relations.subscribe(relationId)
|
||||
val s2 = values.subscribe(target)
|
||||
jobs += viewModelScope.launch {
|
||||
s1.combine(s2) { relation, record ->
|
||||
println("relation=$relation, record=$record")
|
||||
buildViews(relation, record, relationId).also {
|
||||
if (relation.format == Relation.Format.STATUS) {
|
||||
isAddButtonVisible.value = false
|
||||
|
@ -216,9 +218,12 @@ abstract class AddObjectRelationValueViewModel(
|
|||
}
|
||||
|
||||
views.value = result
|
||||
println("views.value set $result")
|
||||
}
|
||||
|
||||
fun onFilterInputChanged(input: String) { query.value = input }
|
||||
fun onFilterInputChanged(input: String) {
|
||||
query.value = input
|
||||
}
|
||||
|
||||
fun onTagClicked(tag: RelationValueView.Tag) {
|
||||
views.value = views.value.map { view ->
|
||||
|
@ -233,372 +238,9 @@ abstract class AddObjectRelationValueViewModel(
|
|||
view
|
||||
}
|
||||
}.also { result ->
|
||||
counter.value = result.count { it is RelationValueView.Selectable && it.isSelected == true }
|
||||
counter.value =
|
||||
result.count { it is RelationValueView.Selectable && it.isSelected == true }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class RelationOptionValueDVAddViewModel(
|
||||
details: ObjectDetailProvider,
|
||||
types: ObjectTypesProvider,
|
||||
urlBuilder: UrlBuilder,
|
||||
values: ObjectValueProvider,
|
||||
relations: ObjectRelationProvider,
|
||||
private val addDataViewRelationOption: AddDataViewRelationOption,
|
||||
private val addTagToDataViewRecord: AddTagToDataViewRecord,
|
||||
private val addStatusToDataViewRecord: AddStatusToDataViewRecord,
|
||||
private val dispatcher: Dispatcher<Payload>,
|
||||
private val analytics: Analytics
|
||||
) : AddObjectRelationValueViewModel(
|
||||
details = details,
|
||||
values = values,
|
||||
types = types,
|
||||
urlBuilder = urlBuilder,
|
||||
relations = relations,
|
||||
analytics = analytics
|
||||
) {
|
||||
|
||||
fun onCreateDataViewRelationOptionClicked(
|
||||
ctx: Id,
|
||||
dataview: Id,
|
||||
viewer: Id,
|
||||
relation: Id,
|
||||
target: Id,
|
||||
name: String
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
addDataViewRelationOption(
|
||||
AddDataViewRelationOption.Params(
|
||||
ctx = ctx,
|
||||
relation = relation,
|
||||
dataview = dataview,
|
||||
record = target,
|
||||
name = name,
|
||||
color = ThemeColor.values().filter { it != ThemeColor.DEFAULT }.random().title
|
||||
)
|
||||
).proceed(
|
||||
success = { (payload, option) ->
|
||||
dispatcher.send(payload)
|
||||
if (option != null) {
|
||||
when (relations.get(relation).format) {
|
||||
Relation.Format.TAG -> {
|
||||
proceedWithAddingTagToDataViewRecord(
|
||||
ctx = ctx,
|
||||
dataview = dataview,
|
||||
viewer = viewer,
|
||||
relation = relation,
|
||||
target = target,
|
||||
tags = listOf(option)
|
||||
)
|
||||
}
|
||||
Relation.Format.STATUS -> {
|
||||
proceedWithAddingStatusToDataViewRecord(
|
||||
ctx = ctx,
|
||||
dataview = dataview,
|
||||
viewer = viewer,
|
||||
relation = relation,
|
||||
obj = target,
|
||||
status = option
|
||||
)
|
||||
}
|
||||
else -> Timber.e("Trying to create option for wrong relation.")
|
||||
}
|
||||
}
|
||||
},
|
||||
failure = { Timber.e(it, "Error while creating a new option") }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun onAddObjectSetStatusClicked(
|
||||
ctx: Id,
|
||||
dataview: Id,
|
||||
viewer: Id,
|
||||
relation: Id,
|
||||
obj: Id,
|
||||
status: RelationValueView.Status
|
||||
) {
|
||||
proceedWithAddingStatusToDataViewRecord(ctx, dataview, viewer, relation, obj, status.id)
|
||||
}
|
||||
|
||||
private fun proceedWithAddingStatusToDataViewRecord(
|
||||
ctx: Id,
|
||||
dataview: Id,
|
||||
viewer: Id,
|
||||
relation: Id,
|
||||
obj: Id,
|
||||
status: Id
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
addStatusToDataViewRecord(
|
||||
AddStatusToDataViewRecord.Params(
|
||||
ctx = ctx,
|
||||
dataview = dataview,
|
||||
viewer = viewer,
|
||||
relation = relation,
|
||||
obj = obj,
|
||||
status = status,
|
||||
record = values.get(target = obj)
|
||||
)
|
||||
).process(
|
||||
failure = { Timber.e(it, "Error while adding tag") },
|
||||
success = { isParentDismissed.value = true }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun onAddSelectedValuesToDataViewClicked(
|
||||
ctx: Id,
|
||||
dataview: Id,
|
||||
target: Id,
|
||||
relation: Id,
|
||||
viewer: Id
|
||||
) {
|
||||
val tags = views.value.mapNotNull { view ->
|
||||
if (view is RelationValueView.Tag && view.isSelected == true)
|
||||
view.id
|
||||
else
|
||||
null
|
||||
}
|
||||
proceedWithAddingTagToDataViewRecord(
|
||||
target = target,
|
||||
ctx = ctx,
|
||||
dataview = dataview,
|
||||
relation = relation,
|
||||
viewer = viewer,
|
||||
tags = tags
|
||||
)
|
||||
}
|
||||
|
||||
private fun proceedWithAddingTagToDataViewRecord(
|
||||
target: Id,
|
||||
ctx: Id,
|
||||
dataview: Id,
|
||||
relation: Id,
|
||||
viewer: Id,
|
||||
tags: List<Id>
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
val record = values.get(target = target)
|
||||
addTagToDataViewRecord(
|
||||
AddTagToDataViewRecord.Params(
|
||||
ctx = ctx,
|
||||
tags = tags,
|
||||
record = record,
|
||||
dataview = dataview,
|
||||
relation = relation,
|
||||
viewer = viewer,
|
||||
target = target
|
||||
)
|
||||
).process(
|
||||
failure = { Timber.e(it, "Error while adding tag") },
|
||||
success = { isDismissed.value = true }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class Factory(
|
||||
private val values: ObjectValueProvider,
|
||||
private val details: ObjectDetailProvider,
|
||||
private val relations: ObjectRelationProvider,
|
||||
private val types: ObjectTypesProvider,
|
||||
private val addDataViewRelationOption: AddDataViewRelationOption,
|
||||
private val addTagToDataViewRecord: AddTagToDataViewRecord,
|
||||
private val addStatusToDataViewRecord: AddStatusToDataViewRecord,
|
||||
private val urlBuilder: UrlBuilder,
|
||||
private val dispatcher: Dispatcher<Payload>,
|
||||
private val analytics: Analytics
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return RelationOptionValueDVAddViewModel(
|
||||
details = details,
|
||||
values = values,
|
||||
relations = relations,
|
||||
types = types,
|
||||
urlBuilder = urlBuilder,
|
||||
addDataViewRelationOption = addDataViewRelationOption,
|
||||
addTagToDataViewRecord = addTagToDataViewRecord,
|
||||
addStatusToDataViewRecord = addStatusToDataViewRecord,
|
||||
dispatcher = dispatcher,
|
||||
analytics = analytics
|
||||
) as T
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class RelationOptionValueAddViewModel(
|
||||
details: ObjectDetailProvider,
|
||||
types: ObjectTypesProvider,
|
||||
urlBuilder: UrlBuilder,
|
||||
values: ObjectValueProvider,
|
||||
relations: ObjectRelationProvider,
|
||||
private val addObjectRelationOption: AddObjectRelationOption,
|
||||
private val updateDetail: UpdateDetail,
|
||||
private val dispatcher: Dispatcher<Payload>,
|
||||
private val analytics: Analytics
|
||||
) : AddObjectRelationValueViewModel(
|
||||
details = details,
|
||||
values = values,
|
||||
types = types,
|
||||
urlBuilder = urlBuilder,
|
||||
relations = relations,
|
||||
analytics = analytics
|
||||
) {
|
||||
|
||||
fun onAddObjectStatusClicked(
|
||||
ctx: Id,
|
||||
relation: Id,
|
||||
status: RelationValueView.Status
|
||||
) = proceedWithAddingStatusToObject(
|
||||
ctx = ctx,
|
||||
relation = relation,
|
||||
status = status.id
|
||||
)
|
||||
|
||||
private fun proceedWithAddingStatusToObject(
|
||||
ctx: Id,
|
||||
relation: Id,
|
||||
status: Id
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
updateDetail(
|
||||
UpdateDetail.Params(
|
||||
ctx = ctx,
|
||||
key = relation,
|
||||
value = listOf(status)
|
||||
)
|
||||
).process(
|
||||
failure = { Timber.e(it, "Error while adding tag") },
|
||||
success = { dispatcher.send(it).also {
|
||||
sendAnalyticsRelationValueEvent(analytics)
|
||||
isParentDismissed.value = true
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun onAddSelectedValuesToObjectClicked(
|
||||
ctx: Id,
|
||||
obj: Id,
|
||||
relation: Id
|
||||
) {
|
||||
val tags = views.value.mapNotNull { view ->
|
||||
if (view is RelationValueView.Tag && view.isSelected == true)
|
||||
view.id
|
||||
else
|
||||
null
|
||||
}
|
||||
proceedWithAddingTagToObject(
|
||||
obj = obj,
|
||||
ctx = ctx,
|
||||
relation = relation,
|
||||
tags = tags
|
||||
)
|
||||
}
|
||||
|
||||
private fun proceedWithAddingTagToObject(
|
||||
obj: Id,
|
||||
ctx: Id,
|
||||
relation: Id,
|
||||
tags: List<Id>
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
val obj = values.get(target = obj)
|
||||
val result = mutableListOf<Id>()
|
||||
val value = obj[relation]
|
||||
if (value is List<*>) {
|
||||
result.addAll(value.typeOf())
|
||||
} else if (value is Id) {
|
||||
result.add(value)
|
||||
}
|
||||
result.addAll(tags)
|
||||
updateDetail(
|
||||
UpdateDetail.Params(
|
||||
ctx = ctx,
|
||||
key = relation,
|
||||
value = result
|
||||
)
|
||||
).process(
|
||||
failure = { Timber.e(it, "Error while adding tag") },
|
||||
success = { dispatcher.send(it).also {
|
||||
sendAnalyticsRelationValueEvent(analytics)
|
||||
isDismissed.value = true
|
||||
} }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun onCreateObjectRelationOptionClicked(
|
||||
ctx: Id,
|
||||
relation: Id,
|
||||
name: String,
|
||||
obj: Id
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
addObjectRelationOption(
|
||||
AddObjectRelationOption.Params(
|
||||
ctx = ctx,
|
||||
relation = relation,
|
||||
name = name,
|
||||
color = ThemeColor.values().filter { it != ThemeColor.DEFAULT }.random().title
|
||||
)
|
||||
).proceed(
|
||||
success = { (payload, option) ->
|
||||
dispatcher.send(payload)
|
||||
if (option != null) {
|
||||
when (val format = relations.get(relation).format) {
|
||||
Relation.Format.TAG -> {
|
||||
proceedWithAddingTagToObject(
|
||||
ctx = ctx,
|
||||
relation = relation,
|
||||
obj = obj,
|
||||
tags = listOf(option)
|
||||
)
|
||||
}
|
||||
Relation.Format.STATUS -> {
|
||||
proceedWithAddingStatusToObject(
|
||||
ctx = ctx,
|
||||
relation = relation,
|
||||
status = option
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
Timber.e("Trying to create an option for relation format: $format")
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
failure = { Timber.e(it, "Error while creating a new option for object") }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class Factory(
|
||||
private val values: ObjectValueProvider,
|
||||
private val details: ObjectDetailProvider,
|
||||
private val relations: ObjectRelationProvider,
|
||||
private val types: ObjectTypesProvider,
|
||||
private val addObjectRelationOption: AddObjectRelationOption,
|
||||
private val updateDetail: UpdateDetail,
|
||||
private val urlBuilder: UrlBuilder,
|
||||
private val dispatcher: Dispatcher<Payload>,
|
||||
private val analytics: Analytics
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return RelationOptionValueAddViewModel(
|
||||
details = details,
|
||||
values = values,
|
||||
relations = relations,
|
||||
types = types,
|
||||
urlBuilder = urlBuilder,
|
||||
addObjectRelationOption = addObjectRelationOption,
|
||||
updateDetail = updateDetail,
|
||||
dispatcher = dispatcher,
|
||||
analytics = analytics
|
||||
) as T
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,201 @@
|
|||
package com.anytypeio.anytype.presentation.relations
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.anytypeio.anytype.analytics.base.Analytics
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.Payload
|
||||
import com.anytypeio.anytype.core_models.Relation
|
||||
import com.anytypeio.anytype.core_utils.ext.typeOf
|
||||
import com.anytypeio.anytype.domain.`object`.ObjectTypesProvider
|
||||
import com.anytypeio.anytype.domain.`object`.UpdateDetail
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.domain.relations.AddObjectRelationOption
|
||||
import com.anytypeio.anytype.presentation.editor.editor.ThemeColor
|
||||
import com.anytypeio.anytype.presentation.extension.sendAnalyticsRelationValueEvent
|
||||
import com.anytypeio.anytype.presentation.relations.providers.ObjectDetailProvider
|
||||
import com.anytypeio.anytype.presentation.relations.providers.ObjectRelationProvider
|
||||
import com.anytypeio.anytype.presentation.relations.providers.ObjectValueProvider
|
||||
import com.anytypeio.anytype.presentation.sets.RelationValueBaseViewModel
|
||||
import com.anytypeio.anytype.presentation.util.Dispatcher
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
|
||||
class RelationOptionValueAddViewModel(
|
||||
details: ObjectDetailProvider,
|
||||
types: ObjectTypesProvider,
|
||||
urlBuilder: UrlBuilder,
|
||||
values: ObjectValueProvider,
|
||||
relations: ObjectRelationProvider,
|
||||
private val addObjectRelationOption: AddObjectRelationOption,
|
||||
private val updateDetail: UpdateDetail,
|
||||
private val dispatcher: Dispatcher<Payload>,
|
||||
private val analytics: Analytics
|
||||
) : AddObjectRelationValueViewModel(
|
||||
details = details,
|
||||
values = values,
|
||||
types = types,
|
||||
urlBuilder = urlBuilder,
|
||||
relations = relations,
|
||||
) {
|
||||
|
||||
fun onAddObjectStatusClicked(
|
||||
ctx: Id,
|
||||
relation: Id,
|
||||
status: RelationValueBaseViewModel.RelationValueView.Status
|
||||
) = proceedWithAddingStatusToObject(
|
||||
ctx = ctx,
|
||||
relation = relation,
|
||||
status = status.id
|
||||
)
|
||||
|
||||
private fun proceedWithAddingStatusToObject(
|
||||
ctx: Id,
|
||||
relation: Id,
|
||||
status: Id
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
updateDetail(
|
||||
UpdateDetail.Params(
|
||||
ctx = ctx,
|
||||
key = relation,
|
||||
value = listOf(status)
|
||||
)
|
||||
).process(
|
||||
failure = { Timber.e(it, "Error while adding tag") },
|
||||
success = {
|
||||
dispatcher.send(it).also {
|
||||
sendAnalyticsRelationValueEvent(analytics)
|
||||
isParentDismissed.value = true
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun onAddSelectedValuesToObjectClicked(
|
||||
ctx: Id,
|
||||
obj: Id,
|
||||
relation: Id
|
||||
) {
|
||||
val tags = views.value.mapNotNull { view ->
|
||||
if (view is RelationValueBaseViewModel.RelationValueView.Tag && view.isSelected == true)
|
||||
view.id
|
||||
else
|
||||
null
|
||||
}
|
||||
proceedWithAddingTagToObject(
|
||||
obj = obj,
|
||||
ctx = ctx,
|
||||
relation = relation,
|
||||
tags = tags
|
||||
)
|
||||
}
|
||||
|
||||
private fun proceedWithAddingTagToObject(
|
||||
obj: Id,
|
||||
ctx: Id,
|
||||
relation: Id,
|
||||
tags: List<Id>
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
val obj = values.get(target = obj)
|
||||
val result = mutableListOf<Id>()
|
||||
val value = obj[relation]
|
||||
if (value is List<*>) {
|
||||
result.addAll(value.typeOf())
|
||||
} else if (value is Id) {
|
||||
result.add(value)
|
||||
}
|
||||
result.addAll(tags)
|
||||
updateDetail(
|
||||
UpdateDetail.Params(
|
||||
ctx = ctx,
|
||||
key = relation,
|
||||
value = result
|
||||
)
|
||||
).process(
|
||||
failure = { Timber.e(it, "Error while adding tag") },
|
||||
success = {
|
||||
dispatcher.send(it).also {
|
||||
sendAnalyticsRelationValueEvent(analytics)
|
||||
isDismissed.value = true
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun onCreateObjectRelationOptionClicked(
|
||||
ctx: Id,
|
||||
relation: Id,
|
||||
name: String,
|
||||
obj: Id
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
addObjectRelationOption(
|
||||
AddObjectRelationOption.Params(
|
||||
ctx = ctx,
|
||||
relation = relation,
|
||||
name = name,
|
||||
color = ThemeColor.values().filter { it != ThemeColor.DEFAULT }.random().title
|
||||
)
|
||||
).proceed(
|
||||
success = { (payload, option) ->
|
||||
dispatcher.send(payload)
|
||||
if (option != null) {
|
||||
when (val format = relations.get(relation).format) {
|
||||
Relation.Format.TAG -> {
|
||||
proceedWithAddingTagToObject(
|
||||
ctx = ctx,
|
||||
relation = relation,
|
||||
obj = obj,
|
||||
tags = listOf(option)
|
||||
)
|
||||
}
|
||||
Relation.Format.STATUS -> {
|
||||
proceedWithAddingStatusToObject(
|
||||
ctx = ctx,
|
||||
relation = relation,
|
||||
status = option
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
Timber.e("Trying to create an option for relation format: $format")
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
failure = { Timber.e(it, "Error while creating a new option for object") }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class Factory(
|
||||
private val values: ObjectValueProvider,
|
||||
private val details: ObjectDetailProvider,
|
||||
private val relations: ObjectRelationProvider,
|
||||
private val types: ObjectTypesProvider,
|
||||
private val addObjectRelationOption: AddObjectRelationOption,
|
||||
private val updateDetail: UpdateDetail,
|
||||
private val urlBuilder: UrlBuilder,
|
||||
private val dispatcher: Dispatcher<Payload>,
|
||||
private val analytics: Analytics
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return RelationOptionValueAddViewModel(
|
||||
details = details,
|
||||
values = values,
|
||||
relations = relations,
|
||||
types = types,
|
||||
urlBuilder = urlBuilder,
|
||||
addObjectRelationOption = addObjectRelationOption,
|
||||
updateDetail = updateDetail,
|
||||
dispatcher = dispatcher,
|
||||
analytics = analytics
|
||||
) as T
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,206 @@
|
|||
package com.anytypeio.anytype.presentation.relations
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.Payload
|
||||
import com.anytypeio.anytype.core_models.Relation
|
||||
import com.anytypeio.anytype.domain.`object`.ObjectTypesProvider
|
||||
import com.anytypeio.anytype.domain.dataview.interactor.AddDataViewRelationOption
|
||||
import com.anytypeio.anytype.domain.dataview.interactor.AddStatusToDataViewRecord
|
||||
import com.anytypeio.anytype.domain.dataview.interactor.AddTagToDataViewRecord
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.presentation.editor.editor.ThemeColor
|
||||
import com.anytypeio.anytype.presentation.relations.providers.ObjectDetailProvider
|
||||
import com.anytypeio.anytype.presentation.relations.providers.ObjectRelationProvider
|
||||
import com.anytypeio.anytype.presentation.relations.providers.ObjectValueProvider
|
||||
import com.anytypeio.anytype.presentation.sets.RelationValueBaseViewModel
|
||||
import com.anytypeio.anytype.presentation.util.Dispatcher
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
|
||||
class RelationOptionValueDVAddViewModel(
|
||||
details: ObjectDetailProvider,
|
||||
types: ObjectTypesProvider,
|
||||
urlBuilder: UrlBuilder,
|
||||
values: ObjectValueProvider,
|
||||
relations: ObjectRelationProvider,
|
||||
private val addDataViewRelationOption: AddDataViewRelationOption,
|
||||
private val addTagToDataViewRecord: AddTagToDataViewRecord,
|
||||
private val addStatusToDataViewRecord: AddStatusToDataViewRecord,
|
||||
private val dispatcher: Dispatcher<Payload>,
|
||||
) : AddObjectRelationValueViewModel(
|
||||
details = details,
|
||||
values = values,
|
||||
types = types,
|
||||
urlBuilder = urlBuilder,
|
||||
relations = relations,
|
||||
) {
|
||||
|
||||
fun onCreateDataViewRelationOptionClicked(
|
||||
ctx: Id,
|
||||
dataview: Id,
|
||||
viewer: Id,
|
||||
relation: Id,
|
||||
target: Id,
|
||||
name: String
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
addDataViewRelationOption(
|
||||
AddDataViewRelationOption.Params(
|
||||
ctx = ctx,
|
||||
relation = relation,
|
||||
dataview = dataview,
|
||||
record = target,
|
||||
name = name,
|
||||
color = ThemeColor.values().filter { it != ThemeColor.DEFAULT }.random().title
|
||||
)
|
||||
).proceed(
|
||||
success = { (payload, option) ->
|
||||
dispatcher.send(payload)
|
||||
if (option != null) {
|
||||
when (relations.get(relation).format) {
|
||||
Relation.Format.TAG -> {
|
||||
proceedWithAddingTagToDataViewRecord(
|
||||
ctx = ctx,
|
||||
dataview = dataview,
|
||||
viewer = viewer,
|
||||
relation = relation,
|
||||
target = target,
|
||||
tags = listOf(option)
|
||||
)
|
||||
}
|
||||
Relation.Format.STATUS -> {
|
||||
proceedWithAddingStatusToDataViewRecord(
|
||||
ctx = ctx,
|
||||
dataview = dataview,
|
||||
viewer = viewer,
|
||||
relation = relation,
|
||||
obj = target,
|
||||
status = option
|
||||
)
|
||||
}
|
||||
else -> Timber.e("Trying to create option for wrong relation.")
|
||||
}
|
||||
}
|
||||
},
|
||||
failure = { Timber.e(it, "Error while creating a new option") }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun onAddObjectSetStatusClicked(
|
||||
ctx: Id,
|
||||
dataview: Id,
|
||||
viewer: Id,
|
||||
relation: Id,
|
||||
obj: Id,
|
||||
status: RelationValueBaseViewModel.RelationValueView.Status
|
||||
) {
|
||||
proceedWithAddingStatusToDataViewRecord(ctx, dataview, viewer, relation, obj, status.id)
|
||||
}
|
||||
|
||||
private fun proceedWithAddingStatusToDataViewRecord(
|
||||
ctx: Id,
|
||||
dataview: Id,
|
||||
viewer: Id,
|
||||
relation: Id,
|
||||
obj: Id,
|
||||
status: Id
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
addStatusToDataViewRecord(
|
||||
AddStatusToDataViewRecord.Params(
|
||||
ctx = ctx,
|
||||
dataview = dataview,
|
||||
viewer = viewer,
|
||||
relation = relation,
|
||||
obj = obj,
|
||||
status = status,
|
||||
record = values.get(target = obj)
|
||||
)
|
||||
).process(
|
||||
failure = { Timber.e(it, "Error while adding tag") },
|
||||
success = { isParentDismissed.value = true }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun onAddSelectedValuesToDataViewClicked(
|
||||
ctx: Id,
|
||||
dataview: Id,
|
||||
target: Id,
|
||||
relation: Id,
|
||||
viewer: Id
|
||||
) {
|
||||
val tags = views.value.mapNotNull { view ->
|
||||
if (view is RelationValueBaseViewModel.RelationValueView.Tag && view.isSelected == true)
|
||||
view.id
|
||||
else
|
||||
null
|
||||
}
|
||||
proceedWithAddingTagToDataViewRecord(
|
||||
target = target,
|
||||
ctx = ctx,
|
||||
dataview = dataview,
|
||||
relation = relation,
|
||||
viewer = viewer,
|
||||
tags = tags
|
||||
)
|
||||
}
|
||||
|
||||
private fun proceedWithAddingTagToDataViewRecord(
|
||||
target: Id,
|
||||
ctx: Id,
|
||||
dataview: Id,
|
||||
relation: Id,
|
||||
viewer: Id,
|
||||
tags: List<Id>
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
val record = values.get(target = target)
|
||||
addTagToDataViewRecord(
|
||||
AddTagToDataViewRecord.Params(
|
||||
ctx = ctx,
|
||||
tags = tags,
|
||||
record = record,
|
||||
dataview = dataview,
|
||||
relation = relation,
|
||||
viewer = viewer,
|
||||
target = target
|
||||
)
|
||||
).process(
|
||||
failure = { Timber.e(it, "Error while adding tag") },
|
||||
success = { isDismissed.value = true }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class Factory(
|
||||
private val values: ObjectValueProvider,
|
||||
private val details: ObjectDetailProvider,
|
||||
private val relations: ObjectRelationProvider,
|
||||
private val types: ObjectTypesProvider,
|
||||
private val addDataViewRelationOption: AddDataViewRelationOption,
|
||||
private val addTagToDataViewRecord: AddTagToDataViewRecord,
|
||||
private val addStatusToDataViewRecord: AddStatusToDataViewRecord,
|
||||
private val urlBuilder: UrlBuilder,
|
||||
private val dispatcher: Dispatcher<Payload>,
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return RelationOptionValueDVAddViewModel(
|
||||
details = details,
|
||||
values = values,
|
||||
relations = relations,
|
||||
types = types,
|
||||
urlBuilder = urlBuilder,
|
||||
addDataViewRelationOption = addDataViewRelationOption,
|
||||
addTagToDataViewRecord = addTagToDataViewRecord,
|
||||
addStatusToDataViewRecord = addStatusToDataViewRecord,
|
||||
dispatcher = dispatcher,
|
||||
) as T
|
||||
}
|
||||
}
|
||||
}
|
5
presentation/src/test/java/FakeGateWay.kt
Normal file
5
presentation/src/test/java/FakeGateWay.kt
Normal file
|
@ -0,0 +1,5 @@
|
|||
import com.anytypeio.anytype.domain.config.Gateway
|
||||
|
||||
object FakeGateWay : Gateway {
|
||||
override fun obtain(): String = "anytype.io"
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package com.anytypeio.anytype.presentation.extension
|
||||
|
||||
import FakeGateWay
|
||||
import MockDataFactory
|
||||
import com.anytypeio.anytype.core_models.Block
|
||||
import com.anytypeio.anytype.domain.config.Gateway
|
||||
|
@ -46,14 +47,7 @@ class DashboardViewExtensionKtTest {
|
|||
)
|
||||
)
|
||||
|
||||
|
||||
val testGate = object : Gateway {
|
||||
override fun obtain(): String {
|
||||
return "anytype.io"
|
||||
}
|
||||
}
|
||||
|
||||
val builder = UrlBuilder(gateway = testGate)
|
||||
val builder = UrlBuilder(FakeGateWay)
|
||||
|
||||
val result = views.updateDetails(
|
||||
target = views[1].target,
|
||||
|
|
|
@ -0,0 +1,152 @@
|
|||
package com.anytypeio.anytype.presentation.relations
|
||||
|
||||
import FakeGateWay
|
||||
import app.cash.turbine.test
|
||||
import com.anytypeio.anytype.core_models.Relation
|
||||
import com.anytypeio.anytype.core_models.StubRelation
|
||||
import com.anytypeio.anytype.core_models.StubRelationOption
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.presentation.relations.providers.FakeObjectDetailsProvider
|
||||
import com.anytypeio.anytype.presentation.relations.providers.FakeObjectRelationProvider
|
||||
import com.anytypeio.anytype.presentation.relations.providers.FakeObjectTypesProvider
|
||||
import com.anytypeio.anytype.presentation.relations.providers.FakeObjectValueProvider
|
||||
import com.anytypeio.anytype.presentation.sets.RelationValueBaseViewModel.RelationValueView
|
||||
import com.anytypeio.anytype.presentation.util.CoroutinesTestRule
|
||||
import com.anytypeio.anytype.test_utils.MockDataFactory
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import kotlin.test.BeforeTest
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
class AddObjectRelationValueViewModelTest {
|
||||
|
||||
@get:Rule
|
||||
internal val coroutineTestRule = CoroutinesTestRule()
|
||||
|
||||
private val option = StubRelationOption()
|
||||
private val relation = StubRelation(
|
||||
format = Relation.Format.TAG,
|
||||
selections = listOf(option)
|
||||
)
|
||||
|
||||
private val relationsProvider = FakeObjectRelationProvider
|
||||
|
||||
@Test
|
||||
fun `query is empty - there is only tag view`() {
|
||||
val viewModel = createViewModel()
|
||||
relationsProvider.relation = relation
|
||||
|
||||
runTest {
|
||||
viewModel.ui.test {
|
||||
viewModel.onStart("", "")
|
||||
// first item is emmit in constructor
|
||||
val actual = listOf(awaitItem(), awaitItem())[1]
|
||||
assertEquals(
|
||||
expected = listOf(
|
||||
RelationValueView.Tag(
|
||||
id = option.id,
|
||||
name = option.text,
|
||||
color = option.color,
|
||||
isSelected = false
|
||||
)
|
||||
),
|
||||
actual = actual
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `query doesn't contain tag - there is only create view`() {
|
||||
val viewModel = createViewModel()
|
||||
relationsProvider.relation = relation
|
||||
|
||||
runTest {
|
||||
viewModel.ui.test {
|
||||
viewModel.onStart("", "")
|
||||
val query = MockDataFactory.randomString() + option.text
|
||||
viewModel.onFilterInputChanged(query)
|
||||
// first item is emmit in constructor, second - at onStart
|
||||
val actual = listOf(awaitItem(), awaitItem(), awaitItem())[2]
|
||||
assertEquals(
|
||||
expected = listOf(
|
||||
RelationValueView.Create(query),
|
||||
),
|
||||
actual = actual
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `query contains tag parts but doesn't equal - there are create and tag views`() {
|
||||
val viewModel = createViewModel()
|
||||
relationsProvider.relation = relation
|
||||
|
||||
runTest {
|
||||
viewModel.ui.test {
|
||||
viewModel.onStart("", "")
|
||||
|
||||
val query = option.text.substring(1)
|
||||
viewModel.onFilterInputChanged(query)
|
||||
// first item is emmit in constructor, second - at onStart
|
||||
val actual = listOf(awaitItem(), awaitItem(), awaitItem())[2]
|
||||
assertEquals(
|
||||
expected = listOf(
|
||||
RelationValueView.Create(query),
|
||||
RelationValueView.Tag(
|
||||
id = option.id,
|
||||
name = option.text,
|
||||
color = option.color,
|
||||
isSelected = false
|
||||
)
|
||||
),
|
||||
actual = actual
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `query equals tag - there is only tag view`() {
|
||||
val viewModel = createViewModel()
|
||||
relationsProvider.relation = relation
|
||||
|
||||
runTest {
|
||||
viewModel.ui.test {
|
||||
viewModel.onStart("", "")
|
||||
// first item is emmit in constructor, second - at onStart
|
||||
val actual = listOf(awaitItem(), awaitItem())[1]
|
||||
assertEquals(
|
||||
expected = listOf(
|
||||
RelationValueView.Tag(
|
||||
id = option.id,
|
||||
name = option.text,
|
||||
color = option.color,
|
||||
isSelected = false
|
||||
)
|
||||
),
|
||||
actual = actual
|
||||
)
|
||||
|
||||
val query = option.text
|
||||
viewModel.onFilterInputChanged(query)
|
||||
|
||||
// because `ui` has distinct under the hood
|
||||
expectNoEvents()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createViewModel(): AddObjectRelationValueViewModel = object :
|
||||
AddObjectRelationValueViewModel(
|
||||
FakeObjectValueProvider,
|
||||
FakeObjectDetailsProvider,
|
||||
relationsProvider,
|
||||
FakeObjectTypesProvider,
|
||||
UrlBuilder(FakeGateWay)
|
||||
) {}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package com.anytypeio.anytype.presentation.relations.providers
|
||||
|
||||
import com.anytypeio.anytype.core_models.Block
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.presentation.relations.providers.ObjectDetailProvider
|
||||
|
||||
internal object FakeObjectDetailsProvider : ObjectDetailProvider {
|
||||
override fun provide(): Map<Id, Block.Fields> {
|
||||
return emptyMap()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package com.anytypeio.anytype.presentation.relations.providers
|
||||
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.Relation
|
||||
import com.anytypeio.anytype.core_models.StubRelation
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
|
||||
internal object FakeObjectRelationProvider : ObjectRelationProvider {
|
||||
internal var relation: Relation = StubRelation()
|
||||
|
||||
override fun get(relation: Id): Relation {
|
||||
return this.relation
|
||||
}
|
||||
|
||||
override fun subscribe(relationId: Id): Flow<Relation> {
|
||||
return flow {
|
||||
emit(relation)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package com.anytypeio.anytype.presentation.relations.providers
|
||||
|
||||
import com.anytypeio.anytype.core_models.ObjectType
|
||||
import com.anytypeio.anytype.domain.`object`.ObjectTypesProvider
|
||||
|
||||
internal object FakeObjectTypesProvider : ObjectTypesProvider {
|
||||
internal var list = emptyList<ObjectType>()
|
||||
|
||||
override fun get(): List<ObjectType> = list
|
||||
|
||||
override fun set(objectTypes: List<ObjectType>) {
|
||||
this.list = objectTypes
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package com.anytypeio.anytype.presentation.relations.providers
|
||||
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
|
||||
internal object FakeObjectValueProvider : ObjectValueProvider {
|
||||
|
||||
override fun get(target: Id): Map<String, Any?> = emptyMap()
|
||||
|
||||
override fun subscribe(target: Id): Flow<Map<String, Any?>> =
|
||||
flow { emit(emptyMap()) }
|
||||
}
|
|
@ -15,5 +15,8 @@ include ':app',
|
|||
':analytics',
|
||||
':protocol',
|
||||
':core-models',
|
||||
':test-utils'
|
||||
include ':ui-settings'
|
||||
':test:android-utils',
|
||||
':test:utils',
|
||||
':test:core-models-stub'
|
||||
|
||||
include ':ui-settings'
|
1
test-utils/.gitignore
vendored
1
test-utils/.gitignore
vendored
|
@ -1 +0,0 @@
|
|||
/build
|
|
@ -1,35 +0,0 @@
|
|||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
android {
|
||||
def config = rootProject.extensions.getByName("ext")
|
||||
|
||||
compileSdkVersion config["compile_sdk"]
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion config["min_sdk"]
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = JavaVersion.VERSION_11
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
def applicationDependencies = rootProject.ext.mainApplication
|
||||
def acceptanceTesting = rootProject.ext.acceptanceTesting
|
||||
|
||||
implementation applicationDependencies.appcompat
|
||||
implementation applicationDependencies.kotlin
|
||||
implementation applicationDependencies.coroutinesAndroid
|
||||
implementation applicationDependencies.androidxCore
|
||||
implementation acceptanceTesting.espressoCore
|
||||
|
||||
implementation applicationDependencies.design
|
||||
implementation applicationDependencies.recyclerView
|
||||
}
|
30
test/android-utils/build.gradle
Normal file
30
test/android-utils/build.gradle
Normal file
|
@ -0,0 +1,30 @@
|
|||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
android {
|
||||
compileSdkVersion compile_sdk
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion min_sdk
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = JavaVersion.VERSION_11
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation mainApplication.appcompat
|
||||
implementation mainApplication.kotlin
|
||||
implementation mainApplication.coroutinesAndroid
|
||||
implementation mainApplication.androidxCore
|
||||
implementation acceptanceTesting.espressoCore
|
||||
|
||||
implementation mainApplication.design
|
||||
implementation mainApplication.recyclerView
|
||||
}
|
|
@ -75,11 +75,12 @@ fun ViewInteraction.checkHasChildViewCount(count: Int) : ViewInteraction {
|
|||
return check(matches(WithChildViewCount(count)))
|
||||
}
|
||||
|
||||
fun Int.rVMatcher(): RecyclerViewMatcher = RecyclerViewMatcher(this)
|
||||
fun Int.rVMatcher(): RecyclerViewMatcher =
|
||||
RecyclerViewMatcher(this)
|
||||
|
||||
fun Int.checkRecyclerItemCount(expected: Int) = matchView().check(RecyclerViewItemCountAssertion(expected))
|
||||
|
||||
fun RecyclerViewMatcher.onItemView(pos: Int,target: Int): ViewInteraction {
|
||||
fun RecyclerViewMatcher.onItemView(pos: Int, target: Int): ViewInteraction {
|
||||
return onView(atPositionOnView(pos, target))
|
||||
}
|
||||
|
8
test/core-models-stub/build.gradle
Normal file
8
test/core-models-stub/build.gradle
Normal file
|
@ -0,0 +1,8 @@
|
|||
plugins {
|
||||
id "kotlin"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api project(':test:utils')
|
||||
api project(":core-models")
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package com.anytypeio.anytype.core_models
|
||||
|
||||
import com.anytypeio.anytype.test_utils.MockDataFactory
|
||||
|
||||
fun StubRelation(
|
||||
key: String = MockDataFactory.randomString(),
|
||||
name: String = MockDataFactory.randomString(),
|
||||
format: Relation.Format = Relation.Format.SHORT_TEXT,
|
||||
source: Relation.Source = Relation.Source.LOCAL,
|
||||
isHidden: Boolean = false,
|
||||
isReadOnly: Boolean = false,
|
||||
isMulti: Boolean = false,
|
||||
selections: List<Relation.Option> = emptyList(),
|
||||
objectTypes: List<String> = emptyList(),
|
||||
defaultValue: Any? = null
|
||||
): Relation = Relation(
|
||||
key,
|
||||
name,
|
||||
format,
|
||||
source,
|
||||
isHidden,
|
||||
isReadOnly,
|
||||
isMulti,
|
||||
selections,
|
||||
objectTypes,
|
||||
defaultValue
|
||||
)
|
||||
|
||||
fun StubRelationOption(
|
||||
id: String = MockDataFactory.randomUuid(),
|
||||
text: String = MockDataFactory.randomString(),
|
||||
color: String = MockDataFactory.randomString(),
|
||||
scope: Relation.OptionScope = Relation.OptionScope.LOCAL
|
||||
): Relation.Option =
|
||||
Relation.Option(
|
||||
id, text, color, scope
|
||||
)
|
7
test/utils/build.gradle
Normal file
7
test/utils/build.gradle
Normal file
|
@ -0,0 +1,7 @@
|
|||
plugins {
|
||||
id "kotlin"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation mainApplication.kotlin
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue