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

Feature | Setting cover from Unsplash (without loading state) (#2141)

This commit is contained in:
Evgenii Kozlov 2022-03-21 21:07:31 +03:00 committed by GitHub
parent b24596a26e
commit 1e130241ff
Signed by: github
GPG key ID: 4AEE18F83AFDEB23
53 changed files with 1141 additions and 52 deletions

View file

@ -36,7 +36,9 @@ 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.status.ThreadStatusChannel
import com.anytypeio.anytype.domain.unsplash.DownloadUnsplashImage
import com.anytypeio.anytype.mocking.MockDataFactory
import com.anytypeio.anytype.presentation.common.Delegator
import com.anytypeio.anytype.presentation.editor.DocumentExternalEventReducer
import com.anytypeio.anytype.presentation.editor.Editor
import com.anytypeio.anytype.presentation.editor.EditorViewModelFactory
@ -173,6 +175,8 @@ open class EditorTestSetup {
@Mock
lateinit var objectTypesProvider: ObjectTypesProvider
lateinit var downloadUnsplashImage: DownloadUnsplashImage
val root: String = "rootId123"
private val urlBuilder by lazy {
@ -310,7 +314,10 @@ open class EditorTestSetup {
getDefaultEditorType = getDefaultEditorType,
createObjectSet = createObjectSet,
findObjectSetForType = findObjectSetForType,
copyFileToCacheDirectory = copyFileToCacheDirectory
copyFileToCacheDirectory = copyFileToCacheDirectory,
downloadUnsplashImage = downloadUnsplashImage,
delegator = Delegator.Default(),
setDocCoverImage = setDocCoverImage
)
}

View file

@ -12,6 +12,7 @@ import androidx.test.espresso.matcher.ViewMatchers.*
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import com.anytypeio.anytype.R
import com.anytypeio.anytype.analytics.base.Analytics
import com.anytypeio.anytype.domain.block.repo.BlockRepository
import com.anytypeio.anytype.domain.icon.RemoveDocumentIcon
import com.anytypeio.anytype.domain.icon.SetDocumentEmojiIcon
@ -47,6 +48,9 @@ class DocumentEmojiPickerFragmentTest {
@Mock
lateinit var provider: EmojiProvider
@Mock
lateinit var analytics: Analytics
@Mock
lateinit var repo: BlockRepository
@ -67,7 +71,8 @@ class DocumentEmojiPickerFragmentTest {
setEmojiIcon = setEmojiIcon,
setImageIcon = setImageIcon,
removeDocumentIcon = removeDocumentIcon,
dispatcher = Dispatcher.Default()
dispatcher = Dispatcher.Default(),
analytics = analytics
)
}

View file

@ -7,6 +7,7 @@ import androidx.fragment.app.testing.launchFragmentInContainer
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import com.anytypeio.anytype.R
import com.anytypeio.anytype.analytics.base.Analytics
import com.anytypeio.anytype.core_models.*
import com.anytypeio.anytype.domain.`object`.ObjectTypesProvider
import com.anytypeio.anytype.domain.`object`.UpdateDetail
@ -53,6 +54,9 @@ class AddRelationStatusValueTest {
@Mock
lateinit var dispatcher: Dispatcher<Payload>
@Mock
lateinit var analytics: Analytics
private lateinit var addRelationOption: AddDataViewRelationOption
private lateinit var addObjectRelationOption: AddObjectRelationOption
private lateinit var removeTagFromDataViewRecord: RemoveTagFromDataViewRecord
@ -96,6 +100,7 @@ class AddRelationStatusValueTest {
addStatusToDataViewRecord = addStatusToDataViewRecord,
urlBuilder = urlBuilder,
dispatcher = dispatcher,
analytics = analytics
)
}

View file

@ -7,6 +7,7 @@ import androidx.fragment.app.testing.launchFragmentInContainer
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import com.anytypeio.anytype.R
import com.anytypeio.anytype.analytics.base.Analytics
import com.anytypeio.anytype.core_models.*
import com.anytypeio.anytype.domain.`object`.ObjectTypesProvider
import com.anytypeio.anytype.domain.`object`.UpdateDetail
@ -53,6 +54,9 @@ class AddRelationTagValueTest {
@Mock
lateinit var dispatcher: Dispatcher<Payload>
@Mock
lateinit var analytics: Analytics
private lateinit var addRelationOption: AddDataViewRelationOption
private lateinit var addObjectRelationOption: AddObjectRelationOption
private lateinit var removeTagFromDataViewRecord: RemoveTagFromDataViewRecord
@ -95,7 +99,8 @@ class AddRelationTagValueTest {
addTagToDataViewRecord = addTagToDataViewRecord,
addStatusToDataViewRecord = addStatusToDataViewRecord,
urlBuilder = urlBuilder,
dispatcher = dispatcher
dispatcher = dispatcher,
analytics = analytics
)
}

View file

@ -10,6 +10,7 @@ import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import com.anytypeio.anytype.R
import com.anytypeio.anytype.analytics.base.Analytics
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.Payload
@ -65,6 +66,9 @@ class ObjectRelationListTest {
@Mock
lateinit var detailModificationManager: DetailModificationManager
@Mock
lateinit var analytics: Analytics
private lateinit var objectRelationList: ObjectRelationList
private lateinit var updateDetail: UpdateDetail
private lateinit var addToFeaturedRelations: AddToFeaturedRelations
@ -94,7 +98,8 @@ class ObjectRelationListTest {
updateDetail = updateDetail,
addToFeaturedRelations = addToFeaturedRelations,
removeFromFeaturedRelations = removeFromFeaturedRelations,
deleteRelationFromObject = deleteRelationFromObject
deleteRelationFromObject = deleteRelationFromObject,
analytics = analytics
)
}

View file

@ -11,6 +11,7 @@ import com.anytypeio.anytype.domain.base.Result
import com.anytypeio.anytype.domain.block.interactor.UpdateText
import com.anytypeio.anytype.domain.block.repo.BlockRepository
import com.anytypeio.anytype.domain.config.Gateway
import com.anytypeio.anytype.domain.cover.SetDocCoverImage
import com.anytypeio.anytype.domain.dataview.interactor.*
import com.anytypeio.anytype.domain.event.interactor.InterceptEvents
import com.anytypeio.anytype.domain.misc.UrlBuilder
@ -18,8 +19,12 @@ import com.anytypeio.anytype.domain.page.CloseBlock
import com.anytypeio.anytype.domain.sets.OpenObjectSet
import com.anytypeio.anytype.domain.status.InterceptThreadStatus
import com.anytypeio.anytype.domain.status.ThreadStatusChannel
import com.anytypeio.anytype.domain.unsplash.DownloadUnsplashImage
import com.anytypeio.anytype.domain.unsplash.UnsplashRepository
import com.anytypeio.anytype.emojifier.data.DefaultDocumentEmojiIconProvider
import com.anytypeio.anytype.mocking.MockDataFactory
import com.anytypeio.anytype.presentation.common.Action
import com.anytypeio.anytype.presentation.common.Delegator
import com.anytypeio.anytype.presentation.sets.ObjectSetRecordCache
import com.anytypeio.anytype.presentation.sets.ObjectSetReducer
import com.anytypeio.anytype.presentation.sets.ObjectSetSession
@ -43,12 +48,17 @@ abstract class TestObjectSetSetup {
private lateinit var closeBlock: CloseBlock
private lateinit var setActiveViewer: SetActiveViewer
private lateinit var interceptThreadStatus: InterceptThreadStatus
private lateinit var setDocCoverImage: SetDocCoverImage
private lateinit var downloadUnsplashImage: DownloadUnsplashImage
lateinit var urlBuilder: UrlBuilder
@Mock
lateinit var repo: BlockRepository
@Mock
lateinit var unsplashRepo: UnsplashRepository
@Mock
lateinit var auth: AuthRepository
@ -89,6 +99,8 @@ abstract class TestObjectSetSetup {
)
)
val delegator = Delegator.Default<Action>()
open fun setup() {
MockitoAnnotations.openMocks(this)
@ -102,6 +114,8 @@ abstract class TestObjectSetSetup {
interceptThreadStatus = InterceptThreadStatus(channel = threadStatusChannel)
closeBlock = CloseBlock(repo)
urlBuilder = UrlBuilder(gateway)
downloadUnsplashImage = DownloadUnsplashImage(unsplashRepo)
setDocCoverImage = SetDocCoverImage(repo)
TestObjectSetFragment.testVmFactory = ObjectSetViewModelFactory(
openObjectSet = openObjectSet,
@ -119,7 +133,10 @@ abstract class TestObjectSetSetup {
dispatcher = dispatcher,
reducer = reducer,
objectSetRecordCache = objectSetRecordCache,
analytics = analytics
analytics = analytics,
downloadUnsplashImage = downloadUnsplashImage,
setDocCoverImage = setDocCoverImage,
delegator = delegator
)
}

View file

@ -8,6 +8,7 @@ import androidx.test.espresso.Espresso.onView
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import com.anytypeio.anytype.R
import com.anytypeio.anytype.analytics.base.Analytics
import com.anytypeio.anytype.core_models.*
import com.anytypeio.anytype.domain.`object`.ObjectTypesProvider
import com.anytypeio.anytype.domain.block.repo.BlockRepository
@ -51,6 +52,9 @@ class CreateSelectedFilterTest {
@Mock
lateinit var objectTypesProvider: ObjectTypesProvider
@Mock
lateinit var analytics: Analytics
lateinit var updateDataViewViewer: UpdateDataViewViewer
lateinit var searchObjects: SearchObjects
lateinit var urlBuilder: UrlBuilder
@ -73,7 +77,8 @@ class CreateSelectedFilterTest {
urlBuilder = urlBuilder,
searchObjects = searchObjects,
objectSetState = state,
objectTypesProvider = objectTypesProvider
objectTypesProvider = objectTypesProvider,
analytics = analytics
)
}

View file

@ -8,6 +8,7 @@ import androidx.test.espresso.Espresso.onView
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import com.anytypeio.anytype.R
import com.anytypeio.anytype.analytics.base.Analytics
import com.anytypeio.anytype.core_models.*
import com.anytypeio.anytype.domain.block.repo.BlockRepository
import com.anytypeio.anytype.domain.config.Gateway
@ -50,6 +51,9 @@ class FilterListTest {
@Mock
lateinit var gateway: Gateway
@Mock
lateinit var analytics: Analytics
lateinit var updateDataViewViewer: UpdateDataViewViewer
lateinit var searchObjects: SearchObjects
lateinit var urlBuilder: UrlBuilder
@ -70,7 +74,8 @@ class FilterListTest {
updateDataViewViewer = updateDataViewViewer,
dispatcher = dispatcher,
urlBuilder = urlBuilder,
state = state
state = state,
analytics = analytics
)
}

View file

@ -12,6 +12,7 @@ import androidx.test.espresso.matcher.ViewMatchers.*
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import com.anytypeio.anytype.R
import com.anytypeio.anytype.analytics.base.Analytics
import com.anytypeio.anytype.core_models.*
import com.anytypeio.anytype.domain.`object`.ObjectTypesProvider
import com.anytypeio.anytype.domain.block.repo.BlockRepository
@ -57,6 +58,9 @@ class ModifyInputValueFilterTest {
@Mock
lateinit var gateway: Gateway
@Mock
lateinit var analytics: Analytics
lateinit var updateDataViewViewer: UpdateDataViewViewer
lateinit var searchObjects: SearchObjects
lateinit var urlBuilder: UrlBuilder
@ -79,7 +83,8 @@ class ModifyInputValueFilterTest {
dispatcher = dispatcher,
searchObjects = searchObjects,
urlBuilder = urlBuilder,
objectTypesProvider = objectTypesProvider
objectTypesProvider = objectTypesProvider,
analytics = analytics
)
}

View file

@ -11,6 +11,7 @@ import androidx.test.espresso.matcher.ViewMatchers.*
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import com.anytypeio.anytype.R
import com.anytypeio.anytype.analytics.base.Analytics
import com.anytypeio.anytype.core_models.*
import com.anytypeio.anytype.domain.`object`.ObjectTypesProvider
import com.anytypeio.anytype.domain.block.repo.BlockRepository
@ -56,6 +57,9 @@ class ModifyStatusFilterTest {
@Mock
lateinit var objectTypesProvider: ObjectTypesProvider
@Mock
lateinit var analytics: Analytics
lateinit var updateDataViewViewer: UpdateDataViewViewer
lateinit var searchObjects: SearchObjects
lateinit var urlBuilder: UrlBuilder
@ -78,7 +82,8 @@ class ModifyStatusFilterTest {
dispatcher = dispatcher,
searchObjects = searchObjects,
urlBuilder = urlBuilder,
objectTypesProvider = objectTypesProvider
objectTypesProvider = objectTypesProvider,
analytics = analytics
)
}

View file

@ -11,6 +11,7 @@ import androidx.test.espresso.matcher.ViewMatchers.*
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import com.anytypeio.anytype.R
import com.anytypeio.anytype.analytics.base.Analytics
import com.anytypeio.anytype.core_models.*
import com.anytypeio.anytype.domain.`object`.ObjectTypesProvider
import com.anytypeio.anytype.domain.block.repo.BlockRepository
@ -54,6 +55,9 @@ class ModifyTagFilterTest {
@Mock
lateinit var objectTypesProvider: ObjectTypesProvider
@Mock
lateinit var analytics: Analytics
lateinit var updateDataViewViewer: UpdateDataViewViewer
lateinit var searchObjects: SearchObjects
lateinit var urlBuilder: UrlBuilder
@ -76,7 +80,8 @@ class ModifyTagFilterTest {
dispatcher = dispatcher,
searchObjects = searchObjects,
urlBuilder = urlBuilder,
objectTypesProvider = objectTypesProvider
objectTypesProvider = objectTypesProvider,
analytics = analytics
)
}

View file

@ -10,6 +10,7 @@ import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import com.anytypeio.anytype.R
import com.anytypeio.anytype.analytics.base.Analytics
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.DVSort
import com.anytypeio.anytype.core_models.Payload
@ -47,6 +48,9 @@ class ViewerObjectSortTest {
@Mock
lateinit var repo: BlockRepository
@Mock
lateinit var analytics: Analytics
private lateinit var updateDataViewViewer: UpdateDataViewViewer
private val root = MockDataFactory.randomUuid()
@ -62,7 +66,8 @@ class ViewerObjectSortTest {
state = state,
session = session,
updateDataViewViewer = updateDataViewViewer,
dispatcher = dispatcher
dispatcher = dispatcher,
analytics = analytics
)
}

View file

@ -2,6 +2,7 @@ package com.anytypeio.anytype.di.common
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.di.feature.*
import com.anytypeio.anytype.di.feature.cover.UnsplashModule
import com.anytypeio.anytype.di.feature.relations.*
import com.anytypeio.anytype.di.feature.sets.CreateFilterModule
import com.anytypeio.anytype.di.feature.sets.ModifyFilterModule
@ -522,6 +523,22 @@ class ComponentManager(private val main: MainComponent) {
.build()
}
val objectUnsplashComponent = DependentComponentMap { ctx ->
editorComponent
.get(ctx)
.objectUnsplashComponent()
.module(UnsplashModule)
.build()
}
val objectSetUnsplashComponent = DependentComponentMap { ctx ->
objectSetComponent
.get(ctx)
.objectUnsplashComponent()
.module(UnsplashModule)
.build()
}
val objectSetCoverComponent = DependentComponentMap { ctx ->
objectSetComponent
.get(ctx)

View file

@ -7,6 +7,7 @@ import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.ObjectType
import com.anytypeio.anytype.core_models.Payload
import com.anytypeio.anytype.core_utils.di.scope.PerScreen
import com.anytypeio.anytype.di.feature.cover.UnsplashSubComponent
import com.anytypeio.anytype.di.feature.relations.RelationAddToObjectSubComponent
import com.anytypeio.anytype.di.feature.relations.RelationCreateFromScratchForObjectBlockSubComponent
import com.anytypeio.anytype.di.feature.relations.RelationCreateFromScratchForObjectSubComponent
@ -22,6 +23,7 @@ import com.anytypeio.anytype.domain.clipboard.Clipboard
import com.anytypeio.anytype.domain.clipboard.Copy
import com.anytypeio.anytype.domain.clipboard.Paste
import com.anytypeio.anytype.domain.config.UserSettingsRepository
import com.anytypeio.anytype.domain.cover.SetDocCoverImage
import com.anytypeio.anytype.domain.dataview.interactor.GetCompatibleObjectTypes
import com.anytypeio.anytype.domain.dataview.interactor.SearchObjects
import com.anytypeio.anytype.domain.dataview.interactor.SetRelationKey
@ -40,6 +42,10 @@ import com.anytypeio.anytype.domain.relations.AddFileToObject
import com.anytypeio.anytype.domain.sets.FindObjectSetForType
import com.anytypeio.anytype.domain.status.InterceptThreadStatus
import com.anytypeio.anytype.domain.status.ThreadStatusChannel
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.presentation.editor.DocumentExternalEventReducer
import com.anytypeio.anytype.presentation.editor.Editor
import com.anytypeio.anytype.presentation.editor.EditorViewModelFactory
@ -89,6 +95,7 @@ interface EditorSubComponent {
fun editRelationDateComponent(): RelationDataValueSubComponent.Builder
fun objectCoverComponent() : SelectCoverObjectSubComponent.Builder
fun objectUnsplashComponent() : UnsplashSubComponent.Builder
fun objectMenuComponent() : ObjectMenuComponent.Builder
fun objectLayoutComponent() : ObjectLayoutSubComponent.Builder
@ -142,6 +149,7 @@ object EditorSessionModule {
orchestrator: Orchestrator,
analytics: Analytics,
dispatcher: Dispatcher<Payload>,
delegator: Delegator<Action>,
detailModificationManager: DetailModificationManager,
updateDetail: UpdateDetail,
getCompatibleObjectTypes: GetCompatibleObjectTypes,
@ -149,7 +157,9 @@ object EditorSessionModule {
searchObjects: SearchObjects,
getDefaultEditorType: GetDefaultEditorType,
findObjectSetForType: FindObjectSetForType,
copyFileToCacheDirectory: CopyFileToCacheDirectory
copyFileToCacheDirectory: CopyFileToCacheDirectory,
downloadUnsplashImage: DownloadUnsplashImage,
setDocCoverImage: SetDocCoverImage
): EditorViewModelFactory = EditorViewModelFactory(
openPage = openPage,
closeObject = closePage,
@ -168,6 +178,7 @@ object EditorSessionModule {
orchestrator = orchestrator,
analytics = analytics,
dispatcher = dispatcher,
delegator = delegator,
detailModificationManager = detailModificationManager,
updateDetail = updateDetail,
getCompatibleObjectTypes = getCompatibleObjectTypes,
@ -176,7 +187,9 @@ object EditorSessionModule {
getDefaultEditorType = getDefaultEditorType,
findObjectSetForType = findObjectSetForType,
createObjectSet = createObjectSet,
copyFileToCacheDirectory = copyFileToCacheDirectory
copyFileToCacheDirectory = copyFileToCacheDirectory,
downloadUnsplashImage = downloadUnsplashImage,
setDocCoverImage = setDocCoverImage
)
@JvmStatic
@ -689,6 +702,11 @@ object EditorUseCaseModule {
@PerScreen
fun providePayloadDispatcher() : Dispatcher<Payload> = Dispatcher.Default()
@JvmStatic
@Provides
@PerScreen
fun provideDelegator() : Delegator<Action> = Delegator.Default()
@JvmStatic
@Provides
@PerScreen
@ -771,4 +789,18 @@ object EditorUseCaseModule {
fun provideCopyFileToCache(
context: Context
): CopyFileToCacheDirectory = DefaultCopyFileToCacheDirectory(context)
@JvmStatic
@Provides
@PerScreen
fun provideDownload(repo: UnsplashRepository): DownloadUnsplashImage = DownloadUnsplashImage(
repo = repo
)
@JvmStatic
@Provides
@PerScreen
fun provideSetDocCoverImageUseCase(
repo: BlockRepository
): SetDocCoverImage = SetDocCoverImage(repo)
}

View file

@ -6,6 +6,7 @@ import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.ObjectType
import com.anytypeio.anytype.core_models.Payload
import com.anytypeio.anytype.core_utils.di.scope.PerScreen
import com.anytypeio.anytype.di.feature.cover.UnsplashSubComponent
import com.anytypeio.anytype.di.feature.relations.RelationAddToDataViewSubComponent
import com.anytypeio.anytype.di.feature.relations.RelationCreateFromScratchForDataViewSubComponent
import com.anytypeio.anytype.di.feature.sets.CreateFilterSubComponent
@ -17,6 +18,7 @@ import com.anytypeio.anytype.domain.`object`.UpdateDetail
import com.anytypeio.anytype.domain.auth.repo.AuthRepository
import com.anytypeio.anytype.domain.block.interactor.UpdateText
import com.anytypeio.anytype.domain.block.repo.BlockRepository
import com.anytypeio.anytype.domain.cover.SetDocCoverImage
import com.anytypeio.anytype.domain.dataview.interactor.*
import com.anytypeio.anytype.domain.event.interactor.EventChannel
import com.anytypeio.anytype.domain.event.interactor.InterceptEvents
@ -28,6 +30,10 @@ import com.anytypeio.anytype.domain.relations.DeleteRelationFromDataView
import com.anytypeio.anytype.domain.sets.OpenObjectSet
import com.anytypeio.anytype.domain.status.InterceptThreadStatus
import com.anytypeio.anytype.domain.status.ThreadStatusChannel
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.presentation.relations.providers.*
import com.anytypeio.anytype.presentation.sets.*
import com.anytypeio.anytype.presentation.util.Dispatcher
@ -76,6 +82,7 @@ interface ObjectSetSubComponent {
fun objectSetMenuComponent() : ObjectSetMenuComponent.Builder
fun objectSetIconPickerComponent() : ObjectSetIconPickerComponent.Builder
fun objectSetCoverComponent() : SelectCoverObjectSetSubComponent.Builder
fun objectUnsplashComponent() : UnsplashSubComponent.Builder
}
@Module
@ -97,10 +104,13 @@ object ObjectSetModule {
createDataViewRecord: CreateDataViewRecord,
reducer: ObjectSetReducer,
dispatcher: Dispatcher<Payload>,
delegator: Delegator<Action>,
objectSetRecordCache: ObjectSetRecordCache,
urlBuilder: UrlBuilder,
session: ObjectSetSession,
analytics: Analytics
analytics: Analytics,
downloadUnsplashImage: DownloadUnsplashImage,
setDocCoverImage: SetDocCoverImage
): ObjectSetViewModelFactory = ObjectSetViewModelFactory(
openObjectSet = openObjectSet,
closeBlock = closeBlock,
@ -114,10 +124,13 @@ object ObjectSetModule {
interceptThreadStatus = interceptThreadStatus,
reducer = reducer,
dispatcher = dispatcher,
delegator = delegator,
objectSetRecordCache = objectSetRecordCache,
urlBuilder = urlBuilder,
session = session,
analytics = analytics
analytics = analytics,
downloadUnsplashImage = downloadUnsplashImage,
setDocCoverImage = setDocCoverImage
)
@JvmStatic
@ -218,6 +231,11 @@ object ObjectSetModule {
@PerScreen
fun provideDispatcher(): Dispatcher<Payload> = Dispatcher.Default()
@JvmStatic
@Provides
@PerScreen
fun provideDelegator() : Delegator<Action> = Delegator.Default()
@JvmStatic
@Provides
@PerScreen
@ -292,4 +310,18 @@ object ObjectSetModule {
fun provideDeleteRelationFromDataViewUseCase(
repo: BlockRepository
): DeleteRelationFromDataView = DeleteRelationFromDataView(repo = repo)
@JvmStatic
@Provides
@PerScreen
fun provideSetDocCoverImageUseCase(
repo: BlockRepository
): SetDocCoverImage = SetDocCoverImage(repo)
@JvmStatic
@Provides
@PerScreen
fun provideDownload(repo: UnsplashRepository): DownloadUnsplashImage = DownloadUnsplashImage(
repo = repo
)
}

View file

@ -45,13 +45,6 @@ object SelectCoverObjectModule {
repo: BlockRepository
): SetDocCoverGradient = SetDocCoverGradient(repo)
@JvmStatic
@Provides
@PerModal
fun provideSetDocCoverImageUseCase(
repo: BlockRepository
): SetDocCoverImage = SetDocCoverImage(repo)
@JvmStatic
@Provides
@PerModal
@ -118,13 +111,6 @@ object SelectCoverObjectSetModule {
repo: BlockRepository
): SetDocCoverGradient = SetDocCoverGradient(repo)
@JvmStatic
@Provides
@PerModal
fun provideSetDocCoverImageUseCase(
repo: BlockRepository
): SetDocCoverImage = SetDocCoverImage(repo)
@JvmStatic
@Provides
@PerModal

View file

@ -0,0 +1,58 @@
package com.anytypeio.anytype.di.feature.cover
import com.anytypeio.anytype.core_utils.di.scope.PerDialog
import com.anytypeio.anytype.domain.block.repo.BlockRepository
import com.anytypeio.anytype.domain.cover.SetDocCoverImage
import com.anytypeio.anytype.domain.unsplash.SearchUnsplashImage
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.presentation.editor.cover.UnsplashViewModel
import com.anytypeio.anytype.ui.editor.cover.UnsplashBaseFragment
import dagger.Module
import dagger.Provides
import dagger.Subcomponent
@Subcomponent(modules = [UnsplashModule::class])
@PerDialog
interface UnsplashSubComponent {
@Subcomponent.Builder
interface Builder {
fun module(module: UnsplashModule): Builder
fun build(): UnsplashSubComponent
}
fun inject(fragment: UnsplashBaseFragment)
}
@Module
object UnsplashModule {
@JvmStatic
@Provides
@PerDialog
fun provideViewModelFactory(
search: SearchUnsplashImage,
delegator: Delegator<Action>
): UnsplashViewModel.Factory {
return UnsplashViewModel.Factory(
search = search,
delegator = delegator
)
}
@JvmStatic
@Provides
@PerDialog
fun provideSearch(repo: UnsplashRepository): SearchUnsplashImage = SearchUnsplashImage(
repo = repo
)
@JvmStatic
@Provides
@PerDialog
fun provideSetDocCoverImageUseCase(
repo: BlockRepository
): SetDocCoverImage = SetDocCoverImage(repo)
}

View file

@ -14,6 +14,8 @@ import com.anytypeio.anytype.data.auth.repo.block.BlockDataStoreFactory
import com.anytypeio.anytype.data.auth.repo.block.BlockRemote
import com.anytypeio.anytype.data.auth.repo.block.BlockRemoteDataStore
import com.anytypeio.anytype.data.auth.repo.config.Configurator
import com.anytypeio.anytype.data.auth.repo.unsplash.UnsplashDataRepository
import com.anytypeio.anytype.data.auth.repo.unsplash.UnsplashRemote
import com.anytypeio.anytype.data.auth.types.DefaultObjectTypesProvider
import com.anytypeio.anytype.device.DefaultPathProvider
import com.anytypeio.anytype.domain.`object`.ObjectTypesProvider
@ -26,7 +28,9 @@ import com.anytypeio.anytype.domain.device.PathProvider
import com.anytypeio.anytype.domain.misc.AppActionManager
import com.anytypeio.anytype.domain.objects.DefaultObjectStore
import com.anytypeio.anytype.domain.objects.ObjectStore
import com.anytypeio.anytype.domain.unsplash.UnsplashRepository
import com.anytypeio.anytype.middleware.EventProxy
import com.anytypeio.anytype.middleware.UnsplashMiddleware
import com.anytypeio.anytype.middleware.auth.AuthMiddleware
import com.anytypeio.anytype.middleware.block.BlockMiddleware
import com.anytypeio.anytype.middleware.interactor.Middleware
@ -278,4 +282,26 @@ object DataModule {
@Provides
@Singleton
fun provideObjectStore() : ObjectStore = DefaultObjectStore()
//region Unsplash
@JvmStatic
@Provides
@Singleton
fun provideUnsplashRepo(
remote: UnsplashRemote
) : UnsplashRepository = UnsplashDataRepository(
remote = remote
)
@JvmStatic
@Provides
@Singleton
fun provideUnsplashRemote(
service: MiddlewareService
) : UnsplashRemote = UnsplashMiddleware(
service = service
)
//endregion
}

View file

@ -71,11 +71,22 @@ abstract class SelectCoverGalleryFragment : BaseBottomSheetFragment<FragmentDocC
.onEach { vm.onRemoveCover(ctx) }
.launchIn(lifecycleScope)
binding.btnUnsplash.clicks()
.onEach {
findNavController().navigate(
R.id.objectCoverUnsplashScreen,
bundleOf(
UnsplashBaseFragment.CTX_KEY to ctx
)
)
}
.launchIn(lifecycleScope)
binding.btnUpload.clicks()
.onEach { proceedWithImagePick() }
.launchIn(lifecycleScope)
val spacing = requireContext().dimen(R.dimen.cover_gallery_item_spacing).toInt() / 2
val spacing = requireContext().dimen(R.dimen.cover_gallery_item_spacing).toInt()
binding.docCoverGalleryRecycler.apply {
adapter = docCoverGalleryAdapter
@ -113,6 +124,7 @@ abstract class SelectCoverGalleryFragment : BaseBottomSheetFragment<FragmentDocC
jobs += subscribe(vm.toasts) { toast(it) }
}
super.onStart()
expand()
}
private fun proceedWithImagePick() {
@ -145,6 +157,8 @@ abstract class SelectCoverGalleryFragment : BaseBottomSheetFragment<FragmentDocC
inflater, container, false
)
abstract fun onUnsplashClicked()
companion object {
private const val SELECT_IMAGE_CODE = 1
private const val REQUEST_PERMISSION_CODE = 2
@ -175,6 +189,15 @@ class SelectCoverObjectFragment : SelectCoverGalleryFragment() {
lateinit var factory: SelectCoverObjectViewModel.Factory
override val vm by viewModels<SelectCoverObjectViewModel> { factory }
override fun onUnsplashClicked() {
findNavController().navigate(
R.id.objectCoverUnsplashScreen,
bundleOf(
UnsplashBaseFragment.CTX_KEY to ctx
)
)
}
override fun injectDependencies() {
componentManager().objectCoverComponent.get(ctx).inject(this)
}
@ -208,6 +231,15 @@ class SelectCoverObjectSetFragment : SelectCoverGalleryFragment() {
componentManager().objectSetCoverComponent.release(ctx)
}
override fun onUnsplashClicked() {
findNavController().navigate(
R.id.objectCoverUnsplashScreen,
bundleOf(
UnsplashBaseFragment.CTX_KEY to ctx
)
)
}
companion object {
fun new(ctx: Id) = SelectCoverObjectSetFragment().apply {
arguments = bundleOf(CTX_KEY to ctx)

View file

@ -0,0 +1,140 @@
package com.anytypeio.anytype.ui.editor.cover
import android.graphics.Rect
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.widget.doAfterTextChanged
import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_ui.features.cover.UnsplashImageAdapter
import com.anytypeio.anytype.core_ui.features.editor.modal.DocCoverGalleryAdapter
import com.anytypeio.anytype.core_utils.ext.arg
import com.anytypeio.anytype.core_utils.ext.dimen
import com.anytypeio.anytype.core_utils.ext.invisible
import com.anytypeio.anytype.core_utils.ext.visible
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetFragment
import com.anytypeio.anytype.databinding.FragmentUnsplashBinding
import com.anytypeio.anytype.di.common.componentManager
import com.anytypeio.anytype.presentation.editor.cover.UnsplashViewModel
import kotlinx.coroutines.launch
import javax.inject.Inject
abstract class UnsplashBaseFragment : BaseBottomSheetFragment<FragmentUnsplashBinding>() {
val ctx get() = arg<String>(CTX_KEY)
@Inject
lateinit var factory: UnsplashViewModel.Factory
private val vm by viewModels<UnsplashViewModel> { factory }
private val unsplashImageAdapter by lazy {
UnsplashImageAdapter(
onImageClicked = { img ->
vm.onImageSelected(ctx = ctx, img = img)
}
)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val spacing = requireContext().dimen(R.dimen.cover_gallery_item_spacing).toInt()
binding.unsplashRecycler.apply {
adapter = unsplashImageAdapter
layoutManager = GridLayoutManager(context, 2)
addItemDecoration(
object : RecyclerView.ItemDecoration() {
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
val position = parent.getChildAdapterPosition(view)
val holder = parent.findViewHolderForLayoutPosition(position)
if (holder !is DocCoverGalleryAdapter.ViewHolder.Header) {
outRect.left = spacing
outRect.right = spacing
outRect.top = spacing * 2
outRect.bottom = 0
}
}
}
)
}
binding.searchToolbar.binding.filterInputField.doAfterTextChanged {
vm.onQueryChanged(it.toString())
}
lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
launch {
vm.isCompleted.collect { isCompleted ->
if (isCompleted) onCompleted()
}
}
launch {
vm.images.collect { unsplashImageAdapter.submitList(it) }
}
launch {
vm.isLoading.collect { isLoading ->
if (isLoading)
binding.searchToolbar.binding.progressBar.visible()
else
binding.searchToolbar.binding.progressBar.invisible()
}
}
}
}
}
abstract fun onCompleted()
override fun inflateBinding(
inflater: LayoutInflater,
container: ViewGroup?
): FragmentUnsplashBinding = FragmentUnsplashBinding.inflate(
inflater, container, false
)
companion object {
const val CTX_KEY = "arg.object.cover.unsplash.ctx"
}
}
class ObjectUnsplashFragment: UnsplashBaseFragment() {
override fun injectDependencies() {
componentManager().objectUnsplashComponent.get(ctx).inject(this)
}
override fun releaseDependencies() {
componentManager().objectUnsplashComponent.release(ctx)
}
override fun onCompleted() {
findNavController().popBackStack(
R.id.pageScreen,
false
)
}
}
class ObjectSetUnsplashFragment : UnsplashBaseFragment() {
override fun onCompleted() {
findNavController().popBackStack(
R.id.objectSetScreen,
false
)
}
override fun injectDependencies() {
componentManager().objectSetUnsplashComponent.get(ctx).inject(this)
}
override fun releaseDependencies() {
componentManager().objectSetUnsplashComponent.release(ctx)
}
}

View file

@ -46,11 +46,11 @@
android:id="@+id/docCoverGalleryRecycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingStart="@dimen/dp_8"
android:paddingEnd="@dimen/dp_8"
android:layout_weight="1"
android:clipToPadding="false"
android:minHeight="300dp"
android:paddingStart="@dimen/dp_8"
android:paddingEnd="@dimen/dp_8"
android:paddingBottom="@dimen/dp_16" />
<LinearLayout
@ -60,23 +60,16 @@
android:orientation="horizontal">
<TextView
android:id="@+id/btnGallery"
android:id="@+id/btnUnsplash"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:fontFamily="@font/inter_regular"
android:gravity="center"
android:textColor="@color/black"
android:textSize="17sp" />
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:fontFamily="@font/inter_regular"
android:gravity="center"
android:text=""
android:textColor="#ACA996"
android:text="@string/unsplash"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:textColor="@color/glyph_active"
android:textSize="17sp" />
<TextView
@ -85,9 +78,11 @@
android:layout_height="match_parent"
android:layout_weight="1"
android:fontFamily="@font/inter_regular"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:gravity="center"
android:text="@string/btn_upload"
android:textColor="#ACA996"
android:textColor="@color/glyph_active"
android:textSize="17sp" />
</LinearLayout>

View file

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".ui.editor.cover.SelectCoverGalleryFragment">
<View
android:id="@+id/dragger"
android:layout_width="48dp"
android:layout_height="4dp"
android:layout_gravity="center_horizontal"
android:layout_marginTop="6dp"
android:background="@drawable/page_icon_picker_dragger_background" />
<FrameLayout
android:id="@+id/headerToolbar"
android:layout_width="match_parent"
android:layout_height="@dimen/default_toolbar_height"
android:layout_marginTop="@dimen/dp_6">
<TextView
style="@style/CoverModalTitleStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:text="@string/unsplash" />
</FrameLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="@dimen/default_toolbar_height">
<com.anytypeio.anytype.core_ui.widgets.DefaultSearchToolbar
android:id="@+id/searchToolbar"
android:layout_width="match_parent"
android:layout_height="@dimen/default_search_toolbar_clip_height"
android:layout_marginStart="@dimen/dp_16"
android:layout_marginEnd="@dimen/dp_16"
android:layout_gravity="center_vertical"/>
</FrameLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/unsplashRecycler"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:clipToPadding="false"
android:paddingStart="@dimen/dp_8"
android:paddingEnd="@dimen/dp_8"
android:paddingBottom="@dimen/dp_16" />
</LinearLayout>

View file

@ -55,6 +55,10 @@
android:id="@+id/objectCoverScreen"
android:name="com.anytypeio.anytype.ui.editor.cover.SelectCoverObjectFragment"
android:label="Object-Cover-Screen"/>
<dialog
android:id="@+id/objectCoverUnsplashScreen"
android:name="com.anytypeio.anytype.ui.editor.cover.ObjectUnsplashFragment"
android:label="Object-Cover-Unsplash-Screen"/>
<dialog
android:id="@+id/relationAddToObjectBlockFragment"
android:name="com.anytypeio.anytype.ui.relations.RelationAddToObjectBlockFragment"
@ -111,6 +115,10 @@
android:id="@+id/objectSetCoverScreen"
android:name="com.anytypeio.anytype.ui.editor.cover.SelectCoverObjectSetFragment"
android:label="Object-Cover-Screen"/>
<dialog
android:id="@+id/objectCoverUnsplashScreen"
android:name="com.anytypeio.anytype.ui.editor.cover.ObjectSetUnsplashFragment"
android:label="Object-Cover-Unsplash-Screen"/>
<dialog
android:id="@+id/viewerCardSizeSelectFragment"
android:name="com.anytypeio.anytype.ui.sets.modals.viewer.ViewerCardSizeSelectFragment"

View file

@ -39,4 +39,5 @@
<dimen name="default_dashboard_card_height">126dp</dimen>
<dimen name="dashboard_object_icon_default_size">48dp</dimen>
<dimen name="default_divider_height">0.5dp</dimen>
<dimen name="default_search_toolbar_clip_height">36dp</dimen>
</resources>

View file

@ -268,4 +268,5 @@ Do the computation of an expensive paragraph of text on a background thread:
<string name="anytype_analytics_msg">Understanding how people use Anytype helps us improve the product. This version of Anytype includes the analytics code that protects your privacy.\nIt doesn\'t record the actual document\'s content but still allows us to understand how you use Anytype.\nStay subscribed to our mailing list, as we will soon announce a new release that enables you to opt-out.</string>
<string name="retry">Retry</string>
<string name="limit_object_types">Limit object types</string>
<string name="unsplash">Unsplash</string>
</resources>

View file

@ -0,0 +1,12 @@
package com.anytypeio.anytype.core_models
data class UnsplashImage(
val id: Id,
val url: Url,
val artist: Artist,
) {
data class Artist(
val name: String,
val url: Url
)
}

View file

@ -0,0 +1,60 @@
package com.anytypeio.anytype.core_ui.features.cover
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.anytypeio.anytype.core_models.UnsplashImage
import com.anytypeio.anytype.core_ui.R
import com.anytypeio.anytype.core_ui.databinding.ItemCoverUnsplashImageBinding
import com.anytypeio.anytype.core_utils.ext.dimen
import com.bumptech.glide.Glide
import com.bumptech.glide.load.resource.bitmap.CenterCrop
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
class UnsplashImageAdapter(
private val onImageClicked: (UnsplashImage) -> Unit
) : ListAdapter<UnsplashImage, UnsplashImageAdapter.ViewHolder>(Differ) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder = ViewHolder(
binding = ItemCoverUnsplashImageBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
).apply {
itemView.setOnClickListener {
val pos = bindingAdapterPosition
if (pos != RecyclerView.NO_POSITION)
onImageClicked(getItem(pos))
}
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(getItem(position))
}
class ViewHolder(
val binding: ItemCoverUnsplashImageBinding
) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: UnsplashImage) = with(binding) {
tvName.text = item.artist.name
Glide.with(ivImage)
.load(item.url)
.placeholder(R.drawable.rect_unsplash_image_placeholder)
.transform(CenterCrop(), RoundedCorners(dimen(R.dimen.dp_4)))
.into(ivImage)
}
}
object Differ : DiffUtil.ItemCallback<UnsplashImage>() {
override fun areItemsTheSame(oldItem: UnsplashImage, newItem: UnsplashImage): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: UnsplashImage, newItem: UnsplashImage): Boolean {
return oldItem == newItem
}
}
}

View file

@ -0,0 +1,22 @@
package com.anytypeio.anytype.core_ui.widgets
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.FrameLayout
import com.anytypeio.anytype.core_ui.R
import com.anytypeio.anytype.core_ui.databinding.WidgetDefaultSearchToolbarBinding
class DefaultSearchToolbar @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null
) : FrameLayout(context, attrs) {
val binding = WidgetDefaultSearchToolbarBinding.inflate(
LayoutInflater.from(context), this
)
init {
setBackgroundResource(R.drawable.rect_search_input)
}
}

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<stroke
android:width="0.5dp"
android:color="@color/shape_primary"
android:dashWidth="0.5dp"/>
<corners android:radius="4dp"/>
</shape>

View file

@ -72,6 +72,14 @@
</FrameLayout>
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center"
android:theme="@style/WhiteProgressBar"
android:visibility="gone"/>
</FrameLayout>
<com.anytypeio.anytype.core_ui.widgets.text.TextInputWidget

View file

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="112dp">
<ImageView
android:id="@+id/ivImage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:contentDescription="@string/content_description_cover_image_view"
tools:background="@color/black" />
<TextView
android:id="@+id/tvName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_marginStart="10dp"
android:layout_marginBottom="8dp"
android:fontFamily="@font/inter_medium"
android:textColor="@color/white"
tools:text="Yves Klein" />
</FrameLayout>

View file

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<ImageView
android:id="@+id/searchIcon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center_vertical"
android:layout_marginStart="6dp"
android:background="@drawable/ic_search" />
<EditText
android:id="@+id/filterInputField"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"
android:layout_gravity="center_vertical"
android:background="@null"
android:fontFamily="@font/inter_regular"
android:hint="@string/search"
android:inputType="textNoSuggestions"
android:maxLines="1"
android:singleLine="true"
android:textColorHint="@color/text_tertiary"
android:textColor="@color/text_primary"
android:textSize="17sp"
app:layout_constraintBottom_toBottomOf="@+id/searchIcon"
app:layout_constraintEnd_toStartOf="@+id/clearSearchText"
app:layout_constraintStart_toEndOf="@+id/searchIcon"
app:layout_constraintTop_toTopOf="@+id/searchIcon"
app:layout_goneMarginEnd="@dimen/dp_16" />
<ImageView
android:id="@+id/clearSearchText"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center_vertical|end"
android:layout_marginEnd="6dp"
android:src="@drawable/ic_search_delete"
android:visibility="invisible"
tools:visibility="visible" />
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center_vertical|end"
android:layout_marginEnd="6dp"
android:theme="@style/GreyProgressBar"
android:visibility="invisible"
tools:visibility="visible" />
</merge>

View file

@ -373,6 +373,10 @@
<item name="colorAccent">@color/text_secondary</item>
</style>
<style name="WhiteProgressBar" parent="ThemeOverlay.AppCompat.Light">
<item name="colorAccent">@color/white</item>
</style>
<style name="GridCellDateTextStyle">
<item name="android:textColor">@color/text_primary</item>
<item name="android:maxLines">1</item>

View file

@ -0,0 +1,19 @@
package com.anytypeio.anytype.data.auth.repo.unsplash
import com.anytypeio.anytype.core_models.Hash
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.UnsplashImage
import com.anytypeio.anytype.domain.unsplash.UnsplashRepository
class UnsplashDataRepository(
private val remote: UnsplashRemote
) : UnsplashRepository {
override fun search(
query: String,
limit: Int
): List<UnsplashImage> = remote.search(
query = query,
limit = limit
)
override fun download(id: Id) : Hash = remote.download(id = id)
}

View file

@ -0,0 +1,10 @@
package com.anytypeio.anytype.data.auth.repo.unsplash
import com.anytypeio.anytype.core_models.Hash
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.UnsplashImage
interface UnsplashRemote {
fun search(query: String, limit: Int) : List<UnsplashImage>
fun download(id: Id) : Hash
}

View file

@ -0,0 +1,16 @@
package com.anytypeio.anytype.domain.unsplash
import com.anytypeio.anytype.core_models.Hash
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.domain.base.BaseUseCase
class DownloadUnsplashImage(
private val repo: UnsplashRepository
) : BaseUseCase<Hash, DownloadUnsplashImage.Params>() {
override suspend fun run(params: Params) = safe {
repo.download(params.picture)
}
class Params(val picture: Id)
}

View file

@ -0,0 +1,22 @@
package com.anytypeio.anytype.domain.unsplash
import com.anytypeio.anytype.core_models.UnsplashImage
import com.anytypeio.anytype.domain.base.BaseUseCase
class SearchUnsplashImage(
private val repo: UnsplashRepository
) : BaseUseCase<List<UnsplashImage>, SearchUnsplashImage.Params>() {
override suspend fun run(params: Params) = safe {
repo.search(query = params.query, limit = params.limit)
}
class Params(
val query: String,
val limit: Int = DEFAULT_LIMIT
)
companion object {
const val DEFAULT_LIMIT = 36
}
}

View file

@ -0,0 +1,10 @@
package com.anytypeio.anytype.domain.unsplash
import com.anytypeio.anytype.core_models.Hash
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.UnsplashImage
interface UnsplashRepository {
fun search(query: String, limit: Int) : List<UnsplashImage>
fun download(id: Id) : Hash
}

View file

@ -0,0 +1,32 @@
package com.anytypeio.anytype.middleware
import anytype.Rpc
import anytype.Rpc.UnsplashSearch
import com.anytypeio.anytype.core_models.Hash
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.UnsplashImage
import com.anytypeio.anytype.data.auth.repo.unsplash.UnsplashRemote
import com.anytypeio.anytype.middleware.log.logRequest
import com.anytypeio.anytype.middleware.log.logResponse
import com.anytypeio.anytype.middleware.mappers.core
import com.anytypeio.anytype.middleware.service.MiddlewareService
class UnsplashMiddleware(
private val service: MiddlewareService
) : UnsplashRemote {
override fun search(query: String, limit: Int): List<UnsplashImage> {
val request = UnsplashSearch.Request(
query = query,
limit = limit
).also { it.logRequest() }
val response = service.unsplashSearch(request = request).also { it.logResponse() }
return response.pictures.map { p -> p.core() }
}
override fun download(id: Id): Hash {
val request = Rpc.UnsplashDownload.Request(pictureId = id).also { it.logRequest() }
val response = service.unsplashDownload(request = request).also { it.logResponse() }
return response.hash
}
}

View file

@ -0,0 +1,13 @@
package com.anytypeio.anytype.middleware.log
import timber.log.Timber
fun Any.logResponse() {
val message = "===> " + this::class.java.canonicalName + ":" + "\n" + this.toString()
Timber.d(message)
}
fun Any.logRequest() {
val message = "<=== " + this::class.java.canonicalName + ":" + "\n" + this.toString()
Timber.d(message)
}

View file

@ -0,0 +1,13 @@
package com.anytypeio.anytype.middleware.mappers
import anytype.Rpc.UnsplashSearch.Response.Picture
import com.anytypeio.anytype.core_models.UnsplashImage
fun Picture.core() : UnsplashImage = UnsplashImage(
id = id,
url = url,
artist = UnsplashImage.Artist(
name = artist,
url = artistUrl
)
)

View file

@ -227,4 +227,10 @@ interface MiddlewareService {
@Throws(Exception::class)
fun fileListOffload(request: FileList.Offload.Request): FileList.Offload.Response
@Throws(Exception::class)
fun unsplashSearch(request: UnsplashSearch.Request) : UnsplashSearch.Response
@Throws(Exception::class)
fun unsplashDownload(request: UnsplashDownload.Request) : UnsplashDownload.Response
}

View file

@ -920,4 +920,30 @@ class MiddlewareServiceImplementation : MiddlewareService {
return response
}
}
override fun unsplashSearch(request: UnsplashSearch.Request): UnsplashSearch.Response {
val encoded = Service.unsplashSearch(
UnsplashSearch.Request.ADAPTER.encode(request)
)
val response = UnsplashSearch.Response.ADAPTER.decode(encoded)
val error = response.error
if (error != null && error.code != UnsplashSearch.Response.Error.Code.NULL) {
throw Exception(error.description)
} else {
return response
}
}
override fun unsplashDownload(request: UnsplashDownload.Request): UnsplashDownload.Response {
val encoded = Service.unsplashDownload(
UnsplashDownload.Request.ADAPTER.encode(request)
)
val response = UnsplashDownload.Response.ADAPTER.decode(encoded)
val error = response.error
if (error != null && error.code != UnsplashDownload.Response.Error.Code.NULL) {
throw Exception(error.description)
} else {
return response
}
}
}

View file

@ -0,0 +1,22 @@
package com.anytypeio.anytype.presentation.common
import com.anytypeio.anytype.core_models.Id
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
interface Delegator<T> {
val channel : SharedFlow<T>
suspend fun delegate(action: T)
suspend fun receive() : Flow<T> = channel
class Default<T> : Delegator<T> {
override val channel = MutableSharedFlow<T>()
override suspend fun delegate(action: T) {
channel.emit(action)
}
}
}
sealed class Action {
data class SetUnsplashImage(val img: Id) : Action()
}

View file

@ -25,6 +25,7 @@ import com.anytypeio.anytype.domain.block.interactor.RemoveLinkMark
import com.anytypeio.anytype.domain.block.interactor.UpdateLinkMarks
import com.anytypeio.anytype.domain.block.interactor.UpdateText
import com.anytypeio.anytype.domain.block.interactor.sets.CreateObjectSet
import com.anytypeio.anytype.domain.cover.SetDocCoverImage
import com.anytypeio.anytype.domain.dataview.interactor.GetCompatibleObjectTypes
import com.anytypeio.anytype.domain.dataview.interactor.SearchObjects
import com.anytypeio.anytype.domain.editor.Editor
@ -36,7 +37,10 @@ import com.anytypeio.anytype.domain.objects.SetObjectIsArchived
import com.anytypeio.anytype.domain.page.*
import com.anytypeio.anytype.domain.sets.FindObjectSetForType
import com.anytypeio.anytype.domain.status.InterceptThreadStatus
import com.anytypeio.anytype.domain.unsplash.DownloadUnsplashImage
import com.anytypeio.anytype.presentation.BuildConfig
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.presentation.editor.ControlPanelMachine.Interactor
@ -123,6 +127,7 @@ class EditorViewModel(
private val orchestrator: Orchestrator,
private val analytics: Analytics,
private val dispatcher: Dispatcher<Payload>,
private val delegator: Delegator<Action>,
private val detailModificationManager: DetailModificationManager,
private val updateDetail: UpdateDetail,
private val getCompatibleObjectTypes: GetCompatibleObjectTypes,
@ -131,7 +136,9 @@ class EditorViewModel(
private val getDefaultEditorType: GetDefaultEditorType,
private val findObjectSetForType: FindObjectSetForType,
private val createObjectSet: CreateObjectSet,
private val copyFileToCache: CopyFileToCacheDirectory
private val copyFileToCache: CopyFileToCacheDirectory,
private val downloadUnsplashImage: DownloadUnsplashImage,
private val setDocCoverImage: SetDocCoverImage
) : ViewStateViewModel<ViewState>(),
SupportNavigation<EventWrapper<AppNavigation.Command>>,
SupportCommand<Command>,
@ -224,6 +231,43 @@ class EditorViewModel(
processRendering()
processMarkupChanges()
viewModelScope.launch { orchestrator.start() }
viewModelScope.launch {
delegator.receive().collect { action ->
when (action) {
is Action.SetUnsplashImage -> {
proceedWithSettingUnsplashImage(action)
}
}
}
}
}
private suspend fun proceedWithSettingUnsplashImage(
action: Action.SetUnsplashImage
) {
downloadUnsplashImage(
DownloadUnsplashImage.Params(
picture = action.img
)
).process(
failure = {
Timber.e(it, "Error while download unsplash image")
},
success = { hash ->
setDocCoverImage(
SetDocCoverImage.Params.FromHash(
context = context,
hash = hash
)
).process(
failure = {
Timber.e(it, "Error while setting unsplash image")
},
success = { payload -> dispatcher.send(payload) }
)
}
)
}
private fun startProcessingInternalDetailModifications() {

View file

@ -11,6 +11,7 @@ import com.anytypeio.anytype.domain.`object`.UpdateDetail
import com.anytypeio.anytype.domain.block.interactor.RemoveLinkMark
import com.anytypeio.anytype.domain.block.interactor.UpdateLinkMarks
import com.anytypeio.anytype.domain.block.interactor.sets.CreateObjectSet
import com.anytypeio.anytype.domain.cover.SetDocCoverImage
import com.anytypeio.anytype.domain.dataview.interactor.GetCompatibleObjectTypes
import com.anytypeio.anytype.domain.dataview.interactor.SearchObjects
import com.anytypeio.anytype.domain.event.interactor.InterceptEvents
@ -20,6 +21,9 @@ import com.anytypeio.anytype.domain.objects.SetObjectIsArchived
import com.anytypeio.anytype.domain.page.*
import com.anytypeio.anytype.domain.sets.FindObjectSetForType
import com.anytypeio.anytype.domain.status.InterceptThreadStatus
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.presentation.editor.editor.DetailModificationManager
import com.anytypeio.anytype.presentation.editor.editor.Orchestrator
@ -46,6 +50,7 @@ open class EditorViewModelFactory(
private val orchestrator: Orchestrator,
private val analytics: Analytics,
private val dispatcher: Dispatcher<Payload>,
private val delegator: Delegator<Action>,
private val detailModificationManager: DetailModificationManager,
private val updateDetail: UpdateDetail,
private val getCompatibleObjectTypes: GetCompatibleObjectTypes,
@ -53,7 +58,9 @@ open class EditorViewModelFactory(
private val searchObjects: SearchObjects,
private val getDefaultEditorType: GetDefaultEditorType,
private val findObjectSetForType: FindObjectSetForType,
private val copyFileToCacheDirectory: CopyFileToCacheDirectory
private val copyFileToCacheDirectory: CopyFileToCacheDirectory,
private val downloadUnsplashImage: DownloadUnsplashImage,
private val setDocCoverImage: SetDocCoverImage
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
@ -76,6 +83,7 @@ open class EditorViewModelFactory(
orchestrator = orchestrator,
analytics = analytics,
dispatcher = dispatcher,
delegator = delegator,
detailModificationManager = detailModificationManager,
updateDetail = updateDetail,
getCompatibleObjectTypes = getCompatibleObjectTypes,
@ -84,7 +92,9 @@ open class EditorViewModelFactory(
getDefaultEditorType = getDefaultEditorType,
findObjectSetForType = findObjectSetForType,
createObjectSet = createObjectSet,
copyFileToCache = copyFileToCacheDirectory
copyFileToCache = copyFileToCacheDirectory,
downloadUnsplashImage = downloadUnsplashImage,
setDocCoverImage = setDocCoverImage
) as T
}
}

View file

@ -0,0 +1,88 @@
package com.anytypeio.anytype.presentation.editor.cover
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.UnsplashImage
import com.anytypeio.anytype.domain.unsplash.SearchUnsplashImage
import com.anytypeio.anytype.presentation.common.Action
import com.anytypeio.anytype.presentation.common.BaseViewModel
import com.anytypeio.anytype.presentation.common.Delegator
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import timber.log.Timber
class UnsplashViewModel(
private val search: SearchUnsplashImage,
private val delegator: Delegator<Action>
) : BaseViewModel() {
val isCompleted = MutableStateFlow(false)
private val input = MutableStateFlow("")
private val query = input.take(1).onCompletion {
emitAll(
input.debounce(DEBOUNCE_DURATION).distinctUntilChanged()
)
}
val images = MutableStateFlow<List<UnsplashImage>>(emptyList())
val isLoading = MutableStateFlow(false)
init {
viewModelScope.launch {
query.mapLatest { q ->
isLoading.value = true
search(
SearchUnsplashImage.Params(
query = q
)
).process(
failure = {
Timber.e(it, "Error while searching unsplash pictures")
},
success = {
images.value = it
}
).also {
isLoading.value = false
}
}.collect()
}
}
fun onImageSelected(ctx: Id, img: UnsplashImage) {
viewModelScope.launch {
delegator.delegate(
Action.SetUnsplashImage(img.id)
)
isCompleted.value = true
}
}
fun onQueryChanged(query: String) {
viewModelScope.launch {
input.emit(query)
}
}
class Factory(
private val search: SearchUnsplashImage,
private val delegator: Delegator<Action>,
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return UnsplashViewModel(
search = search,
delegator = delegator,
) as T
}
}
companion object {
const val DEBOUNCE_DURATION = 300L
}
}

View file

@ -32,7 +32,7 @@ open class ObjectSearchViewModel(
) : ViewStateViewModel<ObjectSearchView>(),
SupportNavigation<EventWrapper<AppNavigation.Command>> {
private val userInput = MutableStateFlow(EMPTY_QUERY)
private val userInput = MutableStateFlow(EMPTY_QUERY)
private val searchQuery = userInput
.take(1)
.onCompletion {

View file

@ -11,6 +11,7 @@ import com.anytypeio.anytype.core_models.restrictions.DataViewRestriction
import com.anytypeio.anytype.core_utils.common.EventWrapper
import com.anytypeio.anytype.domain.base.Result
import com.anytypeio.anytype.domain.block.interactor.UpdateText
import com.anytypeio.anytype.domain.cover.SetDocCoverImage
import com.anytypeio.anytype.domain.dataview.interactor.*
import com.anytypeio.anytype.domain.error.Error
import com.anytypeio.anytype.domain.event.interactor.InterceptEvents
@ -18,6 +19,9 @@ import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.page.CloseBlock
import com.anytypeio.anytype.domain.sets.OpenObjectSet
import com.anytypeio.anytype.domain.status.InterceptThreadStatus
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.editor.editor.Proxy
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
import com.anytypeio.anytype.presentation.editor.model.TextUpdate
@ -48,10 +52,13 @@ class ObjectSetViewModel(
private val updateDataViewViewer: UpdateDataViewViewer,
private val updateDataViewRecord: UpdateDataViewRecord,
private val createDataViewRecord: CreateDataViewRecord,
private val downloadUnsplashImage: DownloadUnsplashImage,
private val setDocCoverImage: SetDocCoverImage,
private val updateText: UpdateText,
private val interceptEvents: InterceptEvents,
private val interceptThreadStatus: InterceptThreadStatus,
private val dispatcher: Dispatcher<Payload>,
private val delegator: Delegator<Action>,
private val objectSetRecordCache: ObjectSetRecordCache,
private val urlBuilder: UrlBuilder,
private val session: ObjectSetSession,
@ -177,6 +184,43 @@ class ObjectSetViewModel(
}
.collect()
}
viewModelScope.launch {
delegator.receive().collect { action ->
when (action) {
is Action.SetUnsplashImage -> {
proceedWithSettingUnsplashImage(action)
}
}
}
}
}
private suspend fun proceedWithSettingUnsplashImage(
action: Action.SetUnsplashImage
) {
downloadUnsplashImage(
DownloadUnsplashImage.Params(
picture = action.img
)
).process(
failure = {
Timber.e(it, "Error while download unsplash image")
},
success = { hash ->
setDocCoverImage(
SetDocCoverImage.Params.FromHash(
context = context,
hash = hash
)
).process(
failure = {
Timber.e(it, "Error while setting unsplash image")
},
success = { payload -> dispatcher.send(payload) }
)
}
)
}
fun onStart(ctx: Id) {

View file

@ -5,12 +5,16 @@ import androidx.lifecycle.ViewModelProvider
import com.anytypeio.anytype.analytics.base.Analytics
import com.anytypeio.anytype.core_models.Payload
import com.anytypeio.anytype.domain.block.interactor.UpdateText
import com.anytypeio.anytype.domain.cover.SetDocCoverImage
import com.anytypeio.anytype.domain.dataview.interactor.*
import com.anytypeio.anytype.domain.event.interactor.InterceptEvents
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.page.CloseBlock
import com.anytypeio.anytype.domain.sets.OpenObjectSet
import com.anytypeio.anytype.domain.status.InterceptThreadStatus
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.util.Dispatcher
class ObjectSetViewModelFactory(
@ -22,10 +26,13 @@ class ObjectSetViewModelFactory(
private val updateDataViewViewer: UpdateDataViewViewer,
private val updateDataViewRecord: UpdateDataViewRecord,
private val createDataViewRecord: CreateDataViewRecord,
private val downloadUnsplashImage: DownloadUnsplashImage,
private val setDocCoverImage: SetDocCoverImage,
private val updateText: UpdateText,
private val interceptEvents: InterceptEvents,
private val interceptThreadStatus: InterceptThreadStatus,
private val dispatcher: Dispatcher<Payload>,
private val delegator: Delegator<Action>,
private val objectSetRecordCache: ObjectSetRecordCache,
private val urlBuilder: UrlBuilder,
private val session: ObjectSetSession,
@ -42,10 +49,13 @@ class ObjectSetViewModelFactory(
updateDataViewViewer = updateDataViewViewer,
updateDataViewRecord = updateDataViewRecord,
createDataViewRecord = createDataViewRecord,
setDocCoverImage = setDocCoverImage,
downloadUnsplashImage = downloadUnsplashImage,
updateText = updateText,
interceptEvents = interceptEvents,
interceptThreadStatus = interceptThreadStatus,
dispatcher = dispatcher,
delegator = delegator,
objectSetRecordCache = objectSetRecordCache,
urlBuilder = urlBuilder,
session = session,

View file

@ -16,6 +16,7 @@ import com.anytypeio.anytype.domain.block.repo.BlockRepository
import com.anytypeio.anytype.domain.clipboard.Copy
import com.anytypeio.anytype.domain.clipboard.Paste
import com.anytypeio.anytype.domain.config.Gateway
import com.anytypeio.anytype.domain.cover.SetDocCoverImage
import com.anytypeio.anytype.domain.dataview.interactor.GetCompatibleObjectTypes
import com.anytypeio.anytype.domain.dataview.interactor.SearchObjects
import com.anytypeio.anytype.domain.dataview.interactor.SetRelationKey
@ -29,7 +30,11 @@ 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.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.presentation.editor.cover.CoverImageHashProvider
import com.anytypeio.anytype.presentation.editor.editor.*
import com.anytypeio.anytype.presentation.editor.editor.Command
@ -212,6 +217,9 @@ open class EditorViewModelTest {
@Mock
lateinit var repo: BlockRepository
@Mock
lateinit var unsplashRepo: UnsplashRepository
@Mock
lateinit var setObjectType: SetObjectType
@ -238,9 +246,13 @@ open class EditorViewModelTest {
lateinit var vm: EditorViewModel
private lateinit var builder: UrlBuilder
private lateinit var downloadUnsplashImage: DownloadUnsplashImage
private lateinit var setDocCoverImage: SetDocCoverImage
val root = MockDataFactory.randomUuid()
val delegator = Delegator.Default<Action>()
val title = Block(
id = MockDataFactory.randomUuid(),
content = Block.Content.Text(
@ -3918,6 +3930,8 @@ open class EditorViewModelTest {
selections = SelectionStateHolder.Default()
)
updateDetail = UpdateDetail(repo)
setDocCoverImage = SetDocCoverImage(repo)
downloadUnsplashImage = DownloadUnsplashImage(unsplashRepo)
vm = EditorViewModel(
openPage = openPage,
@ -3988,7 +4002,10 @@ open class EditorViewModelTest {
searchObjects = searchObjects,
findObjectSetForType = findObjectSetForType,
createObjectSet = createObjectSet,
copyFileToCache = copyFileToCacheDirectory
copyFileToCache = copyFileToCacheDirectory,
downloadUnsplashImage = downloadUnsplashImage,
setDocCoverImage = setDocCoverImage,
delegator = delegator
)
}

View file

@ -15,6 +15,7 @@ import com.anytypeio.anytype.domain.block.repo.BlockRepository
import com.anytypeio.anytype.domain.clipboard.Copy
import com.anytypeio.anytype.domain.clipboard.Paste
import com.anytypeio.anytype.domain.config.Gateway
import com.anytypeio.anytype.domain.cover.SetDocCoverImage
import com.anytypeio.anytype.domain.dataview.interactor.GetCompatibleObjectTypes
import com.anytypeio.anytype.domain.dataview.interactor.SearchObjects
import com.anytypeio.anytype.domain.dataview.interactor.SetRelationKey
@ -28,6 +29,10 @@ 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.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.presentation.editor.DocumentExternalEventReducer
import com.anytypeio.anytype.presentation.editor.Editor
import com.anytypeio.anytype.presentation.editor.EditorViewModel
@ -176,6 +181,9 @@ open class EditorPresentationTestSetup {
@Mock
lateinit var repo: BlockRepository
@Mock
lateinit var unsplashRepo: UnsplashRepository
@Mock
lateinit var getCompatibleObjectTypes: GetCompatibleObjectTypes
@ -206,9 +214,13 @@ open class EditorPresentationTestSetup {
protected val builder: UrlBuilder get() = UrlBuilder(gateway)
private lateinit var updateDetail: UpdateDetail
private lateinit var downloadUnsplashImage: DownloadUnsplashImage
private lateinit var setDocCoverImage: SetDocCoverImage
open lateinit var orchestrator: Orchestrator
private val delegator = Delegator.Default<Action>()
open fun buildViewModel(urlBuilder: UrlBuilder = builder): EditorViewModel {
val storage = Editor.Storage()
@ -217,6 +229,8 @@ open class EditorPresentationTestSetup {
selections = SelectionStateHolder.Default()
)
updateDetail = UpdateDetail(repo)
setDocCoverImage = SetDocCoverImage(repo)
downloadUnsplashImage = DownloadUnsplashImage(unsplashRepo)
orchestrator = Orchestrator(
createBlock = createBlock,
@ -289,7 +303,10 @@ open class EditorPresentationTestSetup {
getDefaultEditorType = getDefaultEditorType,
findObjectSetForType = findObjectSetForType,
createObjectSet = createObjectSet,
copyFileToCache = copyFileToCacheDirectory
copyFileToCache = copyFileToCacheDirectory,
delegator = delegator,
setDocCoverImage = setDocCoverImage,
downloadUnsplashImage = downloadUnsplashImage
)
}

View file

@ -8,12 +8,16 @@ import com.anytypeio.anytype.domain.base.Either
import com.anytypeio.anytype.domain.base.Result
import com.anytypeio.anytype.domain.block.interactor.UpdateText
import com.anytypeio.anytype.domain.config.Gateway
import com.anytypeio.anytype.domain.cover.SetDocCoverImage
import com.anytypeio.anytype.domain.dataview.interactor.*
import com.anytypeio.anytype.domain.event.interactor.InterceptEvents
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.page.CloseBlock
import com.anytypeio.anytype.domain.sets.OpenObjectSet
import com.anytypeio.anytype.domain.status.InterceptThreadStatus
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.sets.ObjectSetRecordCache
import com.anytypeio.anytype.presentation.sets.ObjectSetReducer
import com.anytypeio.anytype.presentation.sets.ObjectSetSession
@ -67,7 +71,14 @@ open class ObjectSetViewModelTestSetup {
@Mock
lateinit var analytics: Analytics
@Mock
lateinit var downloadUnsplashImage: DownloadUnsplashImage
@Mock
lateinit var setDocCoverImage: SetDocCoverImage
val dispatcher = Dispatcher.Default<Payload>()
val delegator = Delegator.Default<Action>()
val reducer = ObjectSetReducer()
val cache = ObjectSetRecordCache()
val session = ObjectSetSession()
@ -87,11 +98,14 @@ open class ObjectSetViewModelTestSetup {
createDataViewRecord = createDataViewRecord,
setActiveViewer = setActiveViewer,
dispatcher = dispatcher,
delegator = delegator,
reducer = reducer,
objectSetRecordCache = cache,
urlBuilder = urlBuilder,
session = session,
analytics = analytics
analytics = analytics,
downloadUnsplashImage = downloadUnsplashImage,
setDocCoverImage = setDocCoverImage
)
fun stubInterceptEvents(