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

Editor | Enhancement | Add plus button action to editor (#2353)

* Editor | Enhancement | Add plus button action to editor
This commit is contained in:
Mikhail 2022-06-28 17:32:37 +03:00 committed by GitHub
parent c5198bae5b
commit de8161bb3f
Signed by: github
GPG key ID: 4AEE18F83AFDEB23
51 changed files with 731 additions and 436 deletions

View file

@ -28,6 +28,7 @@ object EventsDictionary {
const val authScreenShow = "ScreenIndex"
const val loginScreenShow = "ScreenLogin"
const val searchScreenShow = "ScreenSearch"
const val createObjectNavBar = "CreateObjectNavBar"
const val signupScreenShow = "ScreenAuthRegistration"
const val invitationScreenShow = "ScreenAuthInvitation"
const val aboutAnalyticsScreenShow = "ScreenDisclaimer"

View file

@ -76,6 +76,7 @@ import com.anytypeio.anytype.domain.templates.GetTemplates
import com.anytypeio.anytype.domain.unsplash.DownloadUnsplashImage
import com.anytypeio.anytype.domain.unsplash.UnsplashRepository
import com.anytypeio.anytype.presentation.common.Delegator
import com.anytypeio.anytype.domain.page.CreateNewObject
import com.anytypeio.anytype.presentation.editor.DocumentExternalEventReducer
import com.anytypeio.anytype.presentation.editor.Editor
import com.anytypeio.anytype.presentation.editor.EditorViewModelFactory
@ -162,6 +163,9 @@ open class EditorTestSetup {
@Mock
lateinit var mergeBlocks: MergeBlocks
@Mock
lateinit var createNewObject: CreateNewObject
lateinit var editorTemplateDelegate: EditorTemplateDelegate
lateinit var getTemplates: GetTemplates
lateinit var applyTemplate: ApplyTemplate
@ -383,7 +387,8 @@ open class EditorTestSetup {
delegator = Delegator.Default(),
setDocCoverImage = setDocCoverImage,
setDocImageIcon = setDocImageIcon,
editorTemplateDelegate = editorTemplateDelegate
editorTemplateDelegate = editorTemplateDelegate,
createNewObject = createNewObject
)
}

View file

@ -36,6 +36,7 @@ import com.anytypeio.anytype.domain.unsplash.UnsplashRepository
import com.anytypeio.anytype.emojifier.data.DefaultDocumentEmojiIconProvider
import com.anytypeio.anytype.presentation.common.Action
import com.anytypeio.anytype.presentation.common.Delegator
import com.anytypeio.anytype.domain.page.CreateNewObject
import com.anytypeio.anytype.presentation.sets.ObjectSetRecordCache
import com.anytypeio.anytype.presentation.sets.ObjectSetReducer
import com.anytypeio.anytype.presentation.sets.ObjectSetSession
@ -84,6 +85,9 @@ abstract class TestObjectSetSetup {
@Mock
lateinit var analytics: Analytics
@Mock
lateinit var createNewObject: CreateNewObject
lateinit var getTemplates: GetTemplates
private val session = ObjectSetSession()
@ -160,7 +164,8 @@ abstract class TestObjectSetSetup {
downloadUnsplashImage = downloadUnsplashImage,
setDocCoverImage = setDocCoverImage,
delegator = delegator,
getTemplates = getTemplates
getTemplates = getTemplates,
createNewObject = createNewObject
)
}

View file

@ -9,7 +9,6 @@ import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
import com.anytypeio.anytype.domain.block.interactor.Move
import com.anytypeio.anytype.domain.block.repo.BlockRepository
import com.anytypeio.anytype.domain.config.ConfigStorage
import com.anytypeio.anytype.domain.config.FeaturesConfigProvider
import com.anytypeio.anytype.domain.config.GetConfig
import com.anytypeio.anytype.domain.config.GetDebugSettings
import com.anytypeio.anytype.domain.config.InfrastructureRepository
@ -29,6 +28,7 @@ import com.anytypeio.anytype.domain.search.CancelSearchSubscription
import com.anytypeio.anytype.domain.search.ObjectSearchSubscriptionContainer
import com.anytypeio.anytype.domain.search.SubscriptionEventChannel
import com.anytypeio.anytype.domain.templates.GetTemplates
import com.anytypeio.anytype.domain.page.CreateNewObject
import com.anytypeio.anytype.presentation.dashboard.HomeDashboardEventConverter
import com.anytypeio.anytype.presentation.dashboard.HomeDashboardViewModelFactory
import com.anytypeio.anytype.ui.dashboard.DashboardFragment
@ -62,7 +62,6 @@ object HomeDashboardModule {
fun provideDesktopViewModelFactory(
getProfile: GetProfile,
openDashboard: OpenDashboard,
createPage: CreatePage,
closeDashboard: CloseDashboard,
getConfig: GetConfig,
move: Move,
@ -71,19 +70,16 @@ object HomeDashboardModule {
getDebugSettings: GetDebugSettings,
analytics: Analytics,
searchObjects: SearchObjects,
getDefaultEditorType: GetDefaultEditorType,
urlBuilder: UrlBuilder,
setObjectListIsArchived: SetObjectListIsArchived,
deleteObjects: DeleteObjects,
featuresConfigProvider: FeaturesConfigProvider,
objectSearchSubscriptionContainer: ObjectSearchSubscriptionContainer,
cancelSearchSubscription: CancelSearchSubscription,
objectStore: ObjectStore,
getTemplates: GetTemplates
createNewObject: CreateNewObject
): HomeDashboardViewModelFactory = HomeDashboardViewModelFactory(
getProfile = getProfile,
openDashboard = openDashboard,
createPage = createPage,
closeDashboard = closeDashboard,
getConfig = getConfig,
move = move,
@ -95,12 +91,23 @@ object HomeDashboardModule {
urlBuilder = urlBuilder,
setObjectListIsArchived = setObjectListIsArchived,
deleteObjects = deleteObjects,
getDefaultEditorType = getDefaultEditorType,
featuresConfigProvider = featuresConfigProvider,
objectSearchSubscriptionContainer = objectSearchSubscriptionContainer,
cancelSearchSubscription = cancelSearchSubscription,
objectStore = objectStore,
getTemplates = getTemplates
createNewObject = createNewObject
)
@JvmStatic
@Provides
@PerScreen
fun provideCreateNewObject(
getDefaultEditorType: GetDefaultEditorType,
getTemplates: GetTemplates,
createPage: CreatePage,
) : CreateNewObject = CreateNewObject(
getDefaultEditorType,
getTemplates,
createPage
)
@JvmStatic

View file

@ -80,6 +80,7 @@ import com.anytypeio.anytype.domain.unsplash.DownloadUnsplashImage
import com.anytypeio.anytype.domain.unsplash.UnsplashRepository
import com.anytypeio.anytype.presentation.common.Action
import com.anytypeio.anytype.presentation.common.Delegator
import com.anytypeio.anytype.domain.page.CreateNewObject
import com.anytypeio.anytype.presentation.editor.DocumentExternalEventReducer
import com.anytypeio.anytype.presentation.editor.Editor
import com.anytypeio.anytype.presentation.editor.EditorViewModelFactory
@ -203,7 +204,8 @@ object EditorSessionModule {
downloadUnsplashImage: DownloadUnsplashImage,
setDocCoverImage: SetDocCoverImage,
setDocImageIcon: SetDocumentImageIcon,
editorTemplateDelegate: EditorTemplateDelegate
editorTemplateDelegate: EditorTemplateDelegate,
createNewObject: CreateNewObject
): EditorViewModelFactory = EditorViewModelFactory(
openPage = openPage,
closeObject = closePage,
@ -234,7 +236,21 @@ object EditorSessionModule {
downloadUnsplashImage = downloadUnsplashImage,
setDocCoverImage = setDocCoverImage,
setDocImageIcon = setDocImageIcon,
editorTemplateDelegate = editorTemplateDelegate
editorTemplateDelegate = editorTemplateDelegate,
createNewObject = createNewObject
)
@JvmStatic
@Provides
@PerScreen
fun provideCreateNewObject(
getDefaultEditorType: GetDefaultEditorType,
getTemplates: GetTemplates,
createPage: CreatePage,
) : CreateNewObject = CreateNewObject(
getDefaultEditorType,
getTemplates,
createPage
)
@JvmStatic

View file

@ -19,6 +19,7 @@ import com.anytypeio.anytype.domain.auth.repo.AuthRepository
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
import com.anytypeio.anytype.domain.block.interactor.UpdateText
import com.anytypeio.anytype.domain.block.repo.BlockRepository
import com.anytypeio.anytype.domain.config.UserSettingsRepository
import com.anytypeio.anytype.domain.cover.SetDocCoverImage
import com.anytypeio.anytype.domain.dataview.interactor.AddNewRelationToDataView
import com.anytypeio.anytype.domain.dataview.interactor.CreateDataViewRecord
@ -29,9 +30,11 @@ import com.anytypeio.anytype.domain.dataview.interactor.UpdateDataViewViewer
import com.anytypeio.anytype.domain.event.interactor.EventChannel
import com.anytypeio.anytype.domain.event.interactor.InterceptEvents
import com.anytypeio.anytype.domain.icon.SetDocumentImageIcon
import com.anytypeio.anytype.domain.launch.GetDefaultEditorType
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.objects.SetObjectIsArchived
import com.anytypeio.anytype.domain.page.CloseBlock
import com.anytypeio.anytype.domain.page.CreatePage
import com.anytypeio.anytype.domain.relations.AddFileToRecord
import com.anytypeio.anytype.domain.relations.DeleteRelationFromDataView
import com.anytypeio.anytype.domain.sets.OpenObjectSet
@ -42,6 +45,7 @@ import com.anytypeio.anytype.domain.unsplash.DownloadUnsplashImage
import com.anytypeio.anytype.domain.unsplash.UnsplashRepository
import com.anytypeio.anytype.presentation.common.Action
import com.anytypeio.anytype.presentation.common.Delegator
import com.anytypeio.anytype.domain.page.CreateNewObject
import com.anytypeio.anytype.presentation.relations.providers.DataViewObjectRelationProvider
import com.anytypeio.anytype.presentation.relations.providers.DataViewObjectValueProvider
import com.anytypeio.anytype.presentation.relations.providers.ObjectDetailProvider
@ -135,7 +139,8 @@ object ObjectSetModule {
analytics: Analytics,
downloadUnsplashImage: DownloadUnsplashImage,
setDocCoverImage: SetDocCoverImage,
getTemplates: GetTemplates
getTemplates: GetTemplates,
createNewObject: CreateNewObject
): ObjectSetViewModelFactory = ObjectSetViewModelFactory(
openObjectSet = openObjectSet,
closeBlock = closeBlock,
@ -156,9 +161,38 @@ object ObjectSetModule {
analytics = analytics,
downloadUnsplashImage = downloadUnsplashImage,
setDocCoverImage = setDocCoverImage,
getTemplates = getTemplates
getTemplates = getTemplates,
createNewObject = createNewObject
)
@JvmStatic
@Provides
@PerScreen
fun provideCreateNewObject(
getDefaultEditorType: GetDefaultEditorType,
getTemplates: GetTemplates,
createPage: CreatePage,
) : CreateNewObject = CreateNewObject(
getDefaultEditorType,
getTemplates,
createPage
)
@JvmStatic
@Provides
@PerScreen
fun provideCreatePageUseCase(
repo: BlockRepository
): CreatePage = CreatePage(
repo = repo
)
@JvmStatic
@Provides
@PerScreen
fun provideGetDefaultPageType(repo: UserSettingsRepository): GetDefaultEditorType =
GetDefaultEditorType(repo)
@JvmStatic
@Provides
@PerScreen

View file

@ -47,6 +47,11 @@ class CreateObjectFragment : BaseFragment<FragmentCreateObjectBinding>(R.layout.
vm.onStart(mType)
}
override fun onStop() {
vm.onStop()
super.onStop()
}
override fun injectDependencies() {
componentManager().createObjectComponent.get().inject(this)
}

View file

@ -595,6 +595,11 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
.onEach { vm.onPageSearchClicked() }
.launchIn(lifecycleScope)
binding.bottomToolbar
.addDocClicks()
.onEach { vm.onAddNewDocumentClicked() }
.launchIn(lifecycleScope)
binding.topToolbar.menu
.clicks()
.onEach { vm.onDocumentMenuClicked() }

View file

@ -212,6 +212,7 @@ open class ObjectSetFragment :
subscribe(binding.bottomToolbar.homeClicks()) { vm.onHomeButtonClicked() }
subscribe(binding.bottomToolbar.backClicks()) { vm.onBackButtonClicked() }
subscribe(binding.bottomToolbar.searchClicks()) { vm.onSearchButtonClicked() }
subscribe(binding.bottomToolbar.addDocClicks()) { vm.onAddNewDocumentClicked() }
}
with(binding.paginatorToolbar) {

View file

@ -22,6 +22,7 @@ class MainBottomToolbar @JvmOverloads constructor(
}
fun searchClicks() = binding.btnSearch.clicks()
fun addDocClicks() = binding.btnAddDoc.clicks()
fun homeClicks() = binding.btnHome.clicks()
fun backClicks() = binding.btnBack.clicks()
}

View file

@ -53,4 +53,21 @@
</FrameLayout>
<FrameLayout
android:id="@+id/btnAddDoc"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/default_ripple">
<ImageView
android:id="@+id/icAddDoc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:contentDescription="@string/content_desc_add_doc_button"
app:srcCompat="@drawable/ic_page_toolbar_add_doc" />
</FrameLayout>
</merge>

View file

@ -445,6 +445,7 @@
<string name="content_desc_back_button">Back button</string>
<string name="content_desc_home_button">Home button</string>
<string name="content_desc_search_button">Search button</string>
<string name="content_desc_add_doc_button">Add doc</string>
<string name="unsupported_block">Unsupported block</string>
<string name="new_relation">New relation</string>
<string name="change_type">Change type</string>

View file

@ -15,6 +15,7 @@ dependencies {
testImplementation project(":test:utils")
testImplementation unitTestDependencies.kotlinTest
testImplementation unitTestDependencies.turbine
testImplementation unitTestDependencies.mockitoKotlin
testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0"
testImplementation unitTestDependencies.coroutineTesting

View file

@ -54,11 +54,7 @@ abstract class Interactor<in P>(
abstract class ResultInteractor<in P, R> {
operator fun invoke(params: P): Flow<R> = flow { emit(doWork(params)) }
suspend fun run(params: P) = doWork(params)
suspend fun execute(params: P): Result<R> = try {
Result.success(doWork(params))
} catch (e: Exception) {
Result.failure(e)
}
suspend fun execute(params: P): Result<R> = runCatching { doWork(params) }
protected abstract suspend fun doWork(params: P): R
}

View file

@ -1,15 +1,15 @@
package com.anytypeio.anytype.domain.launch
import com.anytypeio.anytype.domain.base.BaseUseCase
import com.anytypeio.anytype.domain.base.ResultInteractor
import com.anytypeio.anytype.domain.config.UserSettingsRepository
class GetDefaultEditorType(
private val userSettingsRepository: UserSettingsRepository
) : BaseUseCase<GetDefaultEditorType.Response, Unit>() {
) : ResultInteractor<Unit, GetDefaultEditorType.Response>() {
override suspend fun run(params: Unit) = safe {
override suspend fun doWork(params: Unit): Response {
val pair = userSettingsRepository.getDefaultObjectType()
Response(pair.first, pair.second)
return Response(pair.first, pair.second)
}
class Response(val type: String?, val name: String?)

View file

@ -0,0 +1,45 @@
package com.anytypeio.anytype.domain.page
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.domain.base.ResultInteractor
import com.anytypeio.anytype.domain.launch.GetDefaultEditorType
import com.anytypeio.anytype.domain.templates.GetTemplates
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
class CreateNewObject(
private val getDefaultEditorType: GetDefaultEditorType,
private val getTemplates: GetTemplates,
private val createPage: CreatePage,
) : ResultInteractor<Unit, Id>() {
private suspend fun createPageWithType(type: Id): Id {
val template = getTemplates.run(GetTemplates.Params(type)).firstOrNull()?.id
return createPage.run(
CreatePage.Params(
ctx = null,
isDraft = template == null,
type = type,
emoji = null,
template = template
)
)
}
override suspend fun doWork(params: Unit) = getDefaultEditorType(Unit)
.map { it.type }
.catch { emit(null) }
.map { type ->
if (type == null) {
createPage.run(
CreatePage.Params(
isDraft = true
)
)
} else {
createPageWithType(type)
}
}
.first()
}

View file

@ -1,7 +1,7 @@
package com.anytypeio.anytype.domain.page
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.domain.base.BaseUseCase
import com.anytypeio.anytype.domain.base.ResultInteractor
import com.anytypeio.anytype.domain.block.repo.BlockRepository
/**
@ -10,17 +10,15 @@ import com.anytypeio.anytype.domain.block.repo.BlockRepository
*/
class CreatePage(
private val repo: BlockRepository
) : BaseUseCase<Id, CreatePage.Params>() {
) : ResultInteractor<CreatePage.Params, Id>() {
override suspend fun run(params: Params) = safe {
repo.createPage(
ctx = params.ctx,
emoji = null,
isDraft = params.isDraft,
type = params.type,
template = params.template
)
}
override suspend fun doWork(params: Params): Id = repo.createPage(
ctx = params.ctx,
emoji = null,
isDraft = params.isDraft,
type = params.type,
template = params.template
)
/**
* @property [ctx] context (parent) for this new page.
@ -34,5 +32,14 @@ class CreatePage(
val emoji: String?,
val isDraft: Boolean?,
val template: Id? = null
)
) {
constructor(
isDraft: Boolean?,
) : this(
ctx = null,
type = null,
emoji = null,
isDraft = isDraft,
template = null)
}
}

View file

@ -21,36 +21,40 @@ class GetTemplates(
override suspend fun doWork(params: Params): List<ObjectWrapper.Basic> {
return withContext(dispatchers.io) {
repo.searchObjects(
filters = listOf(
DVFilter(
relationKey = Relations.IS_ARCHIVED,
condition = DVFilterCondition.EQUAL,
value = false
try {
repo.searchObjects(
filters = listOf(
DVFilter(
relationKey = Relations.IS_ARCHIVED,
condition = DVFilterCondition.EQUAL,
value = false
),
DVFilter(
relationKey = Relations.IS_DELETED,
condition = DVFilterCondition.EQUAL,
value = false
),
DVFilter(
relationKey = Relations.TYPE,
condition = DVFilterCondition.EQUAL,
value = ObjectType.TEMPLATE_URL
),
DVFilter(
relationKey = Relations.TARGET_OBJECT_TYPE,
condition = DVFilterCondition.EQUAL,
value = params.type
)
),
DVFilter(
relationKey = Relations.IS_DELETED,
condition = DVFilterCondition.EQUAL,
value = false
),
DVFilter(
relationKey = Relations.TYPE,
condition = DVFilterCondition.EQUAL,
value = ObjectType.TEMPLATE_URL
),
DVFilter(
relationKey = Relations.TARGET_OBJECT_TYPE,
condition = DVFilterCondition.EQUAL,
value = params.type
)
),
keys = listOf(Relations.ID, Relations.NAME),
sorts = emptyList(),
fulltext = "",
offset = 0,
limit = 0
).map { obj ->
ObjectWrapper.Basic(obj)
keys = listOf(Relations.ID, Relations.NAME),
sorts = emptyList(),
fulltext = "",
offset = 0,
limit = 0
).map { obj ->
ObjectWrapper.Basic(obj)
}
} catch (e: Exception) {
emptyList()
}
}
}

View file

@ -0,0 +1,120 @@
package com.anytypeio.anytype.domain.page
import com.anytypeio.anytype.core_models.CoroutineTestRule
import com.anytypeio.anytype.core_models.ObjectWrapper
import com.anytypeio.anytype.domain.launch.GetDefaultEditorType
import com.anytypeio.anytype.domain.templates.GetTemplates
import com.anytypeio.anytype.test_utils.MockDataFactory
import com.nhaarman.mockitokotlin2.any
import com.nhaarman.mockitokotlin2.doReturn
import com.nhaarman.mockitokotlin2.stub
import com.nhaarman.mockitokotlin2.times
import com.nhaarman.mockitokotlin2.verifyBlocking
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.runBlocking
import org.junit.Rule
import org.junit.Test
import org.mockito.Mock
import org.mockito.junit.MockitoJUnit
class CreateNewObjectTest {
@get:Rule
val mockitoRule = MockitoJUnit.rule()
@get:Rule
var rule = CoroutineTestRule()
@Mock
lateinit var createPage: CreatePage
@Mock
lateinit var getDefaultEditorType: GetDefaultEditorType
@Mock
lateinit var getTemplates: GetTemplates
@Test
fun `should start creating page`() {
givenCreatePage()
givenGetDefaultObjectType()
runBlocking { givenCreateNewObject().run(any()) }
verifyBlocking(createPage, times(1)) { run(any()) }
}
@Test
fun `should create new object with null template and isDraft true params`() {
val type = MockDataFactory.randomString()
givenCreatePage()
givenGetTemplates()
givenGetDefaultObjectType(type = type)
runBlocking { givenCreateNewObject().run(Unit) }
val params = CreatePage.Params(
ctx = null,
type = type,
emoji = null,
isDraft = true,
template = null
)
verifyBlocking(createPage, times(1)) { run(params) }
}
@Test
fun `should create new object with non nullable template and isDraft false params`() {
val templateId = MockDataFactory.randomUuid()
val type = MockDataFactory.randomString()
val obj = ObjectWrapper.Basic(mapOf("id" to templateId))
givenCreatePage()
givenGetTemplates(objects = listOf(obj))
givenGetDefaultObjectType(type = type)
runBlocking { givenCreateNewObject().run(Unit) }
val params = CreatePage.Params(
ctx = null,
type = type,
emoji = null,
isDraft = false,
template = templateId
)
verifyBlocking(createPage, times(1)) { run(params) }
}
private fun givenCreatePage(id: String = "") {
createPage.stub {
onBlocking { run(any()) } doReturn id
}
}
private fun givenGetDefaultObjectType(type: String? = null, name: String? = null) {
getDefaultEditorType.stub {
onBlocking { invoke(Unit) } doReturn flow {
emit(
GetDefaultEditorType.Response(
type,
name
)
)
}
}
}
private fun givenGetTemplates(objects: List<ObjectWrapper.Basic> = listOf()) {
getTemplates.stub {
onBlocking { run(any()) } doReturn objects
}
}
private fun givenCreateNewObject() = CreateNewObject(
getDefaultEditorType, getTemplates, createPage
)
}

View file

@ -0,0 +1 @@
mock-maker-inline

View file

@ -26,22 +26,19 @@ import com.anytypeio.anytype.core_utils.ui.ViewState
import com.anytypeio.anytype.core_utils.ui.ViewStateViewModel
import com.anytypeio.anytype.domain.auth.interactor.GetProfile
import com.anytypeio.anytype.domain.block.interactor.Move
import com.anytypeio.anytype.domain.config.FeaturesConfigProvider
import com.anytypeio.anytype.domain.config.GetConfig
import com.anytypeio.anytype.domain.config.GetDebugSettings
import com.anytypeio.anytype.domain.dashboard.interactor.CloseDashboard
import com.anytypeio.anytype.domain.dashboard.interactor.OpenDashboard
import com.anytypeio.anytype.domain.dataview.interactor.SearchObjects
import com.anytypeio.anytype.domain.event.interactor.InterceptEvents
import com.anytypeio.anytype.domain.launch.GetDefaultEditorType
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.objects.DeleteObjects
import com.anytypeio.anytype.domain.objects.ObjectStore
import com.anytypeio.anytype.domain.objects.SetObjectListIsArchived
import com.anytypeio.anytype.domain.page.CreatePage
import com.anytypeio.anytype.domain.page.CreateNewObject
import com.anytypeio.anytype.domain.search.CancelSearchSubscription
import com.anytypeio.anytype.domain.search.ObjectSearchSubscriptionContainer
import com.anytypeio.anytype.domain.templates.GetTemplates
import com.anytypeio.anytype.presentation.BuildConfig
import com.anytypeio.anytype.presentation.dashboard.HomeDashboardStateMachine.Interactor
import com.anytypeio.anytype.presentation.dashboard.HomeDashboardStateMachine.State
@ -74,7 +71,6 @@ class HomeDashboardViewModel(
private val getProfile: GetProfile,
private val openDashboard: OpenDashboard,
private val closeDashboard: CloseDashboard,
private val createPage: CreatePage,
private val getConfig: GetConfig,
private val move: Move,
private val interceptEvents: InterceptEvents,
@ -82,15 +78,13 @@ class HomeDashboardViewModel(
private val getDebugSettings: GetDebugSettings,
private val analytics: Analytics,
private val searchObjects: SearchObjects,
private val getDefaultEditorType: GetDefaultEditorType,
private val urlBuilder: UrlBuilder,
private val setObjectListIsArchived: SetObjectListIsArchived,
private val deleteObjects: DeleteObjects,
private val featuresConfigProvider: FeaturesConfigProvider,
private val objectSearchSubscriptionContainer: ObjectSearchSubscriptionContainer,
private val cancelSearchSubscription: CancelSearchSubscription,
private val objectStore: ObjectStore,
private val getTemplates: GetTemplates
private val createNewObject: CreateNewObject
) : ViewStateViewModel<State>(),
HomeDashboardEventConverter by eventConverter,
SupportNavigation<EventWrapper<AppNavigation.Command>> {
@ -264,7 +258,15 @@ class HomeDashboardViewModel(
fun onAddNewDocumentClicked() {
Timber.d("onAddNewDocumentClicked, ")
proceedWithGettingDefaultPageType()
subscriptions += viewModelScope.launch {
createNewObject.execute(Unit).fold(
onSuccess = { id ->
machine.onEvents(listOf(Machine.Event.OnFinishedCreatingPage))
proceedWithOpeningDocument(id)
},
onFailure = { e -> Timber.e(e, "Error while creating a new page") }
)
}
}
/**
@ -754,61 +756,6 @@ class HomeDashboardViewModel(
enum class TAB { FAVOURITE, RECENT, SETS, SHARED, BIN }
//region CREATE PAGE
private fun proceedWithGettingDefaultPageType() {
viewModelScope.launch {
getDefaultEditorType.invoke(Unit).proceed(
failure = {
Timber.e(it, "Error while getting default page type")
proceedWithCreatingNewObject(type = null)
},
success = { response -> proceedWithCreatingNewObject(type = response.type) }
)
}
}
private fun proceedWithCreatingNewObject(type: String?) {
if (type != null) {
viewModelScope.launch {
val templates = try {
getTemplates.run(GetTemplates.Params(type))
} catch (e: Exception) {
emptyList()
}
val template = if (templates.size == 1) templates.first().id else null
val params = CreatePage.Params(
ctx = null,
isDraft = template == null,
type = type,
emoji = null,
template = template
)
createNewObject(params)
}
} else {
val params = CreatePage.Params(
ctx = null,
isDraft = true,
type = type,
emoji = null
)
createNewObject(params)
}
}
private fun createNewObject(params: CreatePage.Params) {
viewModelScope.launch {
createPage.invoke(params).proceed(
failure = { e -> Timber.e(e, "Error while creating a new page") },
success = { id ->
machine.onEvents(listOf(Machine.Event.OnFinishedCreatingPage))
proceedWithOpeningDocument(id)
}
)
}
}
//endregion
enum class Mode { DEFAULT, SELECTION }
sealed class Alert {

View file

@ -5,28 +5,24 @@ import androidx.lifecycle.ViewModelProvider
import com.anytypeio.anytype.analytics.base.Analytics
import com.anytypeio.anytype.domain.auth.interactor.GetProfile
import com.anytypeio.anytype.domain.block.interactor.Move
import com.anytypeio.anytype.domain.config.FeaturesConfigProvider
import com.anytypeio.anytype.domain.config.GetConfig
import com.anytypeio.anytype.domain.config.GetDebugSettings
import com.anytypeio.anytype.domain.dashboard.interactor.CloseDashboard
import com.anytypeio.anytype.domain.dashboard.interactor.OpenDashboard
import com.anytypeio.anytype.domain.dataview.interactor.SearchObjects
import com.anytypeio.anytype.domain.event.interactor.InterceptEvents
import com.anytypeio.anytype.domain.launch.GetDefaultEditorType
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.objects.DeleteObjects
import com.anytypeio.anytype.domain.objects.ObjectStore
import com.anytypeio.anytype.domain.objects.SetObjectListIsArchived
import com.anytypeio.anytype.domain.page.CreatePage
import com.anytypeio.anytype.domain.page.CreateNewObject
import com.anytypeio.anytype.domain.search.CancelSearchSubscription
import com.anytypeio.anytype.domain.search.ObjectSearchSubscriptionContainer
import com.anytypeio.anytype.domain.templates.GetTemplates
class HomeDashboardViewModelFactory(
private val getProfile: GetProfile,
private val openDashboard: OpenDashboard,
private val closeDashboard: CloseDashboard,
private val createPage: CreatePage,
private val getConfig: GetConfig,
private val move: Move,
private val interceptEvents: InterceptEvents,
@ -34,15 +30,13 @@ class HomeDashboardViewModelFactory(
private val getDebugSettings: GetDebugSettings,
private val analytics: Analytics,
private val searchObjects: SearchObjects,
private val getDefaultEditorType: GetDefaultEditorType,
private val urlBuilder: UrlBuilder,
private val setObjectListIsArchived: SetObjectListIsArchived,
private val deleteObjects: DeleteObjects,
private val featuresConfigProvider: FeaturesConfigProvider,
private val objectSearchSubscriptionContainer: ObjectSearchSubscriptionContainer,
private val cancelSearchSubscription: CancelSearchSubscription,
private val objectStore: ObjectStore,
private val getTemplates: GetTemplates
private val createNewObject: CreateNewObject
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
@ -51,7 +45,6 @@ class HomeDashboardViewModelFactory(
getProfile = getProfile,
openDashboard = openDashboard,
closeDashboard = closeDashboard,
createPage = createPage,
getConfig = getConfig,
move = move,
interceptEvents = interceptEvents,
@ -60,14 +53,12 @@ class HomeDashboardViewModelFactory(
analytics = analytics,
searchObjects = searchObjects,
urlBuilder = urlBuilder,
getDefaultEditorType = getDefaultEditorType,
deleteObjects = deleteObjects,
setObjectListIsArchived = setObjectListIsArchived,
featuresConfigProvider = featuresConfigProvider,
objectSearchSubscriptionContainer = objectSearchSubscriptionContainer,
cancelSearchSubscription = cancelSearchSubscription,
objectStore = objectStore,
getTemplates = getTemplates
createNewObject = createNewObject
) as T
}
}

View file

@ -78,6 +78,7 @@ import com.anytypeio.anytype.presentation.common.Action
import com.anytypeio.anytype.presentation.common.Delegator
import com.anytypeio.anytype.presentation.common.StateReducer
import com.anytypeio.anytype.presentation.common.SupportCommand
import com.anytypeio.anytype.domain.page.CreateNewObject
import com.anytypeio.anytype.presentation.editor.ControlPanelMachine.Interactor
import com.anytypeio.anytype.presentation.editor.Editor.Restore
import com.anytypeio.anytype.presentation.editor.editor.Command
@ -235,7 +236,8 @@ class EditorViewModel(
private val downloadUnsplashImage: DownloadUnsplashImage,
private val setDocCoverImage: SetDocCoverImage,
private val setDocImageIcon: SetDocumentImageIcon,
private val templateDelegate: EditorTemplateDelegate
private val templateDelegate: EditorTemplateDelegate,
private val createNewObject: CreateNewObject
) : ViewStateViewModel<ViewState>(),
PickerListener,
SupportNavigation<EventWrapper<AppNavigation.Command>>,
@ -2858,6 +2860,28 @@ class EditorViewModel(
}
}
fun onAddNewDocumentClicked() {
Timber.d("onAddNewDocumentClicked, ")
viewModelScope.sendEvent(
analytics = analytics,
eventName = EventsDictionary.createObjectNavBar,
props = Props(mapOf(EventsPropertiesKey.context to analyticsContext))
)
jobs += viewModelScope.launch {
createNewObject.execute(Unit).fold(
onSuccess = { id ->
proceedWithOpeningPage(id)
},
onFailure = { e -> Timber.e(e, "Error while creating a new page") }
)
}
}
@Deprecated("Not used")
fun onAddNewPageClicked() {
Timber.d("onAddNewPageClicked, ")
controlPanelInteractor.onEvent(ControlPanelMachine.Event.OnAddBlockToolbarOptionSelected)
@ -5125,12 +5149,12 @@ class EditorViewModel(
fun onAddMentionNewPageClicked(mentionText: String) {
Timber.d("onAddMentionNewPageClicked, mentionText:[$mentionText]")
viewModelScope.launch {
getDefaultEditorType.invoke(Unit).proceed(
failure = {
getDefaultEditorType.execute(Unit).fold(
onFailure = {
Timber.e(it, "Error while getting default object type")
proceedWithCreateNewObject(objectType = null, mentionText = mentionText)
},
success = {
onSuccess = {
proceedWithCreateNewObject(objectType = it.type, mentionText = mentionText)
}
)
@ -5407,9 +5431,11 @@ class EditorViewModel(
}
private suspend fun proceedWithSortingObjectTypesForObjectTypeWidget(views: List<ObjectTypeView.Item>) {
getDefaultEditorType.invoke(Unit).proceed(
failure = { Timber.e(it, "Error while getting default object type") },
success = { response ->
getDefaultEditorType.execute(Unit).fold(
onFailure = {
Timber.e(it, "Error while getting default object type")
},
onSuccess = { response ->
val filtered = views.filter { it.id != response.type }
val result = listOf(ObjectTypeView.Search) + filtered
controlPanelInteractor.onEvent(
@ -5498,9 +5524,11 @@ class EditorViewModel(
fun proceedToCreateObjectAndAddToTextAsLink(name: String) {
Timber.d("proceedToCreateObjectAndAddToTextAsLink, name:[$name]")
viewModelScope.launch {
getDefaultEditorType.invoke(Unit).proceed(
failure = { Timber.e(it, "Error while getting default object type") },
success = { response ->
getDefaultEditorType.execute(Unit).fold(
onFailure = {
Timber.e(it, "Error while getting default object type")
},
onSuccess = { response ->
createObjectAddProceedToAddToTextAsLink(
name = name,
type = response.type

View file

@ -30,6 +30,7 @@ import com.anytypeio.anytype.domain.unsplash.DownloadUnsplashImage
import com.anytypeio.anytype.presentation.common.Action
import com.anytypeio.anytype.presentation.common.Delegator
import com.anytypeio.anytype.presentation.common.StateReducer
import com.anytypeio.anytype.domain.page.CreateNewObject
import com.anytypeio.anytype.presentation.editor.editor.DetailModificationManager
import com.anytypeio.anytype.presentation.editor.editor.Orchestrator
import com.anytypeio.anytype.presentation.editor.render.DefaultBlockViewRenderer
@ -67,7 +68,8 @@ open class EditorViewModelFactory(
private val downloadUnsplashImage: DownloadUnsplashImage,
private val setDocCoverImage: SetDocCoverImage,
private val setDocImageIcon: SetDocumentImageIcon,
private val editorTemplateDelegate: EditorTemplateDelegate
private val editorTemplateDelegate: EditorTemplateDelegate,
private val createNewObject: CreateNewObject
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
@ -102,7 +104,8 @@ open class EditorViewModelFactory(
downloadUnsplashImage = downloadUnsplashImage,
setDocCoverImage = setDocCoverImage,
setDocImageIcon = setDocImageIcon,
templateDelegate = editorTemplateDelegate
templateDelegate = editorTemplateDelegate,
createNewObject = createNewObject
) as T
}
}

View file

@ -4,13 +4,15 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.anytypeio.anytype.domain.page.CreatePage
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.launch
import timber.log.Timber
class CreateObjectViewModel(private val createPage: CreatePage) : ViewModel(){
class CreateObjectViewModel(private val createPage: CreatePage) : ViewModel() {
val createObjectStatus = MutableSharedFlow<State>(replay = 0)
private val jobs = mutableListOf<Job>()
fun onStart(type: String) {
onCreatePage(type)
@ -23,21 +25,30 @@ class CreateObjectViewModel(private val createPage: CreatePage) : ViewModel(){
type = type,
emoji = null
)
createPage.invoke(viewModelScope, params) { result ->
result.either(
fnL = { e -> Timber.e(e, "Error while creating a new page") },
fnR = { id ->
viewModelScope.launch {
createObjectStatus.emit(State.Success(id))
}
jobs += viewModelScope.launch {
createPage.execute(params).fold(
onFailure = { e ->
Timber.e(e, "Error while creating a new page")
},
onSuccess = { id ->
createObjectStatus.emit(State.Success(id))
}
)
}
}
fun onStop() {
viewModelScope.launch {
jobs.apply {
forEach { it.cancel() }
clear()
}
}
}
sealed class State {
data class Success(val id: String): State()
data class Error(val msg: String): State()
data class Success(val id: String) : State()
data class Error(val msg: String) : State()
}
class Factory(

View file

@ -5,6 +5,9 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.anytypeio.anytype.analytics.base.Analytics
import com.anytypeio.anytype.analytics.base.EventsDictionary
import com.anytypeio.anytype.analytics.base.EventsPropertiesKey
import com.anytypeio.anytype.analytics.base.sendEvent
import com.anytypeio.anytype.analytics.props.Props
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.DV
import com.anytypeio.anytype.core_models.DVViewerRelation
@ -37,6 +40,7 @@ import com.anytypeio.anytype.domain.templates.GetTemplates
import com.anytypeio.anytype.domain.unsplash.DownloadUnsplashImage
import com.anytypeio.anytype.presentation.common.Action
import com.anytypeio.anytype.presentation.common.Delegator
import com.anytypeio.anytype.domain.page.CreateNewObject
import com.anytypeio.anytype.presentation.editor.editor.Proxy
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
import com.anytypeio.anytype.presentation.editor.model.TextUpdate
@ -94,7 +98,8 @@ class ObjectSetViewModel(
private val urlBuilder: UrlBuilder,
private val session: ObjectSetSession,
private val analytics: Analytics,
private val getTemplates: GetTemplates
private val getTemplates: GetTemplates,
private val createNewObject: CreateNewObject
) : ViewModel(), SupportNavigation<EventWrapper<AppNavigation.Command>> {
val status = MutableStateFlow(SyncStatus.UNKNOWN)
@ -143,6 +148,7 @@ class ObjectSetViewModel(
private lateinit var context: Id
init {
viewModelScope.launch {
dispatcher.flow().collect { defaultPayloadConsumer(it) }
}
@ -496,9 +502,10 @@ class ObjectSetViewModel(
is CellView.Tag, is CellView.Status, is CellView.Object, is CellView.File -> {
val targetObjectTypes = mutableListOf<String>()
if (cell is CellView.Object) {
val relation = reducer.state.value.dataview.content<DV>().relations.find { relation ->
relation.key == cell.key
}
val relation =
reducer.state.value.dataview.content<DV>().relations.find { relation ->
relation.key == cell.key
}
if (relation != null) {
targetObjectTypes.addAll(relation.objectTypes)
}
@ -736,11 +743,11 @@ class ObjectSetViewModel(
}
}
private suspend fun resolveTemplateForNewRecord() : Id? {
private suspend fun resolveTemplateForNewRecord(): Id? {
val obj = ObjectWrapper.Basic(reducer.state.value.details[context]?.map ?: emptyMap())
val type = if (obj.setOf.size == 1) obj.setOf.first() else null
return if (type != null) {
val templates = try {
val templates = try {
getTemplates.run(GetTemplates.Params(type))
} catch (e: Exception) {
emptyList()
@ -757,7 +764,9 @@ class ObjectSetViewModel(
private suspend fun proceedWithRefreshingViewerAfterObjectCreation() {
val set = reducer.state.value
if (set.isInitialized) {
val viewer = try { set.viewerById(session.currentViewerId).id } catch (e: Exception) {
val viewer = try {
set.viewerById(session.currentViewerId).id
} catch (e: Exception) {
null
}
if (viewer != null) {
@ -1042,6 +1051,20 @@ class ObjectSetViewModel(
//region NAVIGATION
private fun proceedWithOpeningPage(target: Id) {
jobs += viewModelScope.launch {
closeBlock(CloseBlock.Params(context)).process(
success = {
navigate(EventWrapper(AppNavigation.Command.OpenObject(id = target)))
},
failure = {
Timber.e(it, "Error while closing object set: $context")
navigate(EventWrapper(AppNavigation.Command.OpenObject(id = target)))
}
)
}
}
private fun proceedWithNavigation(target: Id, layout: ObjectType.Layout?) {
when (layout) {
ObjectType.Layout.BASIC,
@ -1049,19 +1072,7 @@ class ObjectSetViewModel(
ObjectType.Layout.TODO,
ObjectType.Layout.NOTE,
ObjectType.Layout.IMAGE,
ObjectType.Layout.FILE -> {
viewModelScope.launch {
closeBlock(CloseBlock.Params(context)).process(
success = {
navigate(EventWrapper(AppNavigation.Command.OpenObject(id = target)))
},
failure = {
Timber.e(it, "Error while closing object set: $context")
navigate(EventWrapper(AppNavigation.Command.OpenObject(id = target)))
}
)
}
}
ObjectType.Layout.FILE -> proceedWithOpeningPage(target)
ObjectType.Layout.SET -> {
viewModelScope.launch {
closeBlock(CloseBlock.Params(context)).process(
@ -1118,6 +1129,24 @@ class ObjectSetViewModel(
proceedWithExiting()
}
fun onAddNewDocumentClicked() {
Timber.d("onAddNewDocumentClicked, ")
viewModelScope.sendEvent(
analytics = analytics,
eventName = EventsDictionary.createObjectNavBar,
props = Props(mapOf(EventsPropertiesKey.context to analyticsContext))
)
jobs += viewModelScope.launch {
createNewObject.execute(Unit).fold(
onSuccess = { id ->
proceedWithOpeningPage(id)
},
onFailure = { e -> Timber.e(e, "Error while creating a new page") }
)
}
}
fun onSearchButtonClicked() {
viewModelScope.launch {
closeBlock(CloseBlock.Params(context)).process(
@ -1133,8 +1162,10 @@ class ObjectSetViewModel(
const val NOT_ALLOWED_CELL = "Not allowed for this cell"
const val OBJECT_TYPE_UNKNOWN = "Can't open object, object type unknown"
const val DATA_VIEW_HAS_NO_VIEW_MSG = "Data view has no view."
const val DATA_VIEW_NOT_FOUND_ERROR = "Content missing for this set. Please, try again later."
const val OBJECT_SET_HAS_EMPTY_SOURCE_ERROR = "Object type is not defined for this set. Please, setup object type on Desktop."
const val DATA_VIEW_NOT_FOUND_ERROR =
"Content missing for this set. Please, try again later."
const val OBJECT_SET_HAS_EMPTY_SOURCE_ERROR =
"Object type is not defined for this set. Please, setup object type on Desktop."
const val TOAST_SET_NOT_EXIST = "This object doesn't exist"
}
}

View file

@ -20,6 +20,7 @@ import com.anytypeio.anytype.domain.templates.GetTemplates
import com.anytypeio.anytype.domain.unsplash.DownloadUnsplashImage
import com.anytypeio.anytype.presentation.common.Action
import com.anytypeio.anytype.presentation.common.Delegator
import com.anytypeio.anytype.domain.page.CreateNewObject
import com.anytypeio.anytype.presentation.util.Dispatcher
class ObjectSetViewModelFactory(
@ -42,7 +43,8 @@ class ObjectSetViewModelFactory(
private val urlBuilder: UrlBuilder,
private val session: ObjectSetSession,
private val analytics: Analytics,
private val getTemplates: GetTemplates
private val getTemplates: GetTemplates,
private val createNewObject: CreateNewObject
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
@ -66,7 +68,8 @@ class ObjectSetViewModelFactory(
urlBuilder = urlBuilder,
session = session,
analytics = analytics,
getTemplates = getTemplates
getTemplates = getTemplates,
createNewObject = createNewObject
) as T
}
}

View file

@ -17,6 +17,7 @@ import com.anytypeio.anytype.domain.device.ClearFileCache
import com.anytypeio.anytype.domain.launch.GetDefaultEditorType
import com.anytypeio.anytype.domain.launch.SetDefaultEditorType
import com.anytypeio.anytype.domain.misc.AppActionManager
import com.anytypeio.anytype.presentation.splash.SplashViewModel
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
@ -35,9 +36,13 @@ class OtherSettingsViewModel(
init {
viewModelScope.launch {
getDefaultEditorType.invoke(Unit).proceed(
failure = { Timber.e(it, "Error while getting user settings") },
success = { commands.emit(Command.SetObjectType(name = it.name)) }
getDefaultEditorType.execute(Unit).fold(
onFailure = { e ->
Timber.e(e, "Error while getting user settings")
},
onSuccess = {
commands.emit(Command.SetObjectType(name = it.name))
}
)
}
}

View file

@ -54,12 +54,12 @@ class SplashViewModel(
private fun proceedWithUserSettings() {
viewModelScope.launch {
getDefaultEditorType.invoke(Unit).process(
failure = {
Timber.e(it, "Error while getting default page type")
getDefaultEditorType.execute(Unit).fold(
onFailure = { e ->
Timber.e(e, "Error while getting default page type")
checkAuthorizationStatus()
},
success = { result ->
onSuccess = { result ->
Timber.d("getDefaultPageType: ${result.type}")
if (result.type == null) {
commands.emit(Command.CheckFirstInstall)
@ -169,16 +169,18 @@ class SplashViewModel(
fun onIntentCreateNewObject(type: Id) {
viewModelScope.launch {
createPage(
createPage.execute(
CreatePage.Params(
ctx = null,
emoji = null,
isDraft = true,
type = type
)
).process(
failure = { proceedWithNavigation() },
success = { target ->
).fold(
onFailure = { e ->
proceedWithNavigation()
},
onSuccess = { target ->
commands.emit(Command.NavigateToObject(target))
}
)

View file

@ -14,7 +14,6 @@ import com.anytypeio.anytype.domain.base.Either
import com.anytypeio.anytype.domain.block.interactor.Move
import com.anytypeio.anytype.domain.block.repo.BlockRepository
import com.anytypeio.anytype.domain.config.DebugSettings
import com.anytypeio.anytype.domain.config.FeaturesConfigProvider
import com.anytypeio.anytype.domain.config.Gateway
import com.anytypeio.anytype.domain.config.GetConfig
import com.anytypeio.anytype.domain.config.GetDebugSettings
@ -22,13 +21,12 @@ import com.anytypeio.anytype.domain.dashboard.interactor.CloseDashboard
import com.anytypeio.anytype.domain.dashboard.interactor.OpenDashboard
import com.anytypeio.anytype.domain.dataview.interactor.SearchObjects
import com.anytypeio.anytype.domain.event.interactor.InterceptEvents
import com.anytypeio.anytype.domain.launch.GetDefaultEditorType
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.objects.DefaultObjectStore
import com.anytypeio.anytype.domain.objects.DeleteObjects
import com.anytypeio.anytype.domain.objects.ObjectStore
import com.anytypeio.anytype.domain.objects.SetObjectListIsArchived
import com.anytypeio.anytype.domain.page.CreatePage
import com.anytypeio.anytype.domain.page.CreateNewObject
import com.anytypeio.anytype.domain.search.CancelSearchSubscription
import com.anytypeio.anytype.domain.search.ObjectSearchSubscriptionContainer
import com.anytypeio.anytype.domain.search.SubscriptionEventChannel
@ -70,9 +68,6 @@ open class DashboardTestSetup {
@Mock
lateinit var setObjectListIsArchived: SetObjectListIsArchived
@Mock
lateinit var createPage: CreatePage
@Mock
lateinit var interceptEvents: InterceptEvents
@ -94,12 +89,6 @@ open class DashboardTestSetup {
@Mock
lateinit var objectTypesProvider: ObjectTypesProvider
@Mock
lateinit var featuresConfigProvider: FeaturesConfigProvider
@Mock
lateinit var getDefaultEditorType: GetDefaultEditorType
@Mock
lateinit var cancelSearchSubscription: CancelSearchSubscription
@ -112,7 +101,8 @@ open class DashboardTestSetup {
@Mock
lateinit var getTemplates: GetTemplates
lateinit var objectSearchSubscriptionContainer: ObjectSearchSubscriptionContainer
@Mock
lateinit var createNewObject: CreateNewObject
lateinit var objectStore: ObjectStore
@ -132,7 +122,6 @@ open class DashboardTestSetup {
getProfile = getProfile,
openDashboard = openDashboard,
closeDashboard = closeDashboard,
createPage = createPage,
getConfig = getConfig,
move = move,
interceptEvents = interceptEvents,
@ -144,10 +133,8 @@ open class DashboardTestSetup {
analytics = analytics,
searchObjects = searchObjects,
urlBuilder = builder,
getDefaultEditorType = getDefaultEditorType,
setObjectListIsArchived = setObjectListIsArchived,
deleteObjects = deleteObjects,
featuresConfigProvider = featuresConfigProvider,
cancelSearchSubscription = cancelSearchSubscription,
objectStore = objectStore,
objectSearchSubscriptionContainer = ObjectSearchSubscriptionContainer(
@ -160,7 +147,7 @@ open class DashboardTestSetup {
main = coroutineTestRule.testDispatcher
)
),
getTemplates = getTemplates
createNewObject = createNewObject
)
}
@ -192,14 +179,6 @@ open class DashboardTestSetup {
}
}
fun stubCreatePage(id: String) {
createPage.stub {
onBlocking { invoke(any(), any(), any()) } doAnswer { answer ->
answer.getArgument<(Either<Throwable, String>) -> Unit>(2)(Either.Right(id))
}
}
}
fun stubCloseDashboard() {
closeDashboard.stub {
onBlocking { invoke(any(), any(), any()) } doAnswer { answer ->

View file

@ -5,7 +5,7 @@ import com.anytypeio.anytype.analytics.base.Analytics
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.Config
import com.anytypeio.anytype.core_models.Event
import com.anytypeio.anytype.core_models.ObjectWrapper
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.Payload
import com.anytypeio.anytype.core_models.Relations
import com.anytypeio.anytype.core_models.SmartBlockType
@ -15,7 +15,6 @@ import com.anytypeio.anytype.domain.auth.interactor.GetProfile
import com.anytypeio.anytype.domain.base.Either
import com.anytypeio.anytype.domain.block.interactor.Move
import com.anytypeio.anytype.domain.config.DebugSettings
import com.anytypeio.anytype.domain.config.FeaturesConfigProvider
import com.anytypeio.anytype.domain.config.Gateway
import com.anytypeio.anytype.domain.config.GetConfig
import com.anytypeio.anytype.domain.config.GetDebugSettings
@ -23,24 +22,25 @@ import com.anytypeio.anytype.domain.dashboard.interactor.CloseDashboard
import com.anytypeio.anytype.domain.dashboard.interactor.OpenDashboard
import com.anytypeio.anytype.domain.dataview.interactor.SearchObjects
import com.anytypeio.anytype.domain.event.interactor.InterceptEvents
import com.anytypeio.anytype.domain.launch.GetDefaultEditorType
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.objects.DeleteObjects
import com.anytypeio.anytype.domain.objects.ObjectStore
import com.anytypeio.anytype.domain.objects.SetObjectListIsArchived
import com.anytypeio.anytype.domain.page.CreatePage
import com.anytypeio.anytype.domain.page.CreateNewObject
import com.anytypeio.anytype.domain.search.CancelSearchSubscription
import com.anytypeio.anytype.domain.search.ObjectSearchSubscriptionContainer
import com.anytypeio.anytype.domain.templates.GetTemplates
import com.anytypeio.anytype.presentation.MockBlockContentFactory.StubLinkContent
import com.anytypeio.anytype.presentation.MockBlockFactory.link
import com.anytypeio.anytype.presentation.navigation.AppNavigation
import com.anytypeio.anytype.presentation.util.CoroutinesTestRule
import com.anytypeio.anytype.test_utils.MockDataFactory
import com.anytypeio.anytype.test_utils.ValueClassAnswer
import com.jraska.livedata.test
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runBlockingTest
import org.junit.Before
import org.junit.Rule
@ -57,7 +57,6 @@ import org.mockito.kotlin.eq
import org.mockito.kotlin.stub
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import org.mockito.kotlin.verifyBlocking
import org.mockito.kotlin.verifyZeroInteractions
import kotlin.test.assertContains
@ -81,9 +80,6 @@ class HomeDashboardViewModelTest {
@Mock
lateinit var closeDashboard: CloseDashboard
@Mock
lateinit var createPage: CreatePage
@Mock
lateinit var searchObjects: SearchObjects
@ -111,12 +107,6 @@ class HomeDashboardViewModelTest {
@Mock
lateinit var objectTypesProvider: ObjectTypesProvider
@Mock
lateinit var featuresConfigProvider: FeaturesConfigProvider
@Mock
lateinit var getDefaultEditorType: GetDefaultEditorType
@Mock
lateinit var cancelSearchSubscription: CancelSearchSubscription
@ -127,7 +117,7 @@ class HomeDashboardViewModelTest {
lateinit var objectStore: ObjectStore
@Mock
lateinit var getTemplates: GetTemplates
lateinit var createNewObject: CreateNewObject
private lateinit var vm: HomeDashboardViewModel
@ -149,7 +139,6 @@ class HomeDashboardViewModelTest {
getProfile = getProfile,
openDashboard = openDashboard,
closeDashboard = closeDashboard,
createPage = createPage,
getConfig = getConfig,
move = move,
interceptEvents = interceptEvents,
@ -163,12 +152,10 @@ class HomeDashboardViewModelTest {
deleteObjects = deleteObjects,
setObjectListIsArchived = setObjectListIsArchived,
urlBuilder = builder,
getDefaultEditorType = getDefaultEditorType,
featuresConfigProvider = featuresConfigProvider,
cancelSearchSubscription = cancelSearchSubscription,
objectStore = objectStore,
objectSearchSubscriptionContainer = objectSearchSubscriptionContainer,
getTemplates = getTemplates
createNewObject = createNewObject
)
}
@ -361,19 +348,6 @@ class HomeDashboardViewModelTest {
}
}
@Test
fun `should start creating page when requested from UI`() {
stubObserveEvents()
stubGetDefaultObjectType()
vm = givenViewModel()
vm.onAddNewDocumentClicked()
verifyBlocking(createPage, times(1)) { invoke(any()) }
}
@Test
fun `should close dashboard and navigate to page screen when page is created`() {
@ -382,12 +356,10 @@ class HomeDashboardViewModelTest {
stubObserveEvents()
stubGetEditorSettings()
stubCloseDashboard()
stubCreatePage(id)
stubGetTemplates()
stubGetDefaultObjectType()
vm = givenViewModel()
givenDelegateId(id)
vm.onAddNewDocumentClicked()
vm.navigation
@ -398,58 +370,10 @@ class HomeDashboardViewModelTest {
}
}
@Test
fun `should create new object with null template and isDraft true params`() {
val type = MockDataFactory.randomString()
stubObserveEvents()
stubGetEditorSettings()
stubCloseDashboard()
stubGetTemplates()
stubGetDefaultObjectType(type = type)
vm = givenViewModel()
vm.onAddNewDocumentClicked()
val params = CreatePage.Params(
ctx = null,
type = type,
emoji = null,
isDraft = true,
template = null
)
verifyBlocking(createPage, times(1)) { invoke(params) }
}
@Test
fun `should create new object with non nullable template and isDraft false params`() {
val templateId = MockDataFactory.randomUuid()
val type = MockDataFactory.randomString()
val obj = ObjectWrapper.Basic(mapOf("id" to templateId))
stubObserveEvents()
stubGetEditorSettings()
stubCloseDashboard()
stubGetTemplates(objects = listOf(obj))
stubGetDefaultObjectType(type = type)
vm = givenViewModel()
vm.onAddNewDocumentClicked()
val params = CreatePage.Params(
ctx = null,
type = type,
emoji = null,
isDraft = false,
template = templateId
)
verifyBlocking(createPage, times(1)) { invoke(params) }
private fun givenDelegateId(id: String) {
createNewObject.stub {
onBlocking { execute(Unit) } doAnswer ValueClassAnswer(id)
}
}
private fun stubGetConfig(response: Either.Right<Config>) {
@ -480,12 +404,6 @@ class HomeDashboardViewModelTest {
}
}
private fun stubCreatePage(id: String) {
createPage.stub {
onBlocking { invoke(any()) } doReturn Either.Right(id)
}
}
private fun stubCloseDashboard() {
closeDashboard.stub {
onBlocking { invoke(any(), any(), any()) } doAnswer { answer ->
@ -500,17 +418,6 @@ class HomeDashboardViewModelTest {
}
}
private fun stubGetDefaultObjectType(type: String? = null, name: String? = null) {
getDefaultEditorType.stub {
onBlocking { invoke(Unit) } doReturn Either.Right(
GetDefaultEditorType.Response(
type,
name
)
)
}
}
private fun stubObserveProfile() {
getProfile.stub {
on {
@ -522,10 +429,4 @@ class HomeDashboardViewModelTest {
} doReturn emptyFlow()
}
}
private fun stubGetTemplates(objects: List<ObjectWrapper.Basic> = listOf()) {
getTemplates.stub {
onBlocking { run(any()) } doReturn objects
}
}
}

View file

@ -148,7 +148,7 @@ class BlockReadModeTest : EditorViewModelTest() {
val paragraphs = blocks
stubObserveEvents(flow)
stubOpenPage()
buildViewModel()
givenViewModel()
vm.onStart(root)
@ -196,7 +196,7 @@ class BlockReadModeTest : EditorViewModelTest() {
val paragraphs = blocks
stubObserveEvents(flow)
stubOpenPage()
buildViewModel()
givenViewModel()
vm.onStart(root)
@ -230,7 +230,7 @@ class BlockReadModeTest : EditorViewModelTest() {
val paragraphs = blocks
stubObserveEvents(flow)
stubOpenPage()
buildViewModel()
givenViewModel()
vm.onStart(root)
@ -280,7 +280,7 @@ class BlockReadModeTest : EditorViewModelTest() {
val paragraphs = blocks
stubObserveEvents(flow)
stubOpenPage()
buildViewModel()
givenViewModel()
vm.onStart(root)
@ -317,7 +317,7 @@ class BlockReadModeTest : EditorViewModelTest() {
val paragraphs = blocks
stubObserveEvents(flow)
stubOpenPage()
buildViewModel()
givenViewModel()
vm.onStart(root)

View file

@ -67,11 +67,14 @@ import com.anytypeio.anytype.domain.page.bookmark.CreateBookmark
import com.anytypeio.anytype.domain.page.bookmark.SetupBookmark
import com.anytypeio.anytype.domain.sets.FindObjectSetForType
import com.anytypeio.anytype.domain.status.InterceptThreadStatus
import com.anytypeio.anytype.domain.templates.ApplyTemplate
import com.anytypeio.anytype.domain.templates.GetTemplates
import com.anytypeio.anytype.domain.unsplash.DownloadUnsplashImage
import com.anytypeio.anytype.domain.unsplash.UnsplashRepository
import com.anytypeio.anytype.presentation.MockBlockFactory
import com.anytypeio.anytype.presentation.common.Action
import com.anytypeio.anytype.presentation.common.Delegator
import com.anytypeio.anytype.domain.page.CreateNewObject
import com.anytypeio.anytype.presentation.editor.cover.CoverImageHashProvider
import com.anytypeio.anytype.presentation.editor.editor.BlockDimensions
import com.anytypeio.anytype.presentation.editor.editor.Command
@ -90,6 +93,7 @@ import com.anytypeio.anytype.presentation.editor.editor.styling.StyleToolbarStat
import com.anytypeio.anytype.presentation.editor.editor.styling.StylingEvent
import com.anytypeio.anytype.presentation.editor.render.DefaultBlockViewRenderer
import com.anytypeio.anytype.presentation.editor.selection.SelectionStateHolder
import com.anytypeio.anytype.presentation.editor.template.DefaultEditorTemplateDelegate
import com.anytypeio.anytype.presentation.editor.template.EditorTemplateDelegate
import com.anytypeio.anytype.presentation.editor.toggle.ToggleStateHolder
import com.anytypeio.anytype.presentation.navigation.AppNavigation
@ -98,6 +102,7 @@ import com.anytypeio.anytype.presentation.util.CoroutinesTestRule
import com.anytypeio.anytype.presentation.util.Dispatcher
import com.anytypeio.anytype.presentation.util.TXT
import com.anytypeio.anytype.test_utils.MockDataFactory
import com.anytypeio.anytype.test_utils.ValueClassAnswer
import com.jraska.livedata.test
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay
@ -111,13 +116,13 @@ import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.RETURNS_MOCKS
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.any
import org.mockito.kotlin.argThat
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.doAnswer
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.stub
import org.mockito.kotlin.times
@ -296,8 +301,17 @@ open class EditorViewModelTest {
@Mock
lateinit var copyFileToCacheDirectory: CopyFileToCacheDirectory
@Mock
lateinit var getTemplates: GetTemplates
@Mock
lateinit var applyTemplate: ApplyTemplate
private lateinit var editorTemplateDelegate: EditorTemplateDelegate
@Mock
lateinit var createNewObject: CreateNewObject
private lateinit var updateDetail: UpdateDetail
lateinit var vm: EditorViewModel
@ -335,12 +349,15 @@ open class EditorViewModelTest {
fun setup() {
MockitoAnnotations.openMocks(this)
builder = UrlBuilder(gateway)
editorTemplateDelegate = mock(defaultAnswer = RETURNS_MOCKS)
editorTemplateDelegate = DefaultEditorTemplateDelegate(
getTemplates = getTemplates,
applyTemplate = applyTemplate
)
}
@Test
fun `should not start observing events when view model is initialized`() {
buildViewModel()
givenViewModel()
verifyZeroInteractions(interceptEvents)
}
@ -349,7 +366,7 @@ open class EditorViewModelTest {
val param = OpenPage.Params(id = root)
stubInterceptEvents()
buildViewModel()
givenViewModel()
stubOpenPage(context = root)
vm.onStart(root)
@ -398,7 +415,7 @@ open class EditorViewModelTest {
stubInterceptEvents()
buildViewModel(builder)
givenViewModel(builder)
vm.onStart(root)
@ -428,7 +445,7 @@ open class EditorViewModelTest {
stubOpenPage(root)
stubInterceptEvents()
buildViewModel()
givenViewModel()
vm.onStart(root)
@ -448,7 +465,7 @@ open class EditorViewModelTest {
stubInterceptEvents()
stubClosePage(response)
buildViewModel()
givenViewModel()
val testObserver = vm.navigation.test()
@ -473,7 +490,7 @@ open class EditorViewModelTest {
stubOpenPage(root)
stubClosePage(response)
stubInterceptEvents()
buildViewModel()
givenViewModel()
vm.onStart(root)
@ -494,7 +511,7 @@ open class EditorViewModelTest {
val text = MockDataFactory.randomString()
stubInterceptEvents()
buildViewModel()
givenViewModel()
stubOpenPage(context = pageId)
stubUpdateText()
@ -520,7 +537,7 @@ open class EditorViewModelTest {
stubObserveEvents()
stubUpdateText()
stubOpenPage(context = pageId)
buildViewModel()
givenViewModel()
vm.onStart(pageId)
@ -615,7 +632,7 @@ open class EditorViewModelTest {
)
stubOpenPage()
buildViewModel(builder)
givenViewModel(builder)
vm.onStart(root)
coroutineTestRule.advanceTime(200)
@ -681,7 +698,7 @@ open class EditorViewModelTest {
stubCreateBlock(root)
buildViewModel()
givenViewModel()
vm.onStart(root)
@ -751,7 +768,7 @@ open class EditorViewModelTest {
}
stubOpenPage()
buildViewModel(builder)
givenViewModel(builder)
vm.onStart(root)
@ -788,7 +805,7 @@ open class EditorViewModelTest {
stubOpenPage()
stubObserveEvents()
buildViewModel()
givenViewModel()
val testObserver = vm.state.test()
@ -849,7 +866,7 @@ open class EditorViewModelTest {
stubUpdateText()
buildViewModel(builder)
givenViewModel(builder)
vm.onStart(root)
@ -989,7 +1006,7 @@ open class EditorViewModelTest {
stubObserveEvents(events)
stubOpenPage()
buildViewModel(builder)
givenViewModel(builder)
vm.onStart(root)
@ -1133,7 +1150,7 @@ open class EditorViewModelTest {
stubObserveEvents(events)
stubUpdateText()
stubOpenPage()
buildViewModel()
givenViewModel()
vm.onStart(root)
@ -1221,7 +1238,7 @@ open class EditorViewModelTest {
stubObserveEvents(events)
stubOpenPage()
buildViewModel(builder)
givenViewModel(builder)
stubUpdateText()
val testObserver = vm.state.test()
@ -1321,7 +1338,7 @@ open class EditorViewModelTest {
stubObserveEvents(events)
stubOpenPage()
buildViewModel()
givenViewModel()
vm.onStart(root)
@ -1407,7 +1424,7 @@ open class EditorViewModelTest {
stubUpdateText()
buildViewModel()
givenViewModel()
vm.onStart(root)
@ -1461,7 +1478,7 @@ open class EditorViewModelTest {
stubObserveEvents(flow)
stubOpenPage()
buildViewModel()
givenViewModel()
vm.onStart(root)
@ -1543,7 +1560,7 @@ open class EditorViewModelTest {
stubObserveEvents(flow)
stubOpenPage()
buildViewModel(builder)
givenViewModel(builder)
vm.onStart(root)
@ -1616,7 +1633,7 @@ open class EditorViewModelTest {
stubOpenPage()
stubObserveEvents(events)
buildViewModel()
givenViewModel()
stubDuplicateBlock(
newBlockId = MockDataFactory.randomString(),
root = root
@ -1687,7 +1704,7 @@ open class EditorViewModelTest {
stubOpenPage()
stubUnlinkBlocks(root = root)
stubObserveEvents(events)
buildViewModel()
givenViewModel()
// TESTING
@ -1779,7 +1796,7 @@ open class EditorViewModelTest {
stubOpenPage()
stubObserveEvents(events)
buildViewModel(builder)
givenViewModel(builder)
stubUnlinkBlocks(root)
vm.onStart(root)
@ -1882,7 +1899,7 @@ open class EditorViewModelTest {
stubOpenPage()
stubObserveEvents(events)
buildViewModel()
givenViewModel()
stubUnlinkBlocks(root)
vm.onStart(root)
@ -1941,7 +1958,7 @@ open class EditorViewModelTest {
stubOpenPage()
stubObserveEvents(events)
buildViewModel()
givenViewModel()
vm.onStart(root)
@ -1999,7 +2016,7 @@ open class EditorViewModelTest {
stubOpenPage()
stubObserveEvents(events)
stubCreateBlock(root)
buildViewModel()
givenViewModel()
vm.onStart(root)
@ -2056,7 +2073,7 @@ open class EditorViewModelTest {
stubOpenPage()
stubTurnIntoStyle()
buildViewModel()
givenViewModel()
vm.onStart(root)
@ -2111,7 +2128,7 @@ open class EditorViewModelTest {
stubObserveEvents(flow)
stubOpenPage()
buildViewModel()
givenViewModel()
stubUpdateTextColor(root)
@ -2185,7 +2202,7 @@ open class EditorViewModelTest {
stubObserveEvents(flow)
stubOpenPage()
stubCreateBlock(root)
buildViewModel()
givenViewModel()
vm.onStart(root)
@ -2250,7 +2267,7 @@ open class EditorViewModelTest {
stubObserveEvents(flow)
stubOpenPage()
stubUpdateTextStyle()
buildViewModel()
givenViewModel()
vm.onStart(root)
@ -2330,7 +2347,7 @@ open class EditorViewModelTest {
stubObserveEvents(flow)
stubOpenPage()
buildViewModel()
givenViewModel()
stubUpdateText()
stubSplitBlocks(root)
@ -2437,7 +2454,7 @@ open class EditorViewModelTest {
stubObserveEvents(flow)
stubOpenPage()
buildViewModel()
givenViewModel()
stubUpdateText()
stubSplitBlocks(root)
@ -2518,7 +2535,7 @@ open class EditorViewModelTest {
stubObserveEvents(flow)
stubOpenPage()
buildViewModel(builder)
givenViewModel(builder)
stubDownloadFile()
@ -2579,7 +2596,7 @@ open class EditorViewModelTest {
stubObserveEvents(flow)
stubOpenPage(context = root)
buildViewModel()
givenViewModel()
undo.stub {
onBlocking { invoke(any()) } doReturn Either.Right(
@ -2644,7 +2661,7 @@ open class EditorViewModelTest {
stubObserveEvents(flow)
stubOpenPage()
buildViewModel()
givenViewModel()
redo.stub {
onBlocking { invoke(any()) } doReturn Either.Right(
@ -2707,7 +2724,7 @@ open class EditorViewModelTest {
stubArchiveDocument()
buildViewModel()
givenViewModel()
vm.onStart(root)
@ -2762,7 +2779,7 @@ open class EditorViewModelTest {
stubObserveEvents(flow)
stubOpenPage()
stubClosePage()
buildViewModel()
givenViewModel()
vm.onStart(root)
@ -2848,7 +2865,7 @@ open class EditorViewModelTest {
stubObserveEvents(flow)
stubOpenPage()
stubReplaceBlock(root = root)
buildViewModel()
givenViewModel()
stubReplaceBlock(root)
vm.onStart(root)
@ -2926,7 +2943,7 @@ open class EditorViewModelTest {
stubObserveEvents(flow)
stubOpenPage()
buildViewModel()
givenViewModel()
vm.onStart(root)
@ -3007,7 +3024,7 @@ open class EditorViewModelTest {
stubObserveEvents(flow)
stubOpenPage()
buildViewModel()
givenViewModel()
stubUpdateText()
stubReplaceBlock(root)
@ -3067,7 +3084,7 @@ open class EditorViewModelTest {
stubObserveEvents(flow)
stubOpenPage()
buildViewModel()
givenViewModel()
vm.onStart(root)
@ -3189,7 +3206,7 @@ open class EditorViewModelTest {
stubObserveEvents(flow)
stubOpenPage()
buildViewModel()
givenViewModel()
vm.onStart(root)
@ -3356,7 +3373,7 @@ open class EditorViewModelTest {
stubObserveEvents(flow)
stubOpenPage()
buildViewModel()
givenViewModel()
vm.onStart(root)
@ -3450,7 +3467,7 @@ open class EditorViewModelTest {
document = page
)
buildViewModel()
givenViewModel()
vm.onStart(root)
@ -3519,7 +3536,7 @@ open class EditorViewModelTest {
document = page
)
buildViewModel()
givenViewModel()
vm.onStart(root)
@ -3589,7 +3606,7 @@ open class EditorViewModelTest {
document = page
)
buildViewModel()
givenViewModel()
vm.onStart(root)
@ -3796,16 +3813,18 @@ open class EditorViewModelTest {
private fun stubGetDefaultObjectType(type: String? = null, name: String? = null) {
getDefaultEditorType.stub {
onBlocking { invoke(Unit) } doReturn Either.Right(
GetDefaultEditorType.Response(
type,
null
onBlocking { invoke(Unit) } doReturn flow {
emit(
GetDefaultEditorType.Response(
type,
name
)
)
)
}
}
}
fun buildViewModel(urlBuilder: UrlBuilder = builder) {
fun givenViewModel(urlBuilder: UrlBuilder = builder) {
val storage = Editor.Storage()
val proxies = Editor.Proxer()
@ -3889,7 +3908,8 @@ open class EditorViewModelTest {
setDocCoverImage = setDocCoverImage,
setDocImageIcon = setDocImageIcon,
delegator = delegator,
templateDelegate = editorTemplateDelegate
templateDelegate = editorTemplateDelegate,
createNewObject = createNewObject
)
}
@ -3956,7 +3976,7 @@ open class EditorViewModelTest {
stubObserveEvents(flow)
stubOpenPage()
buildViewModel()
givenViewModel()
vm.onStart(root)
@ -4071,7 +4091,7 @@ open class EditorViewModelTest {
stubObserveEvents(flow)
stubOpenPage()
buildViewModel()
givenViewModel()
vm.onStart(root)
@ -4194,7 +4214,7 @@ open class EditorViewModelTest {
stubObserveEvents(flow)
stubOpenPage()
buildViewModel()
givenViewModel()
vm.onStart(root)
@ -4320,7 +4340,7 @@ open class EditorViewModelTest {
stubObserveEvents(flow)
stubOpenPage()
buildViewModel()
givenViewModel()
vm.onStart(root)
@ -4367,4 +4387,32 @@ open class EditorViewModelTest {
coroutineTestRule.advanceTime(200)
}
@Test
fun `should close editor and navigate to page screen - when page is created`() {
val id = MockDataFactory.randomUuid()
stubOpenPage(root)
stubInterceptEvents()
stubClosePage()
givenViewModel()
vm.onStart(root)
givenDelegateId(id)
vm.onAddNewDocumentClicked()
vm.navigation
.test()
.assertHasValue()
.assertValue { value ->
(value.peekContent() as AppNavigation.Command.OpenObject).id == id
}
}
private fun givenDelegateId(id: String) {
createNewObject.stub {
onBlocking { execute(Unit) } doAnswer ValueClassAnswer(id)
}
}
}

View file

@ -17,6 +17,7 @@ import com.anytypeio.anytype.presentation.editor.editor.control.ControlPanelStat
import com.anytypeio.anytype.presentation.editor.editor.mention.MentionConst.MENTION_TITLE_EMPTY
import com.anytypeio.anytype.presentation.editor.editor.mention.MentionEvent
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
import com.anytypeio.anytype.presentation.editor.template.DefaultEditorTemplateDelegate
import com.anytypeio.anytype.presentation.util.CoroutinesTestRule
import com.anytypeio.anytype.presentation.util.TXT
import com.anytypeio.anytype.test_utils.MockDataFactory

View file

@ -66,6 +66,7 @@ import com.anytypeio.anytype.domain.unsplash.DownloadUnsplashImage
import com.anytypeio.anytype.domain.unsplash.UnsplashRepository
import com.anytypeio.anytype.presentation.common.Action
import com.anytypeio.anytype.presentation.common.Delegator
import com.anytypeio.anytype.domain.page.CreateNewObject
import com.anytypeio.anytype.presentation.editor.DocumentExternalEventReducer
import com.anytypeio.anytype.presentation.editor.Editor
import com.anytypeio.anytype.presentation.editor.EditorViewModel
@ -78,11 +79,13 @@ import com.anytypeio.anytype.presentation.editor.toggle.ToggleStateHolder
import com.anytypeio.anytype.presentation.util.CopyFileToCacheDirectory
import com.anytypeio.anytype.presentation.util.Dispatcher
import com.anytypeio.anytype.test_utils.MockDataFactory
import com.anytypeio.anytype.test_utils.ValueClassAnswer
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flowOf
import org.mockito.Mock
import org.mockito.kotlin.any
import org.mockito.kotlin.doAnswer
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.stub
@ -243,6 +246,9 @@ open class EditorPresentationTestSetup {
@Mock
lateinit var editorTemplateDelegate: EditorTemplateDelegate
@Mock
lateinit var createNewObject: CreateNewObject
@Mock
lateinit var getTemplates: GetTemplates
@ -343,7 +349,8 @@ open class EditorPresentationTestSetup {
setDocCoverImage = setDocCoverImage,
setDocImageIcon = setDocImageIcon,
downloadUnsplashImage = downloadUnsplashImage,
templateDelegate = editorTemplateDelegate
templateDelegate = editorTemplateDelegate,
createNewObject = createNewObject
)
}
@ -594,7 +601,12 @@ open class EditorPresentationTestSetup {
fun stubGetDefaultObjectType(type: String? = null, name: String? = null) {
getDefaultEditorType.stub {
onBlocking { invoke(Unit) } doReturn Either.Right(GetDefaultEditorType.Response(type, name))
onBlocking { execute(Unit) } doAnswer ValueClassAnswer(
GetDefaultEditorType.Response(
type,
name
)
)
}
}

View file

@ -79,7 +79,7 @@ class ObjectSetAddOrUpdateViewerTest : ObjectSetViewModelTestSetup() {
)
)
val vm = buildViewModel()
val vm = givenViewModel()
// TESTING
@ -180,7 +180,7 @@ class ObjectSetAddOrUpdateViewerTest : ObjectSetViewModelTestSetup() {
)
)
val vm = buildViewModel()
val vm = givenViewModel()
// TESTING

View file

@ -181,7 +181,7 @@ class ObjectSetCellTest : ObjectSetViewModelTestSetup() {
stubSetActiveViewer()
stubUpdateDataViewViewer()
val vm = buildViewModel()
val vm = givenViewModel()
// TESTING

View file

@ -79,7 +79,7 @@ class ObjectSetHeaderTest : ObjectSetViewModelTestSetup() {
)
)
val vm = buildViewModel()
val vm = givenViewModel()
// TESTING
@ -141,7 +141,7 @@ class ObjectSetHeaderTest : ObjectSetViewModelTestSetup() {
)
)
val vm = buildViewModel()
val vm = givenViewModel()
// TESTING

View file

@ -71,7 +71,7 @@ class ObjectSetInitializationTest : ObjectSetViewModelTestSetup() {
)
}
val vm = buildViewModel()
val vm = givenViewModel()
// TESTING

View file

@ -12,13 +12,20 @@ import com.anytypeio.anytype.presentation.sets.ObjectSetViewModel
import com.anytypeio.anytype.presentation.sets.model.Viewer
import com.anytypeio.anytype.presentation.util.CoroutinesTestRule
import com.anytypeio.anytype.test_utils.MockDataFactory
import com.anytypeio.anytype.test_utils.ValueClassAnswer
import com.jraska.livedata.test
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.runBlocking
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.doAnswer
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.stub
import org.mockito.kotlin.times
import org.mockito.kotlin.verifyBlocking
import kotlin.test.assertEquals
@ -139,7 +146,7 @@ class ObjectSetNavigationTest : ObjectSetViewModelTestSetup() {
)
)
val vm = buildViewModel()
val vm = givenViewModel()
vm.onStart(root)
@ -238,7 +245,7 @@ class ObjectSetNavigationTest : ObjectSetViewModelTestSetup() {
details = details
)
val vm = buildViewModel()
val vm = givenViewModel()
vm.onStart(root)
@ -320,7 +327,7 @@ class ObjectSetNavigationTest : ObjectSetViewModelTestSetup() {
details = details
)
val vm = buildViewModel()
val vm = givenViewModel()
vm.onStart(root)
@ -401,7 +408,7 @@ class ObjectSetNavigationTest : ObjectSetViewModelTestSetup() {
details = details
)
val vm = buildViewModel()
val vm = givenViewModel()
vm.onStart(root)
@ -480,7 +487,7 @@ class ObjectSetNavigationTest : ObjectSetViewModelTestSetup() {
details = details
)
val vm = buildViewModel()
val vm = givenViewModel()
vm.onStart(root)
@ -506,4 +513,32 @@ class ObjectSetNavigationTest : ObjectSetViewModelTestSetup() {
content == AppNavigation.Command.OpenObjectSet(linkedProjectTargetId)
}
}
@Test
fun `should close editor and navigate to page screen - when page is created`() {
val id = MockDataFactory.randomUuid()
stubCloseBlock()
val vm = givenViewModel()
vm.onStart(root)
givenDelegateId(id)
vm.onAddNewDocumentClicked()
vm.navigation
.test()
.assertHasValue()
.assertValue { value ->
(value.peekContent() as AppNavigation.Command.OpenObject).id == id
}
}
private fun givenDelegateId(id: String) {
createNewObject.stub {
onBlocking { execute(Unit) } doAnswer ValueClassAnswer(id)
}
}
}

View file

@ -85,7 +85,7 @@ class ObjectSetRecordCreateTest : ObjectSetViewModelTestSetup() {
)
)
val vm = buildViewModel()
val vm = givenViewModel()
// TESTING
@ -153,7 +153,7 @@ class ObjectSetRecordCreateTest : ObjectSetViewModelTestSetup() {
)
)
val vm = buildViewModel()
val vm = givenViewModel()
// TESTING
@ -229,7 +229,7 @@ class ObjectSetRecordCreateTest : ObjectSetViewModelTestSetup() {
)
)
val vm = buildViewModel()
val vm = givenViewModel()
// TESTING
@ -301,7 +301,7 @@ class ObjectSetRecordCreateTest : ObjectSetViewModelTestSetup() {
)
)
val vm = buildViewModel()
val vm = givenViewModel()
// TESTING

View file

@ -58,7 +58,7 @@ class ObjectSetRestrictionsTest : ObjectSetViewModelTestSetup() {
stubSetActiveViewer()
stubUpdateDataViewViewer()
val vm = buildViewModel()
val vm = givenViewModel()
// TESTING
@ -94,7 +94,7 @@ class ObjectSetRestrictionsTest : ObjectSetViewModelTestSetup() {
stubSetActiveViewer()
stubUpdateDataViewViewer()
val vm = buildViewModel()
val vm = givenViewModel()
// TESTING
@ -130,7 +130,7 @@ class ObjectSetRestrictionsTest : ObjectSetViewModelTestSetup() {
stubSetActiveViewer()
stubUpdateDataViewViewer()
val vm = buildViewModel()
val vm = givenViewModel()
// TESTING
@ -166,7 +166,7 @@ class ObjectSetRestrictionsTest : ObjectSetViewModelTestSetup() {
stubSetActiveViewer()
stubUpdateDataViewViewer()
val vm = buildViewModel()
val vm = givenViewModel()
// TESTING
@ -202,7 +202,7 @@ class ObjectSetRestrictionsTest : ObjectSetViewModelTestSetup() {
stubSetActiveViewer()
stubUpdateDataViewViewer()
val vm = buildViewModel()
val vm = givenViewModel()
// TESTING

View file

@ -109,7 +109,7 @@ class ObjectSetSettingActiveViewerTest : ObjectSetViewModelTestSetup() {
)
}
val vm = buildViewModel()
val vm = givenViewModel()
// TESTING

View file

@ -59,7 +59,7 @@ class ObjectSetUpdateViewerSortTest : ObjectSetViewModelTestSetup() {
stubSetActiveViewer()
stubUpdateDataViewViewer()
val vm = buildViewModel()
val vm = givenViewModel()
// TESTING

View file

@ -29,6 +29,7 @@ import com.anytypeio.anytype.domain.templates.GetTemplates
import com.anytypeio.anytype.domain.unsplash.DownloadUnsplashImage
import com.anytypeio.anytype.presentation.common.Action
import com.anytypeio.anytype.presentation.common.Delegator
import com.anytypeio.anytype.domain.page.CreateNewObject
import com.anytypeio.anytype.presentation.sets.ObjectSetRecordCache
import com.anytypeio.anytype.presentation.sets.ObjectSetReducer
import com.anytypeio.anytype.presentation.sets.ObjectSetSession
@ -92,6 +93,9 @@ open class ObjectSetViewModelTestSetup {
@Mock
lateinit var getTemplates: GetTemplates
@Mock
lateinit var createNewObject: CreateNewObject
val dispatcher = Dispatcher.Default<Payload>()
val delegator = Delegator.Default<Action>()
val reducer = ObjectSetReducer()
@ -101,7 +105,7 @@ open class ObjectSetViewModelTestSetup {
val urlBuilder: UrlBuilder
get() = UrlBuilder(gateway)
fun buildViewModel(): ObjectSetViewModel = ObjectSetViewModel(
fun givenViewModel(): ObjectSetViewModel = ObjectSetViewModel(
openObjectSet = openObjectSet,
closeBlock = closeBlock,
addDataViewRelation = addDataViewRelation,
@ -121,7 +125,8 @@ open class ObjectSetViewModelTestSetup {
analytics = analytics,
downloadUnsplashImage = downloadUnsplashImage,
setDocCoverImage = setDocCoverImage,
getTemplates = getTemplates
getTemplates = getTemplates,
createNewObject = createNewObject
)
fun stubInterceptEvents(

View file

@ -59,7 +59,7 @@ class ObjectSetViewerFilterTest : ObjectSetViewModelTestSetup() {
stubSetActiveViewer()
stubUpdateDataViewViewer()
val vm = buildViewModel()
val vm = givenViewModel()
// TESTING

View file

@ -58,7 +58,7 @@ class ObjectSetZeroDataViewTest : ObjectSetViewModelTestSetup() {
)
)
val vm = buildViewModel()
val vm = givenViewModel()
// TESTING
@ -97,7 +97,7 @@ class ObjectSetZeroDataViewTest : ObjectSetViewModelTestSetup() {
)
)
val vm = buildViewModel()
val vm = givenViewModel()
// TESTING
@ -127,7 +127,7 @@ class ObjectSetZeroDataViewTest : ObjectSetViewModelTestSetup() {
)
)
val vm = buildViewModel()
val vm = givenViewModel()
// TESTING
@ -164,7 +164,7 @@ class ObjectSetZeroDataViewTest : ObjectSetViewModelTestSetup() {
)
)
val vm = buildViewModel()
val vm = givenViewModel()
// TESTING

View file

@ -82,7 +82,7 @@ class ObjectSetZeroViewTest : ObjectSetViewModelTestSetup() {
details = objectSetDetails
)
val vm = buildViewModel()
val vm = givenViewModel()
// TESTING
@ -125,7 +125,7 @@ class ObjectSetZeroViewTest : ObjectSetViewModelTestSetup() {
details = objectSetDetails
)
val vm = buildViewModel()
val vm = givenViewModel()
// TESTING

View file

@ -19,13 +19,19 @@ import com.anytypeio.anytype.domain.launch.SetDefaultEditorType
import com.anytypeio.anytype.domain.misc.AppActionManager
import com.anytypeio.anytype.domain.page.CreatePage
import com.anytypeio.anytype.presentation.util.CoroutinesTestRule
import com.anytypeio.anytype.test_utils.ValueClassAnswer
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.runBlocking
import org.junit.Before
import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.mockito.Mock
import org.mockito.MockitoAnnotations
import org.mockito.internal.stubbing.answers.ThrowsException
import org.mockito.invocation.InvocationOnMock
import org.mockito.kotlin.*
import org.mockito.stubbing.Answer
class SplashViewModelTest {
@ -125,6 +131,7 @@ class SplashViewModelTest {
}
}
@Ignore("https://github.com/mockito/mockito-kotlin/issues/456")
@Test
fun `should invoke checkAuthorizationStatus when getDefaultPageType on error `() {
val status = AuthStatus.AUTHORIZED
@ -135,13 +142,13 @@ class SplashViewModelTest {
stubLaunchAccount()
stubGetLastOpenedObject()
getDefaultEditorType.stub {
onBlocking { invoke(Unit) } doReturn Either.Left(Exception("error"))
onBlocking { execute(Unit) } doAnswer ValueClassAnswer (Exception("error"))
}
initViewModel()
runBlocking {
verify(getDefaultEditorType, times(1)).invoke(any())
verify(getDefaultEditorType, times(1)).execute(any())
verify(checkAuthorizationStatus, times(1)).invoke(any())
}
}
@ -160,7 +167,7 @@ class SplashViewModelTest {
initViewModel()
runBlocking {
verify(getDefaultEditorType, times(1)).invoke(any())
verify(getDefaultEditorType, times(1)).execute(any())
verify(checkAuthorizationStatus, times(1)).invoke(any())
}
}
@ -322,7 +329,12 @@ class SplashViewModelTest {
private fun stubGetDefaultObjectType(type: String? = null, name: String? = null) {
getDefaultEditorType.stub {
onBlocking { invoke(Unit) } doReturn Either.Right(GetDefaultEditorType.Response(type, name))
onBlocking { execute(Unit) } doAnswer ValueClassAnswer(
GetDefaultEditorType.Response(
type,
name
)
)
}
}
}

View file

@ -4,4 +4,5 @@ plugins {
dependencies {
implementation mainApplication.kotlin
implementation unitTesting.mockitoKotlin
}

View file

@ -0,0 +1,8 @@
package com.anytypeio.anytype.test_utils
import org.mockito.invocation.InvocationOnMock
import org.mockito.stubbing.Answer
class ValueClassAnswer(private val value: Any) : Answer<Any> {
override fun answer(invocation: InvocationOnMock?): Any = value
}