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

DROID-1590 Set | Enhancement | Template widget, menu + actions (#295)

This commit is contained in:
Konstantin Ivanov 2023-08-18 18:25:54 +02:00 committed by GitHub
parent c6cb71ea05
commit b1514b7de3
Signed by: github
GPG key ID: 4AEE18F83AFDEB23
35 changed files with 494 additions and 140 deletions

View file

@ -28,10 +28,12 @@ import com.anytypeio.anytype.domain.event.interactor.InterceptEvents
import com.anytypeio.anytype.domain.launch.GetDefaultPageType
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.`object`.ConvertObjectToCollection
import com.anytypeio.anytype.domain.`object`.DuplicateObjects
import com.anytypeio.anytype.domain.`object`.UpdateDetail
import com.anytypeio.anytype.domain.objects.DefaultObjectStore
import com.anytypeio.anytype.domain.objects.DefaultStoreOfRelations
import com.anytypeio.anytype.domain.objects.ObjectStore
import com.anytypeio.anytype.domain.objects.SetObjectListIsArchived
import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
import com.anytypeio.anytype.domain.objects.StoreOfRelations
import com.anytypeio.anytype.domain.page.CloseBlock
@ -57,6 +59,7 @@ import com.anytypeio.anytype.presentation.sets.ObjectSetSession
import com.anytypeio.anytype.presentation.sets.ObjectSetViewModelFactory
import com.anytypeio.anytype.presentation.sets.state.DefaultObjectStateReducer
import com.anytypeio.anytype.presentation.sets.subscription.DefaultDataViewSubscription
import com.anytypeio.anytype.presentation.templates.ObjectTypeTemplatesContainer
import com.anytypeio.anytype.presentation.util.Dispatcher
import com.anytypeio.anytype.test_utils.MockDataFactory
import kotlinx.coroutines.flow.Flow
@ -128,7 +131,13 @@ abstract class TestObjectSetSetup {
lateinit var storeOfObjectTypes: StoreOfObjectTypes
@Mock
lateinit var getDefaultType: GetDefaultPageType
lateinit var duplicateObjects: DuplicateObjects
@Mock
lateinit var setObjectListIsArchived: SetObjectListIsArchived
@Mock
lateinit var templatesContainer: ObjectTypeTemplatesContainer
private lateinit var getTemplates: GetTemplates
private lateinit var getDefaultPageType: GetDefaultPageType
@ -247,8 +256,10 @@ abstract class TestObjectSetSetup {
addObjectToCollection = addObjectToCollection,
storeOfObjectTypes = storeOfObjectTypes,
getDefaultPageType = getDefaultPageType,
getTemplates = getTemplates,
updateDataViewViewer = updateDataViewViewer,
templatesContainer = templatesContainer,
setObjectListIsArchived = setObjectListIsArchived,
duplicateObjects = duplicateObjects
)
}

View file

@ -32,10 +32,12 @@ import com.anytypeio.anytype.domain.launch.GetDefaultPageType
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.`object`.ConvertObjectToCollection
import com.anytypeio.anytype.domain.`object`.DuplicateObject
import com.anytypeio.anytype.domain.`object`.DuplicateObjects
import com.anytypeio.anytype.domain.`object`.UpdateDetail
import com.anytypeio.anytype.domain.objects.DefaultObjectStore
import com.anytypeio.anytype.domain.objects.ObjectStore
import com.anytypeio.anytype.domain.objects.SetObjectIsArchived
import com.anytypeio.anytype.domain.objects.SetObjectListIsArchived
import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
import com.anytypeio.anytype.domain.objects.StoreOfRelations
import com.anytypeio.anytype.domain.objects.options.GetOptions
@ -79,6 +81,7 @@ import com.anytypeio.anytype.presentation.sets.state.ObjectState
import com.anytypeio.anytype.presentation.sets.state.ObjectStateReducer
import com.anytypeio.anytype.presentation.sets.subscription.DataViewSubscription
import com.anytypeio.anytype.presentation.sets.subscription.DefaultDataViewSubscription
import com.anytypeio.anytype.presentation.templates.ObjectTypeTemplatesContainer
import com.anytypeio.anytype.presentation.util.CopyFileToCacheDirectory
import com.anytypeio.anytype.presentation.util.DefaultCopyFileToCacheDirectory
import com.anytypeio.anytype.presentation.util.Dispatcher
@ -163,6 +166,17 @@ object ObjectSetModule {
dataViewSubscriptionContainer
)
@JvmStatic
@Provides
@PerScreen
fun provideDuplicateObjectsListUseCase(
repo: BlockRepository,
dispatchers: AppCoroutineDispatchers
): DuplicateObjects = DuplicateObjects(
repo = repo,
dispatchers = dispatchers
)
@JvmStatic
@Provides
@PerScreen
@ -197,8 +211,10 @@ object ObjectSetModule {
convertObjectToCollection: ConvertObjectToCollection,
storeOfObjectTypes: StoreOfObjectTypes,
getDefaultPageType: GetDefaultPageType,
getTemplates: GetTemplates,
updateDataViewViewer: UpdateDataViewViewer
updateDataViewViewer: UpdateDataViewViewer,
duplicateObjects: DuplicateObjects,
templatesContainer: ObjectTypeTemplatesContainer,
setObjectListIsArchived: SetObjectListIsArchived
): ObjectSetViewModelFactory = ObjectSetViewModelFactory(
openObjectSet = openObjectSet,
closeBlock = closeBlock,
@ -230,8 +246,10 @@ object ObjectSetModule {
objectToCollection = convertObjectToCollection,
storeOfObjectTypes = storeOfObjectTypes,
getDefaultPageType = getDefaultPageType,
getTemplates = getTemplates,
updateDataViewViewer = updateDataViewViewer
updateDataViewViewer = updateDataViewViewer,
duplicateObjects = duplicateObjects,
templatesContainer = templatesContainer,
setObjectListIsArchived = setObjectListIsArchived
)
@JvmStatic
@ -480,6 +498,14 @@ object ObjectSetModule {
repo = repo
)
@JvmStatic
@PerScreen
@Provides
fun getSetObjectListIsArchived(
repo: BlockRepository,
dispatchers: AppCoroutineDispatchers
): SetObjectListIsArchived = SetObjectListIsArchived(repo, dispatchers)
@JvmStatic
@Provides
@PerScreen

View file

@ -11,7 +11,6 @@ import com.anytypeio.anytype.domain.debugging.DebugSpaceShareDownloader
import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.`object`.SetObjectDetails
import com.anytypeio.anytype.domain.search.SubscriptionEventChannel
import com.anytypeio.anytype.presentation.settings.MainSettingsViewModel
import com.anytypeio.anytype.presentation.spaces.SpaceGradientProvider
import com.anytypeio.anytype.presentation.util.downloader.UriFileProvider
@ -43,19 +42,6 @@ interface MainSettingsSubComponent {
@Module
object MainSettingsModule {
@JvmStatic
@Provides
@PerScreen
fun provideStoreLessSubscriptionContainer(
repo: BlockRepository,
channel: SubscriptionEventChannel,
dispatchers: AppCoroutineDispatchers
): StorelessSubscriptionContainer = StorelessSubscriptionContainer.Impl(
repo = repo,
channel = channel,
dispatchers = dispatchers
)
@JvmStatic
@Provides
@PerScreen

View file

@ -14,7 +14,6 @@ import com.anytypeio.anytype.domain.icon.SetDocumentImageIcon
import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.`object`.SetObjectDetails
import com.anytypeio.anytype.domain.search.SubscriptionEventChannel
import com.anytypeio.anytype.presentation.spaces.SpaceGradientProvider
import com.anytypeio.anytype.ui.settings.ProfileFragment
import com.anytypeio.anytype.ui_settings.account.ProfileViewModel
@ -110,19 +109,6 @@ object ProfileModule {
repo: BlockRepository
): SetDocumentImageIcon = SetDocumentImageIcon(repo)
@JvmStatic
@Provides
@PerScreen
fun provideStoreLessSubscriptionContainer(
repo: BlockRepository,
channel: SubscriptionEventChannel,
dispatchers: AppCoroutineDispatchers
): StorelessSubscriptionContainer = StorelessSubscriptionContainer.Impl(
repo = repo,
channel = channel,
dispatchers = dispatchers
)
@Module
interface Bindings {
// Add bindings if needed

View file

@ -38,7 +38,6 @@ import com.anytypeio.anytype.di.feature.settings.LogoutWarningSubComponent
import com.anytypeio.anytype.di.feature.settings.MainSettingsSubComponent
import com.anytypeio.anytype.di.feature.settings.ProfileSubComponent
import com.anytypeio.anytype.di.feature.templates.TemplateBlankDependencies
import com.anytypeio.anytype.di.feature.templates.TemplateSelectComponent
import com.anytypeio.anytype.di.feature.templates.TemplateSelectDependencies
import com.anytypeio.anytype.di.feature.templates.TemplateSubComponent
import com.anytypeio.anytype.di.feature.types.TypeCreationDependencies
@ -69,7 +68,8 @@ import javax.inject.Singleton
AnalyticsModule::class,
LocalNetworkProviderModule::class,
SubscriptionsModule::class,
CrashReportingModule::class
CrashReportingModule::class,
TemplatesModule::class
]
)
interface MainComponent :

View file

@ -0,0 +1,40 @@
package com.anytypeio.anytype.di.main
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
import com.anytypeio.anytype.domain.block.repo.BlockRepository
import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer.Companion.SUBSCRIPTION_TEMPLATES
import com.anytypeio.anytype.domain.search.SubscriptionEventChannel
import com.anytypeio.anytype.domain.workspace.WorkspaceManager
import com.anytypeio.anytype.presentation.templates.DefaultObjectTypeTemplatesContainer
import com.anytypeio.anytype.presentation.templates.ObjectTypeTemplatesContainer
import dagger.Module
import dagger.Provides
import javax.inject.Named
import javax.inject.Singleton
@Module
object TemplatesModule {
@JvmStatic
@Provides
@Singleton
fun provideStorelessContainer(
repo: BlockRepository,
channel: SubscriptionEventChannel,
dispatchers: AppCoroutineDispatchers,
): StorelessSubscriptionContainer =
StorelessSubscriptionContainer.Impl(repo, channel, dispatchers)
@JvmStatic
@Provides
@Singleton
fun provideTemplatesContainer(
storage: StorelessSubscriptionContainer,
workspaceManager: WorkspaceManager
): ObjectTypeTemplatesContainer =
DefaultObjectTypeTemplatesContainer(
storage = storage,
workspaceManager = workspaceManager
)
}

View file

@ -326,7 +326,8 @@ sealed class Event {
val coverRelationKey: String,
val hideIcon: Boolean,
val cardSize: DVViewerCardSize,
val coverFit: Boolean
val coverFit: Boolean,
val defaultTemplateId: String?
)
}
}

View file

@ -162,6 +162,7 @@ sealed class ObjectWrapper {
}
else -> null
}
val defaultTemplateId: Id? by default
}
data class Relation(override val map: Struct) : ObjectWrapper() {

View file

@ -59,6 +59,7 @@ object Relations {
const val RECOMMENDED_LAYOUT = "recommendedLayout"
const val RECOMMENDED_RELATIONS = "recommendedRelations"
const val DEFAULT_TEMPLATE_ID = "defaultTemplateId"
val systemRelationKeys = listOf(
"id",

View file

@ -1,5 +1,6 @@
package com.anytypeio.anytype.core_ui.widgets
import androidx.annotation.ColorRes
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
@ -62,6 +63,7 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import coil.compose.rememberAsyncImagePainter
@ -279,7 +281,8 @@ private fun MoreMenu(
}
Column(
modifier = Modifier
.size(244.dp, 176.dp)
.width(244.dp)
.wrapContentHeight()
.offset(x = offsetX, y = -260.dp)
.shadow(
elevation = 40.dp,
@ -291,11 +294,13 @@ private fun MoreMenu(
shape = RoundedCornerShape(size = 10.dp)
)
) {
MenuItem(
click = { menuClick(TemplateMenuClick.Default(templateView)) },
text = stringResource(id = R.string.templates_menu_default_for_view)
)
Divider()
if(currentState.isDefaultStateEnabled) {
MenuItem(
click = { menuClick(TemplateMenuClick.Default(templateView)) },
text = stringResource(id = R.string.templates_menu_default_for_view)
)
Divider()
}
MenuItem(
click = { menuClick(TemplateMenuClick.Edit(templateView)) },
text = stringResource(id = R.string.templates_menu_edit)
@ -308,13 +313,14 @@ private fun MoreMenu(
Divider()
MenuItem(
click = { menuClick(TemplateMenuClick.Delete(templateView)) },
text = stringResource(id = R.string.templates_menu_delete)
text = stringResource(id = R.string.templates_menu_delete),
color = R.color.palette_system_red
)
}
}
@Composable
private fun MenuItem(click: () -> Unit, text: String) {
private fun MenuItem(click: () -> Unit, text: String, @ColorRes color: Int = R.color.text_primary) {
Text(
modifier = Modifier
.fillMaxWidth()
@ -323,7 +329,7 @@ private fun MenuItem(click: () -> Unit, text: String) {
.clickable { click() },
text = text,
style = BodyCalloutRegular,
color = colorResource(id = R.color.text_primary),
color = colorResource(id = color),
textAlign = TextAlign.Center
)
}
@ -364,12 +370,21 @@ private fun TemplatesList(
.height(231.dp)
.width(127.dp)
) {
val borderWidth: Dp
val borderColor: Color
if (state.isDefaultStateEnabled && item.isDefault) {
borderWidth = 2.dp
borderColor = colorResource(id = R.color.palette_system_amber_50)
} else {
borderWidth = 1.dp
borderColor = colorResource(id = R.color.shape_primary)
}
Box(
modifier = Modifier
.padding(top = 7.dp, end = 7.dp)
.border(
width = 1.dp,
color = colorResource(id = R.color.shape_primary),
width = borderWidth,
color = borderColor,
shape = RoundedCornerShape(size = 16.dp)
)
.height(224.dp)

View file

@ -845,4 +845,8 @@ class BlockDataRepository(
override suspend fun setInternalFlags(command: Command.SetInternalFlags): Payload {
return remote.setInternalFlags(command)
}
override suspend fun duplicateObjectsList(ids: List<Id>): List<Id> {
return remote.duplicateObjectsList(ids)
}
}

View file

@ -362,4 +362,6 @@ interface BlockRemote {
suspend fun fileSpaceUsage(): FileLimits
suspend fun setInternalFlags(command: Command.SetInternalFlags): Payload
suspend fun duplicateObjectsList(ids: List<Id>): List<Id>
}

View file

@ -413,4 +413,5 @@ interface BlockRepository {
suspend fun setQueryToSet(command: Command.SetQueryToSet): Payload
suspend fun fileSpaceUsage(): FileLimits
suspend fun setInternalFlags(command: Command.SetInternalFlags): Payload
suspend fun duplicateObjectsList(ids: List<Id>): List<Id>
}

View file

@ -26,6 +26,12 @@ interface StorelessSubscriptionContainer {
fun subscribe(searchParams: StoreSearchByIdsParams) : Flow<List<ObjectWrapper.Basic>>
suspend fun unsubscribe(subscriptions: List<Id>)
companion object {
const val SUBSCRIPTION_SETTINGS = "settings-subscription"
const val SUBSCRIPTION_PROFILE = "profile-subscription"
const val SUBSCRIPTION_TEMPLATES = "templates-subscription"
}
class Impl @Inject constructor(
private val repo: BlockRepository,

View file

@ -0,0 +1,20 @@
package com.anytypeio.anytype.domain.`object`
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
import com.anytypeio.anytype.domain.base.ResultInteractor
import com.anytypeio.anytype.domain.block.repo.BlockRepository
class DuplicateObjects(
private val repo: BlockRepository,
dispatchers: AppCoroutineDispatchers
) : ResultInteractor<DuplicateObjects.Params, List<Id>>(dispatchers.io) {
override suspend fun doWork(params: Params): List<Id> {
return repo.duplicateObjectsList(params.ids)
}
data class Params(
val ids: List<Id>
)
}

View file

@ -52,7 +52,8 @@ class ObjectTypesSubscriptionManager (
Relations.ICON_EMOJI,
Relations.SOURCE_OBJECT,
Relations.IS_READ_ONLY,
Relations.RECOMMENDED_LAYOUT
Relations.RECOMMENDED_LAYOUT,
Relations.DEFAULT_TEMPLATE_ID
),
ignoreWorkspace = true
)

View file

@ -796,4 +796,8 @@ class BlockMiddleware(
override suspend fun setInternalFlags(command: Command.SetInternalFlags): Payload {
return middleware.setInternalFlags(command)
}
override suspend fun duplicateObjectsList(ids: List<Id>): List<Id> {
return middleware.duplicateObjectsList(ids)
}
}

View file

@ -2231,6 +2231,19 @@ class Middleware @Inject constructor(
return response.toCoreModel()
}
@Throws(Exception::class)
fun duplicateObjectsList(
objects: List<Id>
): List<Id> {
val request = Rpc.Object.ListDuplicate.Request(
objectIds = objects
)
if (BuildConfig.DEBUG) logRequest(request)
val response = service.objectsListDuplicate(request)
if (BuildConfig.DEBUG) logResponse(response)
return response.ids
}
private fun logRequest(any: Any) {
logger.logRequest(any).also {
if (BuildConfig.DEBUG && threadInfo.isOnMainThread()) {

View file

@ -137,6 +137,7 @@ fun MDVViewFields.toCoreModels(): DVViewerFields {
coverRelationKey = coverRelationKey,
hideIcon = hideIcon,
cardSize = cardSize.toCodeModels(),
coverFit = coverFit
coverFit = coverFit,
defaultTemplateId = defaultTemplateId
)
}

View file

@ -146,6 +146,9 @@ interface MiddlewareService {
@Throws(Exception::class)
fun setInternalFlags(request: Rpc.Object.SetInternalFlags.Request): Rpc.Object.SetInternalFlags.Response
@Throws(Exception::class)
fun objectsListDuplicate(request: Rpc.Object.ListDuplicate.Request): Rpc.Object.ListDuplicate.Response
//endregion
//region OBJECT'S RELATIONS command

View file

@ -1624,4 +1624,17 @@ class MiddlewareServiceImplementation @Inject constructor(
return response
}
}
override fun objectsListDuplicate(request: Rpc.Object.ListDuplicate.Request): Rpc.Object.ListDuplicate.Response {
val encoded = Service.objectListDuplicate(
Rpc.Object.ListDuplicate.Request.ADAPTER.encode(request)
)
val response = Rpc.Object.ListDuplicate.Response.ADAPTER.decode(encoded)
val error = response.error
if (error != null && error.code != Rpc.Object.ListDuplicate.Response.Error.Code.NULL) {
throw Exception(error.description)
} else {
return response
}
}
}

View file

@ -77,7 +77,7 @@ fun ObjectState.DataView.header(
ctx: Id,
urlBuilder: UrlBuilder,
coverImageHashProvider: CoverImageHashProvider
) : SetOrCollectionHeaderState {
): SetOrCollectionHeaderState {
val title = blocks.title()
return if (title != null) {
val wrapper = ObjectWrapper.Basic(
@ -385,7 +385,7 @@ suspend fun ObjectState.DataView.Set.isTemplatesAllowed(
getDefaultPageType: GetDefaultPageType
): Boolean {
val objectDetails = details[setOfValue.first()]?.map.orEmpty()
return when (objectDetails.type){
return when (objectDetails.type) {
ObjectTypeIds.OBJECT_TYPE -> {
val objectWrapper = ObjectWrapper.Type(objectDetails)
objectWrapper.isTemplatesAllowed()
@ -403,7 +403,7 @@ suspend fun StoreOfObjectTypes.isTemplatesAllowedForDefaultType(getDefaultPageTy
val defaultObjectType = getDefaultPageType.run(Unit).type ?: return false
val defaultObjType = get(defaultObjectType) ?: return false
return defaultObjType.isTemplatesAllowed()
} catch (e: Exception){
} catch (e: Exception) {
return false
}
}
@ -422,7 +422,8 @@ fun DVViewer.updateFields(fields: DVViewerFields?): DVViewer {
coverRelationKey = fields.coverRelationKey,
hideIcon = fields.hideIcon,
cardSize = fields.cardSize,
coverFit = fields.coverFit
coverFit = fields.coverFit,
defaultTemplateId = fields.defaultTemplateId
)
}
@ -437,12 +438,15 @@ fun Viewer.isEmpty(): Boolean =
fun ObjectWrapper.Basic.toTemplateView(
typeId: Id,
urlBuilder: UrlBuilder,
coverImageHashProvider: CoverImageHashProvider
coverImageHashProvider: CoverImageHashProvider,
objectTypeDefaultTemplate: Id? = null,
viewerDefaultTemplate: Id? = null
): TemplateView.Template {
val coverContainer = if (coverType != CoverType.NONE) {
BasicObjectCoverWrapper(this)
.getCover(urlBuilder, coverImageHashProvider)
} else null
val isDefault = viewerDefaultTemplate == id || objectTypeDefaultTemplate == id
return TemplateView.Template(
id = id,
name = name.orEmpty(),
@ -452,14 +456,21 @@ fun ObjectWrapper.Basic.toTemplateView(
layout = layout ?: ObjectType.Layout.BASIC,
coverColor = coverContainer?.coverColor,
coverImage = coverContainer?.coverImage,
coverGradient = coverContainer?.coverGradient
coverGradient = coverContainer?.coverGradient,
isDefault = isDefault
)
}
fun ObjectWrapper.Basic.toTemplateViewBlank(typeId: Id): TemplateView.Blank {
fun ObjectWrapper.Basic.toTemplateViewBlank(
typeId: Id,
objectTypeDefaultTemplate: Id?,
viewerDefaultTemplate: Id?
): TemplateView.Blank {
val isDefault = viewerDefaultTemplate.isNullOrBlank() && objectTypeDefaultTemplate.isNullOrBlank()
return TemplateView.Blank(
typeId = typeId,
layout = layout?.code ?: ObjectType.Layout.BASIC.code
layout = layout?.code ?: ObjectType.Layout.BASIC.code,
isDefault = isDefault
)
}

View file

@ -30,8 +30,10 @@ import com.anytypeio.anytype.domain.event.interactor.InterceptEvents
import com.anytypeio.anytype.domain.launch.GetDefaultPageType
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.`object`.ConvertObjectToCollection
import com.anytypeio.anytype.domain.`object`.DuplicateObjects
import com.anytypeio.anytype.domain.`object`.UpdateDetail
import com.anytypeio.anytype.domain.objects.ObjectStore
import com.anytypeio.anytype.domain.objects.SetObjectListIsArchived
import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
import com.anytypeio.anytype.domain.objects.StoreOfRelations
import com.anytypeio.anytype.domain.page.CloseBlock
@ -42,7 +44,6 @@ import com.anytypeio.anytype.domain.search.DataViewSubscriptionContainer
import com.anytypeio.anytype.domain.sets.OpenObjectSet
import com.anytypeio.anytype.domain.sets.SetQueryToObjectSet
import com.anytypeio.anytype.domain.status.InterceptThreadStatus
import com.anytypeio.anytype.domain.templates.GetTemplates
import com.anytypeio.anytype.domain.unsplash.DownloadUnsplashImage
import com.anytypeio.anytype.domain.workspace.WorkspaceManager
import com.anytypeio.anytype.presentation.common.Action
@ -67,6 +68,7 @@ import com.anytypeio.anytype.presentation.sets.model.Viewer
import com.anytypeio.anytype.presentation.sets.state.ObjectState
import com.anytypeio.anytype.presentation.sets.state.ObjectStateReducer
import com.anytypeio.anytype.presentation.sets.subscription.DataViewSubscription
import com.anytypeio.anytype.presentation.templates.ObjectTypeTemplatesContainer
import com.anytypeio.anytype.presentation.templates.TemplateMenuClick
import com.anytypeio.anytype.presentation.templates.TemplateView
import com.anytypeio.anytype.presentation.util.Dispatcher
@ -126,8 +128,10 @@ class ObjectSetViewModel(
private val objectToCollection: ConvertObjectToCollection,
private val storeOfObjectTypes: StoreOfObjectTypes,
private val getDefaultPageType: GetDefaultPageType,
private val getTemplates: GetTemplates,
private val updateDataViewViewer: UpdateDataViewViewer
private val updateDataViewViewer: UpdateDataViewViewer,
private val duplicateObjects: DuplicateObjects,
private val templatesContainer: ObjectTypeTemplatesContainer,
private val setObjectListIsArchived: SetObjectListIsArchived
) : ViewModel(), SupportNavigation<EventWrapper<AppNavigation.Command>> {
val status = MutableStateFlow(SyncStatus.UNKNOWN)
@ -163,7 +167,7 @@ class ObjectSetViewModel(
val header: StateFlow<SetOrCollectionHeaderState> = _header
val isCustomizeViewPanelVisible = MutableStateFlow(false)
val templatesWidgetState = MutableStateFlow(TemplatesWidgetUiState.reset())
val templatesWidgetState = MutableStateFlow(TemplatesWidgetUiState.init())
@Deprecated("could be deleted")
val isLoading = MutableStateFlow(false)
@ -256,6 +260,12 @@ class ObjectSetViewModel(
}
}
}
viewModelScope.launch {
_templateViews.collectLatest {
templatesWidgetState.value = templatesWidgetState.value.copy(items = it)
}
}
}
private suspend fun proceedWithSettingUnsplashImage(
@ -880,8 +890,7 @@ class ObjectSetViewModel(
fun onNewButtonIconClicked() {
Timber.d("onNewButtonIconClicked, ")
templatesWidgetState.value = TemplatesWidgetUiState(
items = _templateViews.value,
templatesWidgetState.value = templatesWidgetState.value.copy(
showWidget = true,
isEditing = false,
isMoreMenuVisible = false,
@ -1249,6 +1258,7 @@ class ObjectSetViewModel(
override fun onCleared() {
Timber.d("onCleared, ")
viewModelScope.launch { templatesContainer.unsubscribe() }
super.onCleared()
titleUpdateChannel.cancel()
stateReducer.clear()
@ -1478,42 +1488,48 @@ class ObjectSetViewModel(
private suspend fun gettingTemplatesForDataViewState(state: ObjectState.DataView) {
when (state) {
is ObjectState.DataView.Collection -> {
proceedWithGettingTemplates(typeId = null)
subscribeForTypeTemplates(typeId = null)
}
is ObjectState.DataView.Set -> {
val sourceId = proceedWithGettingSetSourceId(state)
val sourceMap = state.details[sourceId] ?: return
when (sourceMap.type.firstOrNull()) {
ObjectTypeIds.RELATION -> proceedWithGettingTemplates(typeId = null)
ObjectTypeIds.OBJECT_TYPE -> proceedWithGettingTemplates(typeId = sourceId)
ObjectTypeIds.RELATION -> subscribeForTypeTemplates(typeId = null)
ObjectTypeIds.OBJECT_TYPE -> subscribeForTypeTemplates(typeId = sourceId)
else -> { Timber.d("Ignoring type of source") }
}
}
}
}
private suspend fun proceedWithGettingTemplates(typeId: Id?) {
private suspend fun subscribeForTypeTemplates(typeId: Id?) {
val state = stateReducer.state.value.dataViewState() ?: return
val viewer = state.viewerById(session.currentViewerId.value) ?: return
val objectType = resolveObjectType(typeId)
if (objectType?.isTemplatesAllowed() == true) {
viewModelScope.launch {
getTemplates.async(GetTemplates.Params(objectType.id)).fold(
onSuccess = { templates ->
if (templates.isNotEmpty()) {
_templateViews.value =
listOf(templates.first().toTemplateViewBlank(objectType.id)) +
templates.map {
it.toTemplateView(
typeId = objectType.id,
urlBuilder = urlBuilder,
coverImageHashProvider = coverImageHashProvider
)
}
}
},
onFailure = { e ->
Timber.e(e, "Error getting templates for type ${objectType.id}")
templatesContainer.subscribe(objectType.id)
.catch { error ->
Timber.e(error, "Error while getting templates for type ${objectType.id}")
toast("Error while getting templates for type ${objectType.id}")
_templateViews.value = emptyList()
}
.map { results ->
Timber.d("subscribeForTypeTemplates, new templates size:[${results.size}]")
val blankTemplate = listOf(objectType.toTemplateViewBlank())
blankTemplate + results
.map { objTemplate ->
objTemplate.toTemplateView(
typeId = objectType.id,
urlBuilder = urlBuilder,
coverImageHashProvider = coverImageHashProvider,
objectTypeDefaultTemplate = objectType.defaultTemplateId,
viewerDefaultTemplate = viewer.defaultTemplateId
)
}
}.collectLatest {
_templateViews.value = it
}
)
}
} else {
Timber.d("Templates are not allowed for type:[${objectType?.id}]")
@ -1537,7 +1553,7 @@ class ObjectSetViewModel(
}
when(item) {
is TemplateView.Blank -> {
templatesWidgetState.value = TemplatesWidgetUiState.reset()
templatesWidgetState.value = templatesWidgetState.value.dismiss()
viewModelScope.launch {
logEvent(
state = stateReducer.state.value,
@ -1551,7 +1567,7 @@ class ObjectSetViewModel(
}
}
is TemplateView.Template -> {
templatesWidgetState.value = TemplatesWidgetUiState.reset()
templatesWidgetState.value = templatesWidgetState.value.dismiss()
viewModelScope.launch {
logEvent(
state = stateReducer.state.value,
@ -1620,17 +1636,18 @@ class ObjectSetViewModel(
val state = templatesWidgetState.value
templatesWidgetState.value = when {
state.isMoreMenuVisible -> state.copy(isMoreMenuVisible = false, moreMenuTemplate = null)
state.showWidget -> TemplatesWidgetUiState.reset()
state.showWidget -> templatesWidgetState.value.dismiss()
else -> state
}
}
fun onMoreMenuClicked(click: TemplateMenuClick) {
Timber.d("onMoreMenuClicked, click:[$click]")
when (click) {
is TemplateMenuClick.Default -> proceedWithUpdatingViewDefaultTemplate()
is TemplateMenuClick.Delete -> TODO()
is TemplateMenuClick.Duplicate -> TODO()
is TemplateMenuClick.Edit -> TODO()
is TemplateMenuClick.Delete -> proceedWithDeletionTemplate()
is TemplateMenuClick.Duplicate -> proceedWithDuplicateTemplate()
is TemplateMenuClick.Edit -> proceedWithEditingTemplate()
}
}
@ -1640,7 +1657,7 @@ class ObjectSetViewModel(
val template = templatesWidgetState.value.moreMenuTemplate ?: return
val params = UpdateDataViewViewer.Params.Template(
context = context,
target = viewer.id,
target = state.dataViewBlock.id,
viewer = viewer.copy(defaultTemplateId = template.id)
)
viewModelScope.launch {
@ -1653,6 +1670,65 @@ class ObjectSetViewModel(
)
}
}
private fun proceedWithDuplicateTemplate() {
val template = templatesWidgetState.value.moreMenuTemplate ?: return
val params = DuplicateObjects.Params(
ids = listOf(template.id)
)
templatesWidgetState.value = templatesWidgetState.value.copy(
isEditing = false,
isMoreMenuVisible = false,
moreMenuTemplate = null
)
viewModelScope.launch {
duplicateObjects.async(params).fold(
onSuccess = { ids ->
Timber.d("Successfully duplicated templates: $ids")
},
onFailure = { e ->
Timber.e(e, "Error while duplicating templates")
toast("Error while duplicating templates")
}
)
}
}
private fun proceedWithDeletionTemplate() {
val template = templatesWidgetState.value.moreMenuTemplate ?: return
val params = SetObjectListIsArchived.Params(
targets = listOf(template.id),
isArchived = true
)
templatesWidgetState.value = templatesWidgetState.value.copy(
isEditing = false,
isMoreMenuVisible = false,
moreMenuTemplate = null
)
viewModelScope.launch {
setObjectListIsArchived.async(params).fold(
onSuccess = { ids ->
Timber.d("Successfully archived templates: $ids")
},
onFailure = { e ->
Timber.e(e, "Error while deleting templates")
toast("Error while deleting templates")
}
)
}
}
private fun proceedWithEditingTemplate() {
val template = templatesWidgetState.value.moreMenuTemplate ?: return
templatesWidgetState.value = templatesWidgetState.value.copy(
isEditing = false,
isMoreMenuVisible = false,
moreMenuTemplate = null
)
viewModelScope.launch {
proceedWithOpeningObject(template.id)
}
}
//endregion
companion object {

View file

@ -14,7 +14,9 @@ import com.anytypeio.anytype.domain.event.interactor.InterceptEvents
import com.anytypeio.anytype.domain.launch.GetDefaultPageType
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.`object`.ConvertObjectToCollection
import com.anytypeio.anytype.domain.`object`.DuplicateObjects
import com.anytypeio.anytype.domain.objects.ObjectStore
import com.anytypeio.anytype.domain.objects.SetObjectListIsArchived
import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
import com.anytypeio.anytype.domain.objects.StoreOfRelations
import com.anytypeio.anytype.domain.page.CloseBlock
@ -24,7 +26,6 @@ import com.anytypeio.anytype.domain.search.DataViewSubscriptionContainer
import com.anytypeio.anytype.domain.sets.OpenObjectSet
import com.anytypeio.anytype.domain.sets.SetQueryToObjectSet
import com.anytypeio.anytype.domain.status.InterceptThreadStatus
import com.anytypeio.anytype.domain.templates.GetTemplates
import com.anytypeio.anytype.domain.unsplash.DownloadUnsplashImage
import com.anytypeio.anytype.domain.workspace.WorkspaceManager
import com.anytypeio.anytype.presentation.common.Action
@ -32,6 +33,7 @@ import com.anytypeio.anytype.presentation.common.Delegator
import com.anytypeio.anytype.presentation.editor.cover.CoverImageHashProvider
import com.anytypeio.anytype.presentation.sets.state.ObjectStateReducer
import com.anytypeio.anytype.presentation.sets.subscription.DataViewSubscription
import com.anytypeio.anytype.presentation.templates.ObjectTypeTemplatesContainer
import com.anytypeio.anytype.presentation.util.Dispatcher
class ObjectSetViewModelFactory(
@ -65,8 +67,10 @@ class ObjectSetViewModelFactory(
private val objectToCollection: ConvertObjectToCollection,
private val storeOfObjectTypes: StoreOfObjectTypes,
private val getDefaultPageType: GetDefaultPageType,
private val getTemplates: GetTemplates,
private val updateDataViewViewer: UpdateDataViewViewer
private val updateDataViewViewer: UpdateDataViewViewer,
private val duplicateObjects: DuplicateObjects,
private val templatesContainer: ObjectTypeTemplatesContainer,
private val setObjectListIsArchived: SetObjectListIsArchived
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
@ -101,8 +105,10 @@ class ObjectSetViewModelFactory(
objectToCollection = objectToCollection,
storeOfObjectTypes = storeOfObjectTypes,
getDefaultPageType = getDefaultPageType,
getTemplates = getTemplates,
updateDataViewViewer = updateDataViewViewer
updateDataViewViewer = updateDataViewViewer,
duplicateObjects = duplicateObjects,
templatesContainer = templatesContainer,
setObjectListIsArchived = setObjectListIsArchived
) as T
}
}

View file

@ -15,6 +15,7 @@ import com.anytypeio.anytype.domain.config.ConfigStorage
import com.anytypeio.anytype.domain.debugging.DebugSpaceShareDownloader
import com.anytypeio.anytype.domain.library.StoreSearchByIdsParams
import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer.Companion.SUBSCRIPTION_SETTINGS
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.`object`.SetObjectDetails
import com.anytypeio.anytype.presentation.profile.ProfileIconView
@ -22,6 +23,7 @@ import com.anytypeio.anytype.presentation.profile.profileIcon
import com.anytypeio.anytype.presentation.spaces.SpaceGradientProvider
import com.anytypeio.anytype.presentation.spaces.SpaceIconView
import com.anytypeio.anytype.presentation.spaces.spaceIcon
import javax.inject.Named
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted

View file

@ -0,0 +1,100 @@
package com.anytypeio.anytype.presentation.templates
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.DVFilter
import com.anytypeio.anytype.core_models.DVFilterCondition
import com.anytypeio.anytype.core_models.DVSort
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.ObjectTypeIds
import com.anytypeio.anytype.core_models.ObjectWrapper
import com.anytypeio.anytype.core_models.Relations
import com.anytypeio.anytype.domain.library.StoreSearchParams
import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer.Companion.SUBSCRIPTION_TEMPLATES
import com.anytypeio.anytype.domain.workspace.WorkspaceManager
import javax.inject.Named
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emptyFlow
sealed interface ObjectTypeTemplatesContainer {
suspend fun subscribe(type: Id?): Flow<List<ObjectWrapper.Basic>>
suspend fun unsubscribe()
}
class DefaultObjectTypeTemplatesContainer(
private val storage: StorelessSubscriptionContainer,
private val workspaceManager: WorkspaceManager
) : ObjectTypeTemplatesContainer {
override suspend fun subscribe(type: Id?): Flow<List<ObjectWrapper.Basic>> {
return if (type.isNullOrBlank()) {
emptyFlow()
} else {
val params = StoreSearchParams(
subscription = TYPE_TEMPLATES_SUBSCRIPTION_ID,
sorts = listOf(
DVSort(
relationKey = Relations.CREATED_DATE,
type = Block.Content.DataView.Sort.Type.DESC
)
),
filters = listOf(
DVFilter(
relation = Relations.IS_ARCHIVED,
condition = DVFilterCondition.NOT_EQUAL,
value = true
),
DVFilter(
relation = Relations.IS_DELETED,
condition = DVFilterCondition.NOT_EQUAL,
value = true
),
DVFilter(
relation = Relations.TYPE,
condition = DVFilterCondition.EQUAL,
value = ObjectTypeIds.TEMPLATE
),
DVFilter(
relation = Relations.TARGET_OBJECT_TYPE,
condition = DVFilterCondition.EQUAL,
value = type
),
DVFilter(
relation = Relations.WORKSPACE_ID,
condition = DVFilterCondition.EQUAL,
value = workspaceManager.getCurrentWorkspace()
),
DVFilter(
relation = Relations.ID,
condition = DVFilterCondition.NOT_EMPTY
)
),
keys = listOf(
Relations.ID,
Relations.NAME,
Relations.LAYOUT,
Relations.ICON_EMOJI,
Relations.ICON_IMAGE,
Relations.ICON_OPTION,
Relations.COVER_ID,
Relations.COVER_TYPE,
Relations.IS_ARCHIVED,
Relations.IS_DELETED,
Relations.CREATED_DATE,
Relations.TARGET_OBJECT_TYPE,
Relations.TYPE
),
)
storage.subscribe(params)
}
}
override suspend fun unsubscribe() {
storage.unsubscribe(listOf(TYPE_TEMPLATES_SUBSCRIPTION_ID))
}
companion object {
const val TYPE_TEMPLATES_SUBSCRIPTION_ID = "object-type-templates-subscription"
}
}

View file

@ -7,8 +7,13 @@ import com.anytypeio.anytype.presentation.editor.cover.CoverColor
sealed class TemplateView {
abstract val isDefault: Boolean
data class Blank(
val typeId: Id, val typeName: String = "", val layout: Int
val typeId: Id,
val typeName: String = "",
val layout: Int,
override val isDefault: Boolean = false
) : TemplateView()
data class Template(
@ -20,7 +25,8 @@ sealed class TemplateView {
val image: String?,
val coverColor: CoverColor?,
val coverImage: Url?,
val coverGradient: String?
val coverGradient: String?,
override val isDefault: Boolean = false
) : TemplateView() {
fun isCoverPresent(): Boolean {
@ -34,8 +40,8 @@ sealed class TemplateView {
}
sealed class TemplateMenuClick {
data class Default(val templateView: TemplateView.Template): TemplateMenuClick()
data class Edit(val templateView: TemplateView.Template): TemplateMenuClick()
data class Duplicate(val templateView: TemplateView.Template): TemplateMenuClick()
data class Delete(val templateView: TemplateView.Template): TemplateMenuClick()
data class Default(val templateView: TemplateView.Template) : TemplateMenuClick()
data class Edit(val templateView: TemplateView.Template) : TemplateMenuClick()
data class Duplicate(val templateView: TemplateView.Template) : TemplateMenuClick()
data class Delete(val templateView: TemplateView.Template) : TemplateMenuClick()
}

View file

@ -7,15 +7,25 @@ data class TemplatesWidgetUiState(
val showWidget: Boolean,
val isEditing: Boolean,
val isMoreMenuVisible: Boolean,
val moreMenuTemplate: TemplateView.Template?
val moreMenuTemplate: TemplateView.Template?,
val isDefaultStateEnabled: Boolean = false
) {
fun dismiss() = copy(
showWidget = false,
isEditing = false,
isMoreMenuVisible = false,
moreMenuTemplate = null,
isDefaultStateEnabled = false
)
companion object {
fun reset() = TemplatesWidgetUiState(
fun init() = TemplatesWidgetUiState(
items = emptyList(),
showWidget = false,
isEditing = false,
isMoreMenuVisible = false,
moreMenuTemplate = null
moreMenuTemplate = null,
isDefaultStateEnabled = false
)
}
}

View file

@ -54,7 +54,7 @@ class CollectionTemplatesDelegateTest: ObjectSetViewModelTestSetup() {
stubInterceptThreadStatus()
stubStoreOfObjectTypes(defaultTypeMap)
stubGetDefaultPageType(type = defaultType, name = defaultTypeName)
stubGetTemplates(type = defaultType)
stubTemplatesContainer(type = defaultType)
val details = Block.Details(
details = mapOf(
@ -87,9 +87,7 @@ class CollectionTemplatesDelegateTest: ObjectSetViewModelTestSetup() {
advanceUntilIdle()
verify(getTemplates, times(1)).async(
GetTemplates.Params(type = defaultType)
)
verify(templatesContainer, times(1)).subscribe(defaultType)
verifyNoInteractions(createDataViewObject)
}

View file

@ -495,7 +495,7 @@ class ObjectStateCollectionViewTest : ObjectSetViewModelTestSetup() {
),
dvSorts = listOf(mockObjectCollection.sortGallery)
)
stubGetTemplates()
stubTemplatesContainer()
// TESTING
viewModel.onStart(ctx = root)
@ -531,7 +531,7 @@ class ObjectStateCollectionViewTest : ObjectSetViewModelTestSetup() {
stubInterceptEvents()
stubInterceptThreadStatus()
stubGetDefaultPageType(defaultObjectType, defaultObjectTypeName)
stubGetTemplates(
stubTemplatesContainer(
type = defaultObjectType,
templates = listOf(StubObject(objectType = defaultObjectType))
)

View file

@ -440,10 +440,9 @@ class ObjectStateSetViewTest : ObjectSetViewModelTestSetup() {
dvFilters = mockObjectSet.filters
)
stubStoreOfObjectTypes(pageTypeMap)
stubGetTemplates(
stubTemplatesContainer(
type = ObjectTypeIds.PAGE,
templates = listOf(StubObject(objectType = ObjectTypeIds.PAGE)
)
templates = listOf(StubObject(objectType = ObjectTypeIds.PAGE))
)
// TESTING

View file

@ -729,7 +729,8 @@ class ObjectSetReducerTest {
coverFit = newCoverFit,
coverRelationKey = relationKey1,
hideIcon = false,
type = Block.Content.DataView.Viewer.Type.GRID
type = Block.Content.DataView.Viewer.Type.GRID,
defaultTemplateId = null
)
)

View file

@ -58,7 +58,7 @@ class ObjectSetTemplatesMenuTest : ObjectSetViewModelTestSetup() {
dvFilters = mockObjectSet.filters
)
stubStoreOfObjectTypes(pageTypeMap)
stubGetTemplates(
stubTemplatesContainer(
type = ObjectTypeIds.PAGE,
templates = listOf(templateView)
)
@ -84,7 +84,7 @@ class ObjectSetTemplatesMenuTest : ObjectSetViewModelTestSetup() {
val state = templatesFlow.awaitItem()
assertIs<TemplatesWidgetUiState>(state)
assertEquals(
TemplatesWidgetUiState.reset(), state
TemplatesWidgetUiState.init(), state
)
}
//endregion
@ -100,7 +100,7 @@ class ObjectSetTemplatesMenuTest : ObjectSetViewModelTestSetup() {
viewModel.onNewButtonIconClicked()
val second = awaitItem()
assertEquals(
TemplatesWidgetUiState.reset().copy(showWidget = true), second
TemplatesWidgetUiState.init().copy(showWidget = true), second
)
}
}
@ -116,7 +116,7 @@ class ObjectSetTemplatesMenuTest : ObjectSetViewModelTestSetup() {
awaitItem()
viewModel.onDismissTemplatesWidget()
val result = awaitItem()
assertEquals(TemplatesWidgetUiState.reset(), result)
assertEquals(TemplatesWidgetUiState.init(), result)
}
}
@ -131,7 +131,7 @@ class ObjectSetTemplatesMenuTest : ObjectSetViewModelTestSetup() {
awaitItem()
viewModel.onTemplateItemClicked(item = StubTemplateView(id = templateView.id))
val result = awaitItem()
assertEquals(TemplatesWidgetUiState.reset(), result)
assertEquals(TemplatesWidgetUiState.init(), result)
}
}
//endregion
@ -149,7 +149,7 @@ class ObjectSetTemplatesMenuTest : ObjectSetViewModelTestSetup() {
viewModel.onEditTemplateButtonClicked()
val third = awaitItem()
assertEquals(
TemplatesWidgetUiState.reset().copy(
TemplatesWidgetUiState.init().copy(
showWidget = true,
isEditing = true
), third
@ -171,7 +171,7 @@ class ObjectSetTemplatesMenuTest : ObjectSetViewModelTestSetup() {
viewModel.onDoneTemplateButtonClicked()
val fourth = awaitItem()
assertEquals(
TemplatesWidgetUiState.reset().copy(
TemplatesWidgetUiState.init().copy(
showWidget = true,
isEditing = false
), fourth
@ -192,7 +192,7 @@ class ObjectSetTemplatesMenuTest : ObjectSetViewModelTestSetup() {
awaitItem()
viewModel.onTemplateItemClicked(item = StubTemplateView(id = templateView.id))
val fourth = awaitItem()
assertEquals(TemplatesWidgetUiState.reset(), fourth)
assertEquals(TemplatesWidgetUiState.init(), fourth)
}
}
@ -209,7 +209,7 @@ class ObjectSetTemplatesMenuTest : ObjectSetViewModelTestSetup() {
awaitItem()
viewModel.onDismissTemplatesWidget()
val fourth = awaitItem()
assertEquals(TemplatesWidgetUiState.reset(), fourth)
assertEquals(TemplatesWidgetUiState.init(), fourth)
}
}
//endregion
@ -232,7 +232,7 @@ class ObjectSetTemplatesMenuTest : ObjectSetViewModelTestSetup() {
viewModel.onMoreTemplateButtonClicked(template = templateView)
val fourth = awaitItem()
assertEquals(
TemplatesWidgetUiState.reset().copy(
TemplatesWidgetUiState.init().copy(
showWidget = true,
isEditing = true,
isMoreMenuVisible = true,
@ -261,7 +261,7 @@ class ObjectSetTemplatesMenuTest : ObjectSetViewModelTestSetup() {
viewModel.onDismissTemplatesWidget()
val result = awaitItem()
assertEquals(
TemplatesWidgetUiState.reset().copy(
TemplatesWidgetUiState.init().copy(
showWidget = true,
isEditing = true,
isMoreMenuVisible = false,
@ -290,7 +290,7 @@ class ObjectSetTemplatesMenuTest : ObjectSetViewModelTestSetup() {
viewModel.onTemplateItemClicked(item = templateView)
val result = awaitItem()
assertEquals(
TemplatesWidgetUiState.reset().copy(
TemplatesWidgetUiState.init().copy(
showWidget = true,
isEditing = true,
isMoreMenuVisible = false,
@ -319,7 +319,7 @@ class ObjectSetTemplatesMenuTest : ObjectSetViewModelTestSetup() {
viewModel.onDoneTemplateButtonClicked()
val result = awaitItem()
assertEquals(
TemplatesWidgetUiState.reset().copy(
TemplatesWidgetUiState.init().copy(
showWidget = true,
isEditing = true,
isMoreMenuVisible = false,
@ -348,7 +348,7 @@ class ObjectSetTemplatesMenuTest : ObjectSetViewModelTestSetup() {
viewModel.onMoreTemplateButtonClicked(template = templateView)
val result = awaitItem()
assertEquals(
TemplatesWidgetUiState.reset().copy(
TemplatesWidgetUiState.init().copy(
showWidget = true,
isEditing = true,
isMoreMenuVisible = false,

View file

@ -10,13 +10,11 @@ import com.anytypeio.anytype.core_models.ObjectType
import com.anytypeio.anytype.core_models.ObjectWrapper
import com.anytypeio.anytype.core_models.Payload
import com.anytypeio.anytype.core_models.Relation
import com.anytypeio.anytype.core_models.Relations
import com.anytypeio.anytype.core_models.SearchResult
import com.anytypeio.anytype.core_models.restrictions.DataViewRestrictions
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
import com.anytypeio.anytype.domain.base.Either
import com.anytypeio.anytype.domain.base.Result
import com.anytypeio.anytype.domain.base.Resultat
import com.anytypeio.anytype.domain.block.interactor.UpdateText
import com.anytypeio.anytype.domain.block.repo.BlockRepository
import com.anytypeio.anytype.domain.collections.AddObjectToCollection
@ -28,10 +26,12 @@ import com.anytypeio.anytype.domain.event.interactor.InterceptEvents
import com.anytypeio.anytype.domain.launch.GetDefaultPageType
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.`object`.ConvertObjectToCollection
import com.anytypeio.anytype.domain.`object`.DuplicateObjects
import com.anytypeio.anytype.domain.`object`.UpdateDetail
import com.anytypeio.anytype.domain.objects.DefaultObjectStore
import com.anytypeio.anytype.domain.objects.DefaultStoreOfRelations
import com.anytypeio.anytype.domain.objects.ObjectStore
import com.anytypeio.anytype.domain.objects.SetObjectListIsArchived
import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
import com.anytypeio.anytype.domain.objects.StoreOfRelations
import com.anytypeio.anytype.domain.page.CloseBlock
@ -57,10 +57,10 @@ import com.anytypeio.anytype.presentation.sets.ObjectSetPaginator
import com.anytypeio.anytype.presentation.sets.ObjectSetSession
import com.anytypeio.anytype.presentation.sets.ObjectSetViewModel
import com.anytypeio.anytype.presentation.sets.state.DefaultObjectStateReducer
import com.anytypeio.anytype.presentation.sets.state.ObjectStateReducer
import com.anytypeio.anytype.presentation.sets.subscription.DataViewSubscription
import com.anytypeio.anytype.presentation.sets.subscription.DefaultDataViewSubscription
import com.anytypeio.anytype.presentation.sets.updateFormatForSubscription
import com.anytypeio.anytype.presentation.templates.ObjectTypeTemplatesContainer
import com.anytypeio.anytype.presentation.util.DefaultCoroutineTestRule
import com.anytypeio.anytype.presentation.util.Dispatcher
import com.anytypeio.anytype.test_utils.MockDataFactory
@ -154,6 +154,15 @@ open class ObjectSetViewModelTestSetup {
@Mock
lateinit var getDefaultPageType: GetDefaultPageType
@Mock
lateinit var duplicateObjects: DuplicateObjects
@Mock
lateinit var setObjectListIsArchived: SetObjectListIsArchived
@Mock
lateinit var templatesContainer: ObjectTypeTemplatesContainer
@Mock
lateinit var updateDataViewViewer: UpdateDataViewViewer
@ -225,8 +234,10 @@ open class ObjectSetViewModelTestSetup {
setQueryToObjectSet = setQueryToObjectSet,
storeOfObjectTypes = storeOfObjectTypes,
getDefaultPageType = getDefaultPageType,
getTemplates = getTemplates,
updateDataViewViewer = updateDataViewViewer,
templatesContainer = templatesContainer,
setObjectListIsArchived = setObjectListIsArchived,
duplicateObjects = duplicateObjects
)
}
@ -362,15 +373,12 @@ open class ObjectSetViewModelTestSetup {
}
}
fun stubGetTemplates(
fun stubTemplatesContainer(
type: String = MockDataFactory.randomString(),
templates: List<ObjectWrapper.Basic> = emptyList()
) {
val params = GetTemplates.Params(
type = type
)
getTemplates.stub {
onBlocking { async(params) }.thenReturn(Resultat.success(templates))
templatesContainer.stub {
onBlocking { subscribe(type) }.thenReturn(flowOf(templates))
}
}
}

View file

@ -16,6 +16,7 @@ import com.anytypeio.anytype.domain.icon.SetDocumentImageIcon
import com.anytypeio.anytype.domain.icon.SetImageIcon
import com.anytypeio.anytype.domain.library.StoreSearchByIdsParams
import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer.Companion.SUBSCRIPTION_PROFILE
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.`object`.SetObjectDetails
import com.anytypeio.anytype.domain.search.PROFILE_SUBSCRIPTION_ID
@ -23,6 +24,7 @@ import com.anytypeio.anytype.presentation.extension.sendScreenSettingsDeleteEven
import com.anytypeio.anytype.presentation.profile.ProfileIconView
import com.anytypeio.anytype.presentation.profile.profileIcon
import com.anytypeio.anytype.presentation.spaces.SpaceGradientProvider
import javax.inject.Named
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted