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:
parent
c6cb71ea05
commit
b1514b7de3
35 changed files with 494 additions and 140 deletions
|
@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 :
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
|
@ -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?
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -162,6 +162,7 @@ sealed class ObjectWrapper {
|
|||
}
|
||||
else -> null
|
||||
}
|
||||
val defaultTemplateId: Id? by default
|
||||
}
|
||||
|
||||
data class Relation(override val map: Struct) : ObjectWrapper() {
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
}
|
|
@ -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>
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
}
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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()) {
|
||||
|
|
|
@ -137,6 +137,7 @@ fun MDVViewFields.toCoreModels(): DVViewerFields {
|
|||
coverRelationKey = coverRelationKey,
|
||||
hideIcon = hideIcon,
|
||||
cardSize = cardSize.toCodeModels(),
|
||||
coverFit = coverFit
|
||||
coverFit = coverFit,
|
||||
defaultTemplateId = defaultTemplateId
|
||||
)
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
)
|
||||
)
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue