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

Editor | Enhancement | Support new link appearance api (#2329)

This commit is contained in:
Sergey Boishtyan 2022-06-10 18:32:21 +03:00 committed by GitHub
parent 0ddc5014f1
commit f89bfb6146
Signed by: github
GPG key ID: 4AEE18F83AFDEB23
50 changed files with 1085 additions and 1061 deletions

View file

@ -23,6 +23,7 @@ import com.anytypeio.anytype.domain.block.interactor.MergeBlocks
import com.anytypeio.anytype.domain.block.interactor.Move
import com.anytypeio.anytype.domain.block.interactor.RemoveLinkMark
import com.anytypeio.anytype.domain.block.interactor.ReplaceBlock
import com.anytypeio.anytype.domain.block.interactor.SetLinkAppearance
import com.anytypeio.anytype.domain.block.interactor.SetObjectType
import com.anytypeio.anytype.domain.block.interactor.SplitBlock
import com.anytypeio.anytype.domain.block.interactor.TurnIntoDocument
@ -727,6 +728,13 @@ object EditorUseCaseModule {
repo: BlockRepository
): UpdateFields = UpdateFields(repo)
@JvmStatic
@Provides
@PerScreen
fun provideSetLinkAppearance(
repo: BlockRepository
): SetLinkAppearance = SetLinkAppearance(repo)
@JvmStatic
@Provides
@PerScreen

View file

@ -2,7 +2,7 @@ package com.anytypeio.anytype.di.feature
import com.anytypeio.anytype.core_models.Payload
import com.anytypeio.anytype.core_utils.di.scope.PerModal
import com.anytypeio.anytype.domain.block.interactor.UpdateFields
import com.anytypeio.anytype.domain.block.interactor.SetLinkAppearance
import com.anytypeio.anytype.presentation.editor.Editor
import com.anytypeio.anytype.presentation.objects.ObjectAppearanceSettingViewModel
import com.anytypeio.anytype.presentation.objects.appearance.ObjectAppearanceCoverViewModel
@ -38,12 +38,12 @@ object ObjectAppearanceSettingModule {
@PerModal
fun provideObjectAppearanceSettingViewModelFactory(
storage: Editor.Storage,
updateFields: UpdateFields,
setLinkAppearance: SetLinkAppearance,
dispatcher: Dispatcher<Payload>
): ObjectAppearanceSettingViewModel.Factory {
return ObjectAppearanceSettingViewModel.Factory(
storage = storage,
updateFields = updateFields,
setLinkAppearance = setLinkAppearance,
dispatcher = dispatcher
)
}
@ -71,12 +71,12 @@ object ObjectAppearanceIconModule {
@PerModal
fun provideObjectAppearanceIconViewModelFactory(
storage: Editor.Storage,
updateFields: UpdateFields,
setLinkAppearance: SetLinkAppearance,
dispatcher: Dispatcher<Payload>
): ObjectAppearanceIconViewModel.Factory {
return ObjectAppearanceIconViewModel.Factory(
storage = storage,
updateFields = updateFields,
setLinkAppearance = setLinkAppearance,
dispatcher = dispatcher
)
}
@ -104,12 +104,12 @@ object ObjectAppearancePreviewLayoutModule {
@PerModal
fun provideObjectAppearancePreviewLayoutViewModelFactory(
storage: Editor.Storage,
updateFields: UpdateFields,
setLinkAppearance: SetLinkAppearance,
dispatcher: Dispatcher<Payload>
): ObjectAppearancePreviewLayoutViewModel.Factory {
return ObjectAppearancePreviewLayoutViewModel.Factory(
storage = storage,
updateFields = updateFields,
setLinkAppearance = setLinkAppearance,
dispatcher = dispatcher
)
}
@ -137,12 +137,12 @@ object ObjectAppearanceCoverModule {
@PerModal
fun provideObjectAppearanceCoverViewModelFactory(
storage: Editor.Storage,
updateFields: UpdateFields,
setLinkAppearance: SetLinkAppearance,
dispatcher: Dispatcher<Payload>
): ObjectAppearanceCoverViewModel.Factory {
return ObjectAppearanceCoverViewModel.Factory(
storage = storage,
updateFields = updateFields,
setLinkAppearance = setLinkAppearance,
dispatcher = dispatcher
)
}

View file

@ -19,6 +19,7 @@ import com.anytypeio.anytype.core_utils.ext.toast
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetFragment
import com.anytypeio.anytype.databinding.FragmentObjAppearanceBaseBinding
import com.anytypeio.anytype.di.common.componentManager
import com.anytypeio.anytype.presentation.objects.ObjectAppearanceSettingView
import com.anytypeio.anytype.presentation.objects.appearance.ObjectAppearanceCoverViewModel
import com.anytypeio.anytype.presentation.objects.appearance.ObjectAppearanceIconViewModel
import com.anytypeio.anytype.presentation.objects.appearance.ObjectAppearancePreviewLayoutViewModel
@ -34,7 +35,10 @@ class ObjectAppearanceIconFragment : BaseBottomSheetFragment<FragmentObjAppearan
private val adapterAppearance by lazy {
ObjectAppearanceSettingAdapter(
onItemClick = { item ->
vm.onItemClicked(item = item, blockId = block, ctx = ctx)
val icon = requireNotNull(item as? ObjectAppearanceSettingView.Icon) {
"item $item must be Icon"
}
vm.onItemClicked(item = icon, blockId = block, ctx = ctx)
},
onSettingToggleChanged = { _, _ -> }
)
@ -119,7 +123,10 @@ class ObjectAppearancePreviewLayoutFragment :
private val adapterAppearance by lazy {
ObjectAppearanceSettingAdapter(
onItemClick = { item ->
vm.onItemClicked(item = item, blockId = block, ctx = ctx)
val preview = requireNotNull(item as? ObjectAppearanceSettingView.PreviewLayout) {
"item $item must be PreviewLayout"
}
vm.onItemClicked(item = preview, blockId = block, ctx = ctx)
},
onSettingToggleChanged = { _, _ -> }
)
@ -200,7 +207,10 @@ class ObjectAppearanceCoverFragment : BaseBottomSheetFragment<FragmentObjAppeara
private val adapterAppearance by lazy {
ObjectAppearanceSettingAdapter(
onItemClick = { item ->
vm.onItemClicked(item = item, blockId = block, ctx = ctx)
val cover = requireNotNull(item as? ObjectAppearanceSettingView.Cover) {
"item $item must be Cover"
}
vm.onItemClicked(item = cover, blockId = block, ctx = ctx)
},
onSettingToggleChanged = { _, _ -> }
)

View file

@ -19,6 +19,7 @@ import com.anytypeio.anytype.core_utils.ext.toast
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetFragment
import com.anytypeio.anytype.databinding.FragmentObjectAppearanceSettingBinding
import com.anytypeio.anytype.di.common.componentManager
import com.anytypeio.anytype.presentation.objects.ObjectAppearanceSettingView
import com.anytypeio.anytype.presentation.objects.ObjectAppearanceSettingViewModel
import javax.inject.Inject
@ -34,7 +35,11 @@ class ObjectAppearanceSettingFragment : BaseBottomSheetFragment<FragmentObjectAp
ObjectAppearanceSettingAdapter(
onItemClick = vm::onItemClicked,
onSettingToggleChanged = { item, isChecked ->
vm.onToggleClicked(item = item, blockId = block, ctx = ctx, isChecked = isChecked)
val relation = requireNotNull(item as? ObjectAppearanceSettingView.Relation.Description) {
"item $item must be Description"
}
vm.onToggleClicked(description = relation, blockId = block, ctx = ctx, isChecked = isChecked)
}
)
}

View file

@ -60,20 +60,6 @@ data class Block(
else -> null
}
val withName: Boolean? by default
val withDescription: Boolean? by default
val withIcon: Boolean? by default
val withCover: Boolean? by default
/**
* 0.0 - text, 1.0 - card
*/
val style: Double? by default
/**
* 1.0 - small, 2.0 - medium, 3.0 - large
*/
val iconSize: Double? by default
val analyticsContext: String? by default
@ -81,13 +67,7 @@ data class Block(
fun empty(): Fields = Fields(emptyMap())
const val NAME_KEY = "name"
const val TYPE_KEY = "type"
const val ICON_SIZE_KEY = "iconSize"
const val ICON_WITH_KEY = "withIcon"
const val IS_LOCKED_KEY = "isLocked"
const val COVER_WITH_KEY = "withCover"
const val STYLE_KEY = "style"
const val WITH_NAME_KEY = "withName"
const val WITH_DESCRIPTION_KEY = "withDescription"
}
}
@ -206,12 +186,38 @@ data class Block(
* @property type type of the link
* @property fields fields storing additional properties
*/
//
data class Link(
val target: Id,
val type: Type,
val fields: Fields
val iconSize: IconSize,
val cardStyle: CardStyle,
val description: Description,
val relations: Set<Relation>,
) : Content() {
sealed interface Relation {
object COVER : Relation
object NAME : Relation
object TYPE : Relation
data class UNKNOWN(val value: String) : Relation
}
enum class Type { PAGE, DATA_VIEW, DASHBOARD, ARCHIVE }
enum class IconSize { NONE, SMALL, MEDIUM }
enum class CardStyle { TEXT, CARD, INLINE }
enum class Description { NONE, ADDED, CONTENT }
val hasDescription: Boolean
get() = description != Description.NONE
val hasName: Boolean
get() = relations.contains(Relation.NAME)
val hasCover: Boolean
get() = relations.contains(Relation.COVER)
val hasType: Boolean
get() = relations.contains(Relation.TYPE)
}
/**

View file

@ -79,7 +79,7 @@ sealed class Command {
data class UpdateBlocksMark(
val context: Id,
val targets: List<Id>,
val mark : Block.Content.Text.Mark
val mark: Block.Content.Text.Mark
)
/**
@ -236,7 +236,7 @@ sealed class Command {
* @property key relation key
*/
data class SetRelationKey(
val contextId : Id,
val contextId: Id,
val blockId: Id,
val key: Id
)
@ -355,4 +355,10 @@ sealed class Command {
val context: Id,
val fields: List<Pair<Id, Block.Fields>>
)
data class SetLinkAppearance(
val contextId: String,
val blockId: String,
val content: Block.Content.Link
)
}

View file

@ -83,7 +83,10 @@ sealed class Event {
override val context: String,
val id: Id,
val target: Id,
val fields: Block.Fields?
val iconSize: Block.Content.Link.IconSize?,
val cardStyle: Block.Content.Link.CardStyle?,
val description: Block.Content.Link.Description?,
val relations: Set<Block.Content.Link.Relation>?,
) : Command()
/**

View file

@ -14,9 +14,8 @@ import com.anytypeio.anytype.core_ui.databinding.ItemObjectPreviewSettingBinding
import com.anytypeio.anytype.core_utils.ext.gone
import com.anytypeio.anytype.core_utils.ext.invisible
import com.anytypeio.anytype.core_utils.ext.visible
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView.Appearance.Companion.LINK_STYLE_CARD
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView.Appearance.MenuItem
import com.anytypeio.anytype.presentation.objects.ObjectAppearanceSettingView
import com.anytypeio.anytype.presentation.objects.appearance.ObjectAppearanceIconState
class ObjectAppearanceSettingAdapter(
private val onItemClick: (ObjectAppearanceSettingView) -> Unit,
@ -163,10 +162,9 @@ class ObjectAppearanceSettingAdapter(
fun bind(item: ObjectAppearanceSettingView.Settings.PreviewLayout) = with(binding) {
settingName.text = itemView.context.getString(R.string.preview_layout)
settingValue.text = if (item.style == LINK_STYLE_CARD) {
itemView.context.getString(R.string.card)
} else {
itemView.context.getString(R.string.text)
settingValue.text = when (item.previewLayoutState) {
MenuItem.PreviewLayout.TEXT -> itemView.context.getString(R.string.text)
MenuItem.PreviewLayout.CARD -> itemView.context.getString(R.string.card)
}
}
}
@ -179,12 +177,10 @@ class ObjectAppearanceSettingAdapter(
fun bind(item: ObjectAppearanceSettingView.Settings.Icon) = with(binding) {
settingName.text = itemView.context.getString(R.string.icon)
settingValue.text = when (item.state) {
ObjectAppearanceIconState.NONE -> itemView.context.getString(R.string.none)
ObjectAppearanceIconState.SMALL -> itemView.context.getString(R.string.small)
ObjectAppearanceIconState.MEDIUM -> itemView.context.getString(R.string.medium)
ObjectAppearanceIconState.LARGE -> itemView.context.getString(R.string.large)
ObjectAppearanceIconState.UNKNOWN -> itemView.context.getString(R.string.unknown)
settingValue.text = when (item.icon) {
MenuItem.Icon.NONE -> itemView.context.getString(R.string.none)
MenuItem.Icon.SMALL -> itemView.context.getString(R.string.small)
MenuItem.Icon.MEDIUM -> itemView.context.getString(R.string.medium)
}
}
}
@ -197,10 +193,9 @@ class ObjectAppearanceSettingAdapter(
fun bind(item: ObjectAppearanceSettingView.Settings.Cover) = with(binding) {
settingName.text = itemView.context.getString(R.string.cover)
settingValue.text = if (item.withCover == true) {
itemView.context.getString(R.string.visible)
} else {
itemView.context.getString(R.string.none)
settingValue.text = when (item.coverState) {
MenuItem.Cover.WITH -> itemView.context.getString(R.string.visible)
MenuItem.Cover.WITHOUT -> itemView.context.getString(R.string.none)
}
}
}
@ -228,12 +223,16 @@ class ObjectAppearanceSettingAdapter(
is ObjectAppearanceSettingView.Relation.Description -> {
relIcon.setImageResource(R.drawable.ic_relation_description)
relName.text = itemView.context.getString(R.string.description)
relSwitch.isChecked = item.withDescription ?: false
relSwitch.visible()
relSwitch.isChecked = when (item.description) {
MenuItem.Description.WITH -> true
MenuItem.Description.WITHOUT -> false
}
}
is ObjectAppearanceSettingView.Relation.Name -> {
relIcon.setImageResource(R.drawable.ic_relation_name)
relName.text = itemView.context.getString(R.string.name)
relSwitch.isChecked = item.withName ?: false
relSwitch.gone()
}
}
}
@ -243,11 +242,6 @@ class ObjectAppearanceSettingAdapter(
fun bind(item: ObjectAppearanceSettingView.Icon) = with(binding) {
when (item) {
is ObjectAppearanceSettingView.Icon.Large -> {
tvSize.text = itemView.context.getString(R.string.large)
ivIcon.gone()
if (item.isSelected) ivCheckbox.visible() else ivCheckbox.invisible()
}
is ObjectAppearanceSettingView.Icon.Medium -> {
tvSize.text = itemView.context.getString(R.string.medium)
ivIcon.gone()

View file

@ -40,7 +40,7 @@ class BlockDataRepository(
Result.Success(factory.remote.openObjectPreview(id))
} catch (e: BackwardCompatilityNotSupportedException) {
Result.Failure(Error.BackwardCompatibility)
} catch (e : NotFoundObjectException) {
} catch (e: NotFoundObjectException) {
Result.Failure(Error.NotFoundObject)
}
@ -105,6 +105,10 @@ class BlockDataRepository(
command: Command.UpdateTextColor
): Payload = factory.remote.updateTextColor(command)
override suspend fun setLinkAppearance(command: Command.SetLinkAppearance): Payload {
return factory.remote.setLinkAppearance(command)
}
override suspend fun updateBackgroundColor(
command: Command.UpdateBackgroundColor
): Payload = factory.remote.updateBackroundColor(command)

View file

@ -2,7 +2,6 @@ package com.anytypeio.anytype.data.auth.repo.block
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.Command
import com.anytypeio.anytype.core_models.Config
import com.anytypeio.anytype.core_models.DVFilter
import com.anytypeio.anytype.core_models.DVSort
import com.anytypeio.anytype.core_models.DVViewer
@ -37,6 +36,8 @@ interface BlockDataStore {
suspend fun updateText(command: Command.UpdateText)
suspend fun updateTextStyle(command: Command.UpdateStyle): Payload
suspend fun setLinkAppearance(command: Command.SetLinkAppearance): Payload
suspend fun updateCheckbox(command: Command.UpdateCheckbox): Payload
suspend fun uploadBlock(command: Command.UploadBlock): Payload
suspend fun move(command: Command.Move): Payload

View file

@ -2,7 +2,6 @@ package com.anytypeio.anytype.data.auth.repo.block
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.Command
import com.anytypeio.anytype.core_models.Config
import com.anytypeio.anytype.core_models.DVFilter
import com.anytypeio.anytype.core_models.DVSort
import com.anytypeio.anytype.core_models.DVViewer
@ -36,6 +35,8 @@ interface BlockRemote {
suspend fun updateText(command: Command.UpdateText)
suspend fun updateTextStyle(command: Command.UpdateStyle) : Payload
suspend fun setLinkAppearance(command: Command.SetLinkAppearance): Payload
suspend fun updateCheckbox(command: Command.UpdateCheckbox): Payload
suspend fun move(command: Command.Move): Payload
suspend fun createPage(

View file

@ -59,6 +59,10 @@ class BlockRemoteDataStore(private val remote: BlockRemote) : BlockDataStore {
remote.updateText(command)
}
override suspend fun setLinkAppearance(command: Command.SetLinkAppearance): Payload {
return remote.setLinkAppearance(command)
}
override suspend fun updateTextStyle(
command: Command.UpdateStyle
): Payload = remote.updateTextStyle(command)

View file

@ -0,0 +1,29 @@
package com.anytypeio.anytype.domain.block.interactor
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.Command
import com.anytypeio.anytype.core_models.Payload
import com.anytypeio.anytype.domain.base.BaseUseCase
import com.anytypeio.anytype.domain.base.Either
import com.anytypeio.anytype.domain.block.repo.BlockRepository
class SetLinkAppearance(
private val repository: BlockRepository,
) : BaseUseCase<Payload, SetLinkAppearance.Params>() {
class Params(
val contextId: String,
val blockId: String,
val content: Block.Content.Link
)
override suspend fun run(params: Params): Either<Throwable, Payload> = safe {
repository.setLinkAppearance(
Command.SetLinkAppearance(
contextId = params.contextId,
blockId = params.blockId,
content = params.content
)
)
}
}

View file

@ -2,7 +2,6 @@ package com.anytypeio.anytype.domain.block.repo
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.Command
import com.anytypeio.anytype.core_models.Config
import com.anytypeio.anytype.core_models.DVFilter
import com.anytypeio.anytype.core_models.DVRecord
import com.anytypeio.anytype.core_models.DVSort
@ -76,6 +75,7 @@ interface BlockRepository {
suspend fun updateDocumentTitle(command: Command.UpdateTitle)
suspend fun updateText(command: Command.UpdateText)
suspend fun updateTextStyle(command: Command.UpdateStyle): Payload
suspend fun setLinkAppearance(command: Command.SetLinkAppearance): Payload
suspend fun updateTextColor(command: Command.UpdateTextColor): Payload
suspend fun updateBackgroundColor(command: Command.UpdateBackgroundColor): Payload
@ -93,7 +93,7 @@ interface BlockRepository {
template: Id?
): Id
suspend fun openObjectPreview(id: Id) : Result<Payload>
suspend fun openObjectPreview(id: Id): Result<Payload>
suspend fun openPage(id: String): Result<Payload>
@ -281,13 +281,13 @@ interface BlockRepository {
suspend fun addToFeaturedRelations(ctx: Id, relations: List<Id>): Payload
suspend fun removeFromFeaturedRelations(ctx: Id, relations: List<Id>): Payload
suspend fun setObjectIsFavorite(ctx: Id, isFavorite: Boolean) : Payload
suspend fun setObjectIsArchived(ctx: Id, isArchived: Boolean) : Payload
suspend fun setObjectIsFavorite(ctx: Id, isFavorite: Boolean): Payload
suspend fun setObjectIsArchived(ctx: Id, isArchived: Boolean): Payload
suspend fun setObjectListIsArchived(targets: List<Id>, isArchived: Boolean)
suspend fun deleteObjects(targets: List<Id>)
suspend fun setObjectLayout(ctx: Id, layout: ObjectType.Layout) : Payload
suspend fun setObjectLayout(ctx: Id, layout: ObjectType.Layout): Payload
suspend fun clearFileCache()

View file

@ -2,7 +2,6 @@ package com.anytypeio.anytype.middleware.block
import com.anytypeio.anytype.core_models.CBTextStyle
import com.anytypeio.anytype.core_models.Command
import com.anytypeio.anytype.core_models.Config
import com.anytypeio.anytype.core_models.DVFilter
import com.anytypeio.anytype.core_models.DVSort
import com.anytypeio.anytype.core_models.DVViewer
@ -76,6 +75,10 @@ class BlockMiddleware(
)
}
override suspend fun setLinkAppearance(command: Command.SetLinkAppearance): Payload {
return middleware.blockLinkSetAppearance(command)
}
override suspend fun uploadBlock(command: Command.UploadBlock): Payload =
middleware.blockUpload(command)
@ -441,7 +444,7 @@ class BlockMiddleware(
override suspend fun addRelationToObject(
ctx: Id, relation: Id
) : Payload = middleware.objectRelationAdd(ctx, relation)
): Payload = middleware.objectRelationAdd(ctx, relation)
override suspend fun addNewRelationToObject(
ctx: Id,
@ -527,7 +530,7 @@ class BlockMiddleware(
isArchived = isArchived
)
override suspend fun setObjectLayout(ctx: Id, layout: ObjectType.Layout) : Payload =
override suspend fun setObjectLayout(ctx: Id, layout: ObjectType.Layout): Payload =
middleware.objectSetLayout(ctx, layout)
override suspend fun clearFileCache() = middleware.fileListOffload()

View file

@ -34,7 +34,6 @@ import com.anytypeio.anytype.middleware.mappers.core
import com.anytypeio.anytype.middleware.mappers.toCoreModels
import com.anytypeio.anytype.middleware.mappers.toMiddlewareModel
import com.anytypeio.anytype.middleware.mappers.toPayload
import com.anytypeio.anytype.middleware.model.CreateAccountResponse
import com.anytypeio.anytype.middleware.model.CreateWalletResponse
import com.anytypeio.anytype.middleware.service.MiddlewareService
import timber.log.Timber
@ -62,7 +61,7 @@ class Middleware(
}
@Throws(Exception::class)
fun accountDelete() : AccountStatus {
fun accountDelete(): AccountStatus {
val request = Rpc.Account.Delete.Request(
revert = false
)
@ -83,7 +82,7 @@ class Middleware(
}
@Throws(Exception::class)
fun accountRestore() : AccountStatus {
fun accountRestore(): AccountStatus {
val request = Rpc.Account.Delete.Request(
revert = true
)
@ -796,6 +795,27 @@ class Middleware(
if (BuildConfig.DEBUG) logResponse(response)
}
@Throws(Exception::class)
fun blockLinkSetAppearance(
command: Command.SetLinkAppearance,
): Payload {
val content = command.content
val request = Rpc.BlockLink.ListSetAppearance.Request(
contextId = command.contextId,
blockIds = listOf(command.blockId),
iconSize = content.iconSize.toMiddlewareModel(),
cardStyle = content.cardStyle.toMiddlewareModel(),
description = content.description.toMiddlewareModel(),
relations = content.relations.map { it.toMiddlewareModel() }
)
if (BuildConfig.DEBUG) logRequest(request)
val response = service.blockLinkListSetAppearance(
request
)
if (BuildConfig.DEBUG) logResponse(response)
return response.event.toPayload()
}
@Throws(Exception::class)
fun blockUpload(command: Command.UploadBlock): Payload {
val request = Rpc.Block.Upload.Request(
@ -941,7 +961,7 @@ class Middleware(
command.type?.let { details[Relations.TYPE] = it }
val request = Rpc.Object.Create.Request(
details = details.toMap()
details = details.toMap()
)
if (BuildConfig.DEBUG) logRequest(request)

View file

@ -2,6 +2,7 @@ package com.anytypeio.anytype.middleware.interactor
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.Event
import com.anytypeio.anytype.middleware.mappers.toCoreLinkRelationModel
import com.anytypeio.anytype.middleware.mappers.toCoreModel
import com.anytypeio.anytype.middleware.mappers.toCoreModels
import com.anytypeio.anytype.middleware.mappers.toCoreModelsAlign
@ -112,7 +113,10 @@ fun anytype.Event.Message.toCoreModels(
context = context,
id = event.id,
target = event.targetBlockId?.value_.orEmpty(),
fields = event.fields?.value_.toCoreModel()
iconSize = event.iconSize?.value_?.toCoreModel(),
cardStyle = event.cardStyle?.value_?.toCoreModel(),
description = event.description?.value_?.toCoreModel(),
relations = event.relations?.value_?.map { it.toCoreLinkRelationModel() }?.toSet()
)
}
blockSetAlign != null -> {

View file

@ -13,6 +13,9 @@ typealias MBFile = anytype.model.Block.Content.File
typealias MBFileState = anytype.model.Block.Content.File.State
typealias MBFileType = anytype.model.Block.Content.File.Type
typealias MBLink = anytype.model.Block.Content.Link
typealias MBLinkIconSize = anytype.model.Block.Content.Link.IconSize
typealias MBLinkCardStyle = anytype.model.Block.Content.Link.CardStyle
typealias MBLinkDescription = anytype.model.Block.Content.Link.Description
typealias MBLinkStyle = anytype.model.Block.Content.Link.Style
typealias MBBookmark = anytype.model.Block.Content.Bookmark
typealias MBLayout = anytype.model.Block.Content.Layout

View file

@ -182,11 +182,47 @@ fun MBlock.toCoreModelsLink(): Block.Content.Link {
val content = checkNotNull(link)
return Block.Content.Link(
target = content.targetBlockId,
fields = Block.Fields(content.fields?.toMap().orEmpty()),
type = content.style.toCoreModels()
type = content.style.toCoreModels(),
iconSize = content.iconSize.toCoreModel(),
cardStyle = content.cardStyle.toCoreModel(),
description = content.description.toCoreModel(),
relations = content.relations.map { it.toCoreLinkRelationModel() }.toSet(),
)
}
internal fun String.toCoreLinkRelationModel(): Block.Content.Link.Relation {
return when (this) {
"cover" -> Block.Content.Link.Relation.COVER
"name" -> Block.Content.Link.Relation.NAME
"type" -> Block.Content.Link.Relation.TYPE
else -> Block.Content.Link.Relation.UNKNOWN(this)
}
}
fun MBLinkIconSize.toCoreModel(): Block.Content.Link.IconSize {
return when (this) {
MBLinkIconSize.SizeNone -> Block.Content.Link.IconSize.NONE
MBLinkIconSize.SizeSmall -> Block.Content.Link.IconSize.SMALL
MBLinkIconSize.SizeMedium -> Block.Content.Link.IconSize.MEDIUM
}
}
fun MBLinkCardStyle.toCoreModel(): Block.Content.Link.CardStyle {
return when (this) {
MBLinkCardStyle.Text -> Block.Content.Link.CardStyle.TEXT
MBLinkCardStyle.Card -> Block.Content.Link.CardStyle.CARD
MBLinkCardStyle.Inline -> Block.Content.Link.CardStyle.INLINE
}
}
fun MBLinkDescription.toCoreModel(): Block.Content.Link.Description {
return when (this) {
MBLinkDescription.None -> Block.Content.Link.Description.NONE
MBLinkDescription.Added -> Block.Content.Link.Description.ADDED
MBLinkDescription.Content -> Block.Content.Link.Description.CONTENT
}
}
fun MBlock.toCoreModelsDivider(): Block.Content.Divider {
val content = checkNotNull(div)
return Block.Content.Divider(
@ -334,7 +370,7 @@ fun MDVView.toCoreModels(): DVViewer = DVViewer(
sorts = sorts.map { it.toCoreModels() },
filters = filters.map { it.toCoreModels() },
viewerRelations = relations.map { it.toCoreModels() },
cardSize = when(cardSize) {
cardSize = when (cardSize) {
MDVViewCardSize.Small -> DVViewerCardSize.SMALL
MDVViewCardSize.Medium -> DVViewerCardSize.MEDIUM
MDVViewCardSize.Large -> DVViewerCardSize.LARGE

View file

@ -1,7 +1,11 @@
package com.anytypeio.anytype.middleware.mappers
import anytype.model.Range
import com.anytypeio.anytype.core_models.*
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.BlockSplitMode
import com.anytypeio.anytype.core_models.ObjectType
import com.anytypeio.anytype.core_models.Position
import com.anytypeio.anytype.core_models.Relation
// ---------------------- BLOCKS ------------------------
@ -94,11 +98,49 @@ fun Block.Content.File.toMiddlewareModel(): MBFile = MBFile(
type = type.toMiddlewareModel()
)
fun Block.Content.Link.toMiddlewareModel(): MBLink = MBLink(
targetBlockId = target,
style = type.toMiddlewareModel(),
fields = fields.map
)
internal fun Block.Content.Link.toMiddlewareModel(): MBLink {
return MBLink(
targetBlockId = target,
style = type.toMiddlewareModel(),
iconSize = iconSize.toMiddlewareModel(),
cardStyle = cardStyle.toMiddlewareModel(),
description = description.toMiddlewareModel(),
relations = relations.map { it.toMiddlewareModel() }
)
}
internal fun Block.Content.Link.Relation.toMiddlewareModel(): String {
return when (this) {
Block.Content.Link.Relation.COVER -> "cover"
Block.Content.Link.Relation.NAME -> "name"
Block.Content.Link.Relation.TYPE -> "type"
is Block.Content.Link.Relation.UNKNOWN -> this.value
}
}
internal fun Block.Content.Link.Description.toMiddlewareModel(): MBLinkDescription {
return when (this) {
Block.Content.Link.Description.NONE -> MBLinkDescription.None
Block.Content.Link.Description.ADDED -> MBLinkDescription.Added
Block.Content.Link.Description.CONTENT -> MBLinkDescription.Content
}
}
internal fun Block.Content.Link.CardStyle.toMiddlewareModel(): MBLinkCardStyle {
return when (this) {
Block.Content.Link.CardStyle.TEXT -> MBLinkCardStyle.Text
Block.Content.Link.CardStyle.CARD -> MBLinkCardStyle.Card
Block.Content.Link.CardStyle.INLINE -> MBLinkCardStyle.Inline
}
}
internal fun Block.Content.Link.IconSize.toMiddlewareModel(): MBLinkIconSize {
return when (this) {
Block.Content.Link.IconSize.NONE -> MBLinkIconSize.SizeNone
Block.Content.Link.IconSize.SMALL -> MBLinkIconSize.SizeSmall
Block.Content.Link.IconSize.MEDIUM -> MBLinkIconSize.SizeMedium
}
}
fun Block.Content.Link.Type.toMiddlewareModel(): MBLinkStyle = when (this) {
Block.Content.Link.Type.PAGE -> MBLinkStyle.Page
@ -255,7 +297,7 @@ fun Block.Content.DataView.Viewer.toMiddlewareModel(): MDVView =
coverRelationKey = coverRelationKey.orEmpty(),
coverFit = coverFit,
hideIcon = hideIcon,
cardSize = when(cardSize) {
cardSize = when (cardSize) {
Block.Content.DataView.Viewer.Size.SMALL -> MDVViewCardSize.Small
Block.Content.DataView.Viewer.Size.MEDIUM -> MDVViewCardSize.Medium
Block.Content.DataView.Viewer.Size.LARGE -> MDVViewCardSize.Large

View file

@ -286,6 +286,13 @@ interface MiddlewareService {
//endregion
//region LINK BLOCK commands
@Throws(Exception::class)
fun blockLinkListSetAppearance(request: Rpc.BlockLink.ListSetAppearance.Request): Rpc.BlockLink.ListSetAppearance.Response
//endregion
//region DEBUG commands
@Throws(Exception::class)

View file

@ -263,6 +263,21 @@ class MiddlewareServiceImplementation : MiddlewareService {
}
}
override fun blockLinkListSetAppearance(
request: Rpc.BlockLink.ListSetAppearance.Request
): Rpc.BlockLink.ListSetAppearance.Response {
val encoded = Service.blockLinkListSetAppearance(
Rpc.BlockLink.ListSetAppearance.Request.ADAPTER.encode(request)
)
val response = Rpc.BlockLink.ListSetAppearance.Response.ADAPTER.decode(encoded)
val error = response.error
if (error != null && error.code != Rpc.BlockLink.ListSetAppearance.Response.Error.Code.NULL) {
throw Exception(error.description)
} else {
return response
}
}
override fun blockListDelete(request: Rpc.Block.ListDelete.Request): Rpc.Block.ListDelete.Response {
val encoded = Service.blockListDelete(Rpc.Block.ListDelete.Request.ADAPTER.encode(request))
val response = Rpc.Block.ListDelete.Response.ADAPTER.decode(encoded)

View file

@ -38,15 +38,6 @@ interface HomeDashboardEventConverter {
null
}
}
is Event.Command.LinkGranularChange -> {
event.fields?.let { fields ->
HomeDashboardStateMachine.Event.OnLinkFieldsChanged(
id = event.id,
fields = fields,
builder = builder
)
}
}
is Event.Command.Details.Set -> {
HomeDashboardStateMachine.Event.OnDetailsUpdated(
context = event.context,
@ -72,7 +63,7 @@ interface HomeDashboardEventConverter {
)
}
else -> {
Timber.d("Ignored event: $event")
Timber.v("Ignored event: $event")
null
}
}

View file

@ -9,7 +9,10 @@ import com.anytypeio.anytype.core_models.ext.set
import com.anytypeio.anytype.core_models.ext.unset
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.presentation.common.StateReducer
import com.anytypeio.anytype.presentation.dashboard.HomeDashboardStateMachine.*
import com.anytypeio.anytype.presentation.dashboard.HomeDashboardStateMachine.Event
import com.anytypeio.anytype.presentation.dashboard.HomeDashboardStateMachine.Interactor
import com.anytypeio.anytype.presentation.dashboard.HomeDashboardStateMachine.Reducer
import com.anytypeio.anytype.presentation.dashboard.HomeDashboardStateMachine.State
import com.anytypeio.anytype.presentation.extension.addAndSortByIds
import com.anytypeio.anytype.presentation.extension.sortByIds
import com.anytypeio.anytype.presentation.extension.updateDetails
@ -106,12 +109,6 @@ sealed class HomeDashboardStateMachine {
val children: List<String>
) : Event()
data class OnLinkFieldsChanged(
val id: String,
val fields: Block.Fields,
val builder: UrlBuilder
) : Event()
object OnDashboardLoadingStarted : Event()
object OnStartedCreatingPage : Event()
@ -191,16 +188,6 @@ sealed class HomeDashboardStateMachine {
blocks = state.blocks.addAndSortByIds(state.childrenIdsList, new)
)
}
is Event.OnLinkFieldsChanged -> {
state.copy(
blocks = state.blocks.updateDetails(
event.id,
event.fields,
event.builder,
objectTypes = state.objectTypes
)
)
}
is Event.OnDetailsUpdated -> {
state.copy(
blocks = state.blocks.updateDetails(

View file

@ -100,9 +100,16 @@ class DocumentExternalEventReducer : StateReducer<List<Block>, Event> {
is Event.Command.LinkGranularChange -> state.replace(
replacement = { block ->
val content = block.content<Block.Content.Link>()
val iconSize = event.iconSize ?: content.iconSize
val cardStyle = event.cardStyle ?: content.cardStyle
val description = event.description ?: content.description
val relations = event.relations ?: content.relations
block.copy(
content = content.copy(
fields = event.fields ?: content.fields
iconSize = iconSize,
cardStyle = cardStyle,
description = description,
relations = relations
)
)
},
@ -138,7 +145,7 @@ class DocumentExternalEventReducer : StateReducer<List<Block>, Event> {
typealias Flag = Int
object Flags {
const val FLAG_REFRESH : Flag = 0
const val FLAG_REFRESH: Flag = 0
val skipRefreshKeys = listOf(
Relations.NAME,
Relations.LAST_MODIFIED_DATE,
@ -147,9 +154,9 @@ object Flags {
)
}
fun List<Event>.flags(ctx: Id) : List<Flag> {
fun List<Event>.flags(ctx: Id): List<Flag> {
forEach { event ->
when(event) {
when (event) {
is Event.Command.Details.Amend -> {
if (event.target == ctx) {
if (event.details.keys.any { key -> !Flags.skipRefreshKeys.contains(key) }) {

View file

@ -4,14 +4,13 @@ import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.Document
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.ObjectWrapper
import com.anytypeio.anytype.core_models.ext.content
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView.Media.Bookmark.Companion.SEARCH_FIELD_DESCRIPTION_KEY
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView.Media.Bookmark.Companion.SEARCH_FIELD_TITLE_KEY
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView.Media.Bookmark.Companion.SEARCH_FIELD_URL_KEY
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView.Searchable.Field.Companion.DEFAULT_SEARCH_FIELD_KEY
import com.anytypeio.anytype.presentation.extension.shift
import com.anytypeio.anytype.presentation.objects.appearance.getLinkToObjectAppearanceParams
import com.anytypeio.anytype.presentation.objects.appearance.LinkAppearanceFactory
import timber.log.Timber
fun List<BlockView>.singleStylingMode(
@ -947,16 +946,23 @@ fun List<BlockView>.update(blockView: BlockView) = this.map {
if (it.id == blockView.id) blockView else it
}
fun Document.getAppearanceParamsOfBlockLink(blockId: Id, details: Block.Details)
: BlockView.Appearance.Params? {
fun Document.getLinkAppearanceMenu(
blockId: Id,
details: Block.Details
): BlockView.Appearance.Menu? {
val block = this.find { it.id == blockId }
val content = block?.content
if (block != null && content is Block.Content.Link) {
return if (block != null && content is Block.Content.Link) {
val target = content.asLink().target
val obj = ObjectWrapper.Basic(details.details[target]?.map ?: emptyMap())
return block.fields.getLinkToObjectAppearanceParams(layout = obj.layout)
val factory = LinkAppearanceFactory(
content = content,
layout = obj.layout
)
return factory.createAppearanceMenuItems()
} else {
null
}
return null
}
fun List<BlockView>.updateTableOfContentsViews(header: BlockView.Text.Header): List<BlockView> {
@ -1017,6 +1023,6 @@ fun List<BlockView>.fillTableOfContents(): List<BlockView> {
}
}
fun BlockView.Text.isStyleClearable() : Boolean {
fun BlockView.Text.isStyleClearable(): Boolean {
return this.isListBlock || this is BlockView.Text.Highlight
}

View file

@ -1,5 +1,6 @@
package com.anytypeio.anytype.presentation.editor.editor.model
import com.anytypeio.anytype.core_models.Block.Content.Link.*
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.Url
import com.anytypeio.anytype.core_utils.ui.ViewType
@ -170,38 +171,43 @@ sealed class BlockView : ViewType {
}
interface Appearance {
data class Params(
val canHaveIcon: Boolean,
val canHaveCover: Boolean,
val canHaveDescription: Boolean,
val style: Double,
val iconSize: Double,
val withIcon: Boolean,
val withName: Boolean,
val withDescription: Boolean? = null,
val withCover: Boolean? = null
) {
companion object {
fun default(): Params = Params(
style = LINK_STYLE_TEXT,
iconSize = LINK_ICON_SIZE_SMALL,
withIcon = true,
withName = true,
canHaveCover = true,
canHaveDescription = true,
canHaveIcon = true
)
data class InEditor(
val isCard: Boolean,
val showIcon: Boolean,
val showName: Boolean,
val showDescription: Boolean,
val showCover: Boolean
)
data class Menu(
val preview: MenuItem.PreviewLayout,
val icon: MenuItem.Icon?,
val cover: MenuItem.Cover?,
val description: MenuItem.Description?
)
sealed interface MenuItem {
sealed interface Icon : MenuItem {
object NONE : Icon
object SMALL : Icon
object MEDIUM : Icon
}
fun isCard() = style == LINK_STYLE_CARD
}
sealed interface Description : MenuItem {
object WITH : Description
object WITHOUT : Description
}
companion object {
const val LINK_STYLE_TEXT = 0.0
const val LINK_STYLE_CARD = 1.0
const val LINK_ICON_SIZE_SMALL = 1.0
const val LINK_ICON_SIZE_MEDIUM = 2.0
const val LINK_ICON_SIZE_LARGE = 3.0
sealed interface Cover : MenuItem {
object WITH : Cover
object WITHOUT : Cover
}
sealed interface PreviewLayout : MenuItem {
object TEXT : PreviewLayout
object CARD : PreviewLayout
}
}
}
@ -461,9 +467,10 @@ sealed class BlockView : ViewType {
) : Text() {
override fun getViewType() = HOLDER_TOGGLE
override val body: String get() = text
val isCreateBlockButtonVisible : Boolean get() {
return mode == Mode.EDIT && toggled && isEmpty
}
val isCreateBlockButtonVisible: Boolean
get() {
return mode == Mode.EDIT && toggled && isEmpty
}
}
}
@ -895,7 +902,7 @@ sealed class BlockView : ViewType {
* @property isDeleted this property determines whether this linked object is deleted or not.
* Whenever isDeleted is true, we don't care about isArchived flags
*/
sealed class Default: LinkToObject() {
sealed class Default : LinkToObject() {
abstract val text: String?
abstract val description: String?
@ -1061,7 +1068,7 @@ sealed class BlockView : ViewType {
data class TableOfContentsItem(
val id: Id,
val name: String,
val depth : Int
val depth: Int
)
enum class Mode { READ, EDIT }

View file

@ -28,11 +28,7 @@ import com.anytypeio.anytype.presentation.mapper.toPictureView
import com.anytypeio.anytype.presentation.mapper.toVideoView
import com.anytypeio.anytype.presentation.mapper.toView
import com.anytypeio.anytype.presentation.objects.ObjectIcon
import com.anytypeio.anytype.presentation.objects.appearance.ObjectAppearanceIconState.NONE
import com.anytypeio.anytype.presentation.objects.appearance.ObjectAppearancePreviewLayoutState.CARD
import com.anytypeio.anytype.presentation.objects.appearance.getLinkToObjectAppearanceParams
import com.anytypeio.anytype.presentation.objects.appearance.getObjectAppearanceIconState
import com.anytypeio.anytype.presentation.objects.appearance.getObjectAppearancePreviewLayoutState
import com.anytypeio.anytype.presentation.objects.appearance.LinkAppearanceFactory
import com.anytypeio.anytype.presentation.relations.DocumentRelationView
import com.anytypeio.anytype.presentation.relations.view
import timber.log.Timber
@ -458,6 +454,7 @@ class DefaultBlockViewRenderer @Inject constructor(
)
val link = toLinks(
block = block,
content = content,
indent = indent,
obj = obj,
mode = mode,
@ -1260,6 +1257,7 @@ class DefaultBlockViewRenderer @Inject constructor(
private fun toLinks(
block: Block,
content: Content.Link,
indent: Int,
obj: ObjectWrapper.Basic,
mode: EditorMode,
@ -1293,6 +1291,7 @@ class DefaultBlockViewRenderer @Inject constructor(
} else {
link(
block = block,
content = content,
indent = indent,
obj = obj,
mode = mode,
@ -1306,63 +1305,66 @@ class DefaultBlockViewRenderer @Inject constructor(
private fun link(
mode: Editor.Mode,
block: Block,
content: Content.Link,
indent: Int,
obj: ObjectWrapper.Basic,
selection: Set<Id>,
isPreviousBlockMedia: Boolean
): BlockView.LinkToObject.Default {
val appearanceParams = block.fields.getLinkToObjectAppearanceParams(obj.layout)
val isCard = appearanceParams.getObjectAppearancePreviewLayoutState() == CARD
val icon = if (appearanceParams.getObjectAppearanceIconState() == NONE) {
ObjectIcon.None
} else {
val factory = LinkAppearanceFactory(content, obj.layout)
val inEditorAppearance = factory.createInEditorLinkAppearance()
val isCard = inEditorAppearance.isCard
val icon = if (inEditorAppearance.showIcon) {
ObjectIcon.from(
obj = obj,
layout = obj.layout,
builder = urlBuilder
)
}
val name = if (!appearanceParams.withName) {
null
} else {
ObjectIcon.None
}
val name = if (inEditorAppearance.showName) {
obj.getProperObjectName()
}
val description = if (isCard && appearanceParams.withDescription == true) {
if (obj.description.isNullOrBlank()) obj.snippet else obj.description
} else {
null
}
var coverColor: CoverColor? = null
var coverImage: Url? = null
var coverGradient: String? = null
if (isCard && appearanceParams.canHaveCover && appearanceParams.withCover == true) {
when (val type = obj.coverType) {
CoverType.UPLOADED_IMAGE -> {
coverImage = obj.coverId?.let { id ->
urlBuilder.image(id)
}
}
CoverType.BUNDLED_IMAGE -> {
val hash = obj.coverId?.let { id ->
coverImageHashProvider.provide(id)
}
if (hash != null) coverImage = urlBuilder.image(hash)
}
CoverType.COLOR -> {
coverColor = obj.coverId?.let { id ->
CoverColor.values().find { it.code == id }
}
}
CoverType.GRADIENT -> {
coverGradient = obj.coverId
}
else -> Timber.d("Missing cover type: $type")
}
}
return if (isCard) {
val description = if (inEditorAppearance.showDescription) {
if (obj.description.isNullOrBlank()) obj.snippet else obj.description
} else {
null
}
var coverColor: CoverColor? = null
var coverImage: Url? = null
var coverGradient: String? = null
if (inEditorAppearance.showCover) {
when (val type = obj.coverType) {
CoverType.UPLOADED_IMAGE -> {
coverImage = obj.coverId?.let { id ->
urlBuilder.image(id)
}
}
CoverType.BUNDLED_IMAGE -> {
val hash = obj.coverId?.let { id ->
coverImageHashProvider.provide(id)
}
if (hash != null) coverImage = urlBuilder.image(hash)
}
CoverType.COLOR -> {
coverColor = obj.coverId?.let { id ->
CoverColor.values().find { it.code == id }
}
}
CoverType.GRADIENT -> {
coverGradient = obj.coverId
}
else -> Timber.d("Missing cover type: $type")
}
}
BlockView.LinkToObject.Default.Card(
id = block.id,
icon = icon,

View file

@ -1,14 +1,21 @@
package com.anytypeio.anytype.presentation.mapper
import com.anytypeio.anytype.core_models.*
import com.anytypeio.anytype.core_models.ext.textColor
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.DVFilterOperator
import com.anytypeio.anytype.core_models.DVSort
import com.anytypeio.anytype.core_models.DocumentInfo
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.ObjectType
import com.anytypeio.anytype.core_models.ObjectWrapper
import com.anytypeio.anytype.core_models.Relation
import com.anytypeio.anytype.core_models.Relations
import com.anytypeio.anytype.core_models.ext.textStyle
import com.anytypeio.anytype.domain.config.DebugSettings
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.presentation.dashboard.DashboardView
import com.anytypeio.anytype.presentation.editor.editor.Markup
import com.anytypeio.anytype.presentation.editor.editor.ThemeColor
import com.anytypeio.anytype.presentation.editor.editor.control.ControlPanelState
import com.anytypeio.anytype.presentation.editor.editor.mention.createMentionMarkup
import com.anytypeio.anytype.presentation.editor.editor.model.Alignment
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
@ -20,7 +27,12 @@ import com.anytypeio.anytype.presentation.objects.ObjectTypeView
import com.anytypeio.anytype.presentation.objects.getProperName
import com.anytypeio.anytype.presentation.relations.type
import com.anytypeio.anytype.presentation.sets.buildGridRow
import com.anytypeio.anytype.presentation.sets.model.*
import com.anytypeio.anytype.presentation.sets.model.ColumnView
import com.anytypeio.anytype.presentation.sets.model.FilterExpression
import com.anytypeio.anytype.presentation.sets.model.FilterValue
import com.anytypeio.anytype.presentation.sets.model.SimpleRelationView
import com.anytypeio.anytype.presentation.sets.model.SortingExpression
import com.anytypeio.anytype.presentation.sets.model.Viewer
import com.anytypeio.anytype.presentation.settings.EditorSettings
fun Block.Content.File.toPictureView(
@ -595,11 +607,11 @@ fun List<Map<String, Any?>>.filterRecordsBy(filterBy: String): List<Map<String,
filter { it.containsKey(filterBy) }
fun List<Map<String, Any?>>.toGridRecordRows(
columns: List<ColumnView>,
relations: List<Relation>,
types: List<ObjectType>,
details: Map<Id, Block.Fields>,
builder: UrlBuilder
columns: List<ColumnView>,
relations: List<Relation>,
types: List<ObjectType>,
details: Map<Id, Block.Fields>,
builder: UrlBuilder
): List<Viewer.GridView.Row> {
val rows = mutableListOf<Viewer.GridView.Row>()
forEach { record ->
@ -617,8 +629,8 @@ fun List<Map<String, Any?>>.toGridRecordRows(
// TODO maybe rename toViewerHeaders
fun List<Block.Content.DataView.Viewer.ViewerRelation>.toViewerColumns(
relations: List<Relation>,
filterBy: List<String>
relations: List<Relation>,
filterBy: List<String>
): List<ColumnView> {
val columns = mutableListOf<ColumnView>()
this.filter { it.key !in filterBy }

View file

@ -1,6 +1,7 @@
package com.anytypeio.anytype.presentation.objects
import com.anytypeio.anytype.presentation.objects.appearance.ObjectAppearanceIconState
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView.Appearance.MenuItem
sealed class ObjectAppearanceSettingView {
@ -9,21 +10,20 @@ sealed class ObjectAppearanceSettingView {
}
sealed class Settings : ObjectAppearanceSettingView() {
data class PreviewLayout(val style: Double?) : Settings()
data class Icon(val state: ObjectAppearanceIconState) : Settings()
data class Cover(val withCover: Boolean?) : Settings()
data class PreviewLayout(val previewLayoutState: MenuItem.PreviewLayout) : Settings()
data class Icon(val icon: MenuItem.Icon) : Settings()
data class Cover(val coverState: MenuItem.Cover) : Settings()
}
sealed class Relation : ObjectAppearanceSettingView() {
data class Name(val withName: Boolean?) : Relation()
data class Description(val withDescription: Boolean?) : Relation()
object Name: Relation()
data class Description(val description: MenuItem.Description) : Relation()
}
sealed class Icon : ObjectAppearanceSettingView() {
data class None(val isSelected: Boolean) : Icon()
data class Small(val isSelected: Boolean) : Icon()
data class Medium(val isSelected: Boolean) : Icon()
data class Large(val isSelected: Boolean) : Icon()
data class None(val isSelected: Boolean) : Icon()
}
sealed class Cover : ObjectAppearanceSettingView() {

View file

@ -3,37 +3,37 @@ package com.anytypeio.anytype.presentation.objects
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.Block.Content.Link
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.Payload
import com.anytypeio.anytype.domain.block.interactor.UpdateFields
import com.anytypeio.anytype.domain.block.interactor.SetLinkAppearance
import com.anytypeio.anytype.presentation.editor.Editor
import com.anytypeio.anytype.presentation.editor.editor.ext.getAppearanceParamsOfBlockLink
import com.anytypeio.anytype.presentation.editor.editor.ext.getLinkAppearanceMenu
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
import com.anytypeio.anytype.presentation.objects.appearance.getObjectAppearanceIconState
import com.anytypeio.anytype.presentation.objects.ObjectAppearanceSettingView.Relation
import com.anytypeio.anytype.presentation.objects.ObjectAppearanceSettingView.Section
import com.anytypeio.anytype.presentation.objects.ObjectAppearanceSettingView.Settings
import com.anytypeio.anytype.presentation.util.Dispatcher
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.launch
import timber.log.Timber
class ObjectAppearanceSettingViewModel(
private val storage: Editor.Storage,
private val updateFields: UpdateFields,
private val setLinkAppearance: SetLinkAppearance,
private val dispatcher: Dispatcher<Payload>
) : ViewModel() {
val objectPreviewState = MutableSharedFlow<State>(replay = 0)
val commands = MutableSharedFlow<Command>(replay = 0)
private val paramsAppearance = MutableSharedFlow<BlockView.Appearance.Params?>()
private val paramsAppearance = MutableSharedFlow<BlockView.Appearance.Menu>()
private val jobs = mutableListOf<Job>()
init {
viewModelScope.launch {
paramsAppearance
.filterNotNull()
.collectLatest { onNewParams(it) }
}
}
@ -41,50 +41,51 @@ class ObjectAppearanceSettingViewModel(
fun onStart(blockId: Id) {
jobs += viewModelScope.launch {
storage.document.observe().collectLatest { state ->
val params = state.getAppearanceParamsOfBlockLink(
val menu = state.getLinkAppearanceMenu(
blockId = blockId,
details = storage.details.current()
)
paramsAppearance.emit(params)
if (menu != null) {
paramsAppearance.emit(menu)
}
}
}
}
private fun onNewParams(params: BlockView.Appearance.Params) {
private fun onNewParams(menu: BlockView.Appearance.Menu) {
viewModelScope.launch {
val views = initSettingsMenu(params)
val views = initSettingsMenu(menu)
objectPreviewState.emit(State.Success(views))
}
}
private fun initSettingsMenu(params: BlockView.Appearance.Params): List<ObjectAppearanceSettingView> {
val menus = mutableListOf<ObjectAppearanceSettingView>()
menus.add(ObjectAppearanceSettingView.Settings.PreviewLayout(params.style))
if (params.canHaveIcon) {
val iconState = params.getObjectAppearanceIconState()
menus.add(ObjectAppearanceSettingView.Settings.Icon(iconState))
private fun initSettingsMenu(menu: BlockView.Appearance.Menu): List<ObjectAppearanceSettingView> {
return buildList {
add(Settings.PreviewLayout(menu.preview))
if (menu.icon != null) {
add(Settings.Icon(menu.icon))
}
if (menu.cover != null) {
add(Settings.Cover(menu.cover))
}
add(Section.FeaturedRelations)
add(Relation.Name)
if (menu.description != null) {
add(Relation.Description(menu.description))
}
}
if (params.canHaveCover) {
menus.add(ObjectAppearanceSettingView.Settings.Cover(params.withCover))
}
menus.add(ObjectAppearanceSettingView.Section.FeaturedRelations)
menus.add(ObjectAppearanceSettingView.Relation.Name(params.withName))
if (params.canHaveDescription) {
menus.add(ObjectAppearanceSettingView.Relation.Description(params.withDescription))
}
return menus
}
fun onItemClicked(item: ObjectAppearanceSettingView) {
viewModelScope.launch {
when (item) {
is ObjectAppearanceSettingView.Settings.Cover -> {
is Settings.Cover -> {
commands.emit(Command.CoverScreen)
}
is ObjectAppearanceSettingView.Settings.Icon -> {
is Settings.Icon -> {
commands.emit(Command.IconScreen)
}
is ObjectAppearanceSettingView.Settings.PreviewLayout -> {
is Settings.PreviewLayout -> {
commands.emit(Command.PreviewLayoutScreen)
}
else -> {}
@ -93,48 +94,37 @@ class ObjectAppearanceSettingViewModel(
}
fun onToggleClicked(
item: ObjectAppearanceSettingView,
description: Relation.Description,
ctx: Id,
blockId: Id,
isChecked: Boolean
) {
val block = storage.document.get().firstOrNull { it.id == blockId }
if (block != null) {
when (item) {
is ObjectAppearanceSettingView.Relation.Description -> {
if (isChecked != block.fields.withDescription) {
val fields = block.fields.copy(
map = block.fields.map.toMutableMap().apply {
put(Block.Fields.WITH_DESCRIPTION_KEY, isChecked)
}
)
proceedWithFieldsUpdate(ctx, blockId, fields)
val content = block?.content
if (block != null && content is Link) {
if (isChecked != content.hasDescription) {
val newContent = content.copy(
description = if (isChecked) {
Link.Description.ADDED
} else {
Link.Description.NONE
}
}
is ObjectAppearanceSettingView.Relation.Name -> {
if (isChecked != block.fields.withName) {
val fields = block.fields.copy(
map = block.fields.map.toMutableMap().apply {
put(Block.Fields.WITH_NAME_KEY, isChecked)
}
)
proceedWithFieldsUpdate(ctx, blockId, fields)
}
}
else -> throw UnsupportedOperationException("Wrong item type:$item")
)
setLinkAppearance(ctx, blockId, newContent)
}
}
}
private fun proceedWithFieldsUpdate(ctx: Id, blockId: Id, fields: Block.Fields) {
private fun setLinkAppearance(ctx: Id, blockId: Id, content: Link) {
viewModelScope.launch {
updateFields.invoke(
UpdateFields.Params(
context = ctx,
fields = listOf(Pair(blockId, fields))
setLinkAppearance(
SetLinkAppearance.Params(
contextId = ctx,
blockId = blockId,
content = content
)
).proceed(
failure = { Timber.e(it, "Error while updating icon size for object") },
failure = { Timber.e(it, "Error while set link appearance") },
success = { dispatcher.send(it) }
)
}
@ -156,13 +146,13 @@ class ObjectAppearanceSettingViewModel(
//region FACTORY
class Factory(
private val storage: Editor.Storage,
private val updateFields: UpdateFields,
private val setLinkAppearance: SetLinkAppearance,
private val dispatcher: Dispatcher<Payload>
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return ObjectAppearanceSettingViewModel(storage, updateFields, dispatcher) as T
return ObjectAppearanceSettingViewModel(storage, setLinkAppearance, dispatcher) as T
}
}
//endregion

View file

@ -0,0 +1,75 @@
package com.anytypeio.anytype.presentation.objects.appearance
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.Block.Content.Link.CardStyle
import com.anytypeio.anytype.core_models.Block.Content.Link.IconSize
import com.anytypeio.anytype.core_models.ObjectType
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView.Appearance.MenuItem
internal class LinkAppearanceFactory(
private val content: Block.Content.Link,
layout: ObjectType.Layout?
) {
private val isTodoLayout = layout == ObjectType.Layout.TODO
private val isNoteLayout = layout == ObjectType.Layout.NOTE
private val withDescription = content.cardStyle != CardStyle.TEXT && !isNoteLayout
//todo Cover menu option is off. No proper design yet.
private val canHaveCover: Boolean =
false && !isNoteLayout && content.cardStyle != CardStyle.TEXT
private val withCover = canHaveCover && (content.hasCover)
internal fun createInEditorLinkAppearance(): BlockView.Appearance.InEditor {
val withIcon = when {
isNoteLayout -> false
isTodoLayout -> true
else -> content.iconSize != IconSize.NONE
}
return BlockView.Appearance.InEditor(
showIcon = withIcon,
isCard = content.cardStyle == CardStyle.CARD,
showName = true,
showDescription = withDescription && content.hasDescription,
showCover = withCover
)
}
internal fun createAppearanceMenuItems(): BlockView.Appearance.Menu {
val hasIconMenuItem = !isTodoLayout && !isNoteLayout
val preview = when (content.cardStyle) {
CardStyle.TEXT -> MenuItem.PreviewLayout.TEXT
CardStyle.CARD,
CardStyle.INLINE -> MenuItem.PreviewLayout.CARD
}
val icon = if (hasIconMenuItem) {
when (content.iconSize) {
IconSize.NONE -> MenuItem.Icon.NONE
IconSize.SMALL -> MenuItem.Icon.SMALL
IconSize.MEDIUM -> MenuItem.Icon.MEDIUM
}
} else null
val cover = if (canHaveCover) {
when (withCover) {
true -> MenuItem.Cover.WITH
false -> MenuItem.Cover.WITHOUT
}
} else null
val description = if (withDescription) {
when (content.description) {
Block.Content.Link.Description.NONE -> MenuItem.Description.WITHOUT
Block.Content.Link.Description.ADDED,
Block.Content.Link.Description.CONTENT -> MenuItem.Description.WITH
}
} else null
return BlockView.Appearance.Menu(
preview = preview,
icon = icon,
cover = cover,
description = description
)
}
}

View file

@ -3,12 +3,13 @@ package com.anytypeio.anytype.presentation.objects.appearance
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.Block.Content.Link
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.Payload
import com.anytypeio.anytype.domain.block.interactor.UpdateFields
import com.anytypeio.anytype.domain.block.interactor.SetLinkAppearance
import com.anytypeio.anytype.presentation.editor.Editor
import com.anytypeio.anytype.presentation.editor.editor.ext.getAppearanceParamsOfBlockLink
import com.anytypeio.anytype.presentation.editor.editor.ext.getLinkAppearanceMenu
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView.Appearance.MenuItem
import com.anytypeio.anytype.presentation.objects.ObjectAppearanceSettingView
import com.anytypeio.anytype.presentation.util.Dispatcher
import kotlinx.coroutines.Job
@ -18,7 +19,7 @@ import timber.log.Timber
class ObjectAppearanceCoverViewModel(
private val storage: Editor.Storage,
private val updateFields: UpdateFields,
private val setLinkAppearance: SetLinkAppearance,
private val dispatcher: Dispatcher<Payload>
) : ViewModel() {
@ -27,20 +28,20 @@ class ObjectAppearanceCoverViewModel(
fun onStart(blockId: Id) {
jobs += viewModelScope.launch {
val params = storage.document.get().getAppearanceParamsOfBlockLink(
val menu = storage.document.get().getLinkAppearanceMenu(
blockId = blockId,
details = storage.details.current()
)
if (params != null) {
val coverState = params.getObjectAppearanceCoverState()
if (menu != null) {
val coverState = menu.cover
state.emit(
State.Success(
items = listOf(
ObjectAppearanceSettingView.Cover.None(
isSelected = coverState == ObjectAppearanceCoverState.NONE
isSelected = coverState == MenuItem.Cover.WITHOUT
),
ObjectAppearanceSettingView.Cover.Visible(
isSelected = coverState == ObjectAppearanceCoverState.VISIBLE
isSelected = coverState == MenuItem.Cover.WITH
)
)
)
@ -52,34 +53,34 @@ class ObjectAppearanceCoverViewModel(
}
}
fun onItemClicked(item: ObjectAppearanceSettingView, ctx: Id, blockId: Id) {
fun onItemClicked(item: ObjectAppearanceSettingView.Cover, ctx: Id, blockId: Id) {
val block = storage.document.get().firstOrNull { it.id == blockId }
if (block != null) {
val fields = when (item) {
is ObjectAppearanceSettingView.Cover.None ->
block.fields.copy(
map = block.fields.map.toMutableMap().apply {
put(Block.Fields.COVER_WITH_KEY, false)
}
val content = block?.content
if (block != null && content is Link) {
val relations = content.relations
val newContent: Link = when (item) {
is ObjectAppearanceSettingView.Cover.None -> {
content.copy(
relations = relations - Link.Relation.COVER
)
is ObjectAppearanceSettingView.Cover.Visible ->
block.fields.copy(
map = block.fields.map.toMutableMap().apply {
put(Block.Fields.COVER_WITH_KEY, true)
}
}
is ObjectAppearanceSettingView.Cover.Visible -> {
content.copy(
relations = relations + Link.Relation.COVER
)
else -> throw UnsupportedOperationException("Wrong item type:$item")
}
}
proceedWithFieldsUpdate(ctx, blockId, fields)
proceedWithFieldsUpdate(ctx, blockId, newContent)
}
}
private fun proceedWithFieldsUpdate(ctx: Id, blockId: Id, fields: Block.Fields) {
private fun proceedWithFieldsUpdate(ctx: Id, blockId: Id, content: Link) {
viewModelScope.launch {
updateFields.invoke(
UpdateFields.Params(
context = ctx,
fields = listOf(Pair(blockId, fields))
setLinkAppearance(
SetLinkAppearance.Params(
contextId = ctx,
blockId = blockId,
content = content
)
).proceed(
failure = { Timber.e(it, "Error while updating cover visibility for block") },
@ -97,12 +98,12 @@ class ObjectAppearanceCoverViewModel(
class Factory(
private val storage: Editor.Storage,
private val updateFields: UpdateFields,
private val setLinkAppearance: SetLinkAppearance,
private val dispatcher: Dispatcher<Payload>
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return ObjectAppearanceCoverViewModel(storage, updateFields, dispatcher) as T
return ObjectAppearanceCoverViewModel(storage, setLinkAppearance, dispatcher) as T
}
}

View file

@ -1,87 +0,0 @@
package com.anytypeio.anytype.presentation.objects.appearance
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.ObjectType
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView.Appearance.Companion.LINK_ICON_SIZE_LARGE
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView.Appearance.Companion.LINK_ICON_SIZE_MEDIUM
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView.Appearance.Companion.LINK_ICON_SIZE_SMALL
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView.Appearance.Companion.LINK_STYLE_CARD
enum class ObjectAppearanceIconState { NONE, SMALL, MEDIUM, LARGE, UNKNOWN }
enum class ObjectAppearanceCoverState { NONE, VISIBLE }
enum class ObjectAppearancePreviewLayoutState { TEXT, CARD }
fun BlockView.Appearance.Params.getObjectAppearanceIconState(): ObjectAppearanceIconState {
return when {
!withIcon -> ObjectAppearanceIconState.NONE
iconSize == LINK_ICON_SIZE_SMALL -> ObjectAppearanceIconState.SMALL
iconSize == LINK_ICON_SIZE_MEDIUM -> ObjectAppearanceIconState.MEDIUM
iconSize == LINK_ICON_SIZE_LARGE -> ObjectAppearanceIconState.LARGE
else -> ObjectAppearanceIconState.UNKNOWN
}
}
fun BlockView.Appearance.Params.getObjectAppearanceCoverState(): ObjectAppearanceCoverState {
return if (withCover == true) {
ObjectAppearanceCoverState.VISIBLE
} else {
ObjectAppearanceCoverState.NONE
}
}
fun BlockView.Appearance.Params.getObjectAppearancePreviewLayoutState(): ObjectAppearancePreviewLayoutState {
return if (style == LINK_STYLE_CARD) {
ObjectAppearancePreviewLayoutState.CARD
} else {
ObjectAppearancePreviewLayoutState.TEXT
}
}
fun Block.Fields.getLinkToObjectAppearanceParams(layout: ObjectType.Layout?): BlockView.Appearance.Params {
var canHaveIcon = true
//todo Cover menu option is off. No proper design yet.
var canHaveCover = false
var canHaveDescription = true
var iconSize = this.iconSize ?: LINK_ICON_SIZE_MEDIUM
val style = this.style ?: BlockView.Appearance.LINK_STYLE_TEXT
var withIcon = this.withIcon ?: true
val withName = this.withName ?: true
var withCover = this.withCover
var withDescription = this.withDescription
if (this.style == BlockView.Appearance.LINK_STYLE_TEXT) {
//canHaveCover = false
canHaveDescription = false
}
if (layout == ObjectType.Layout.TODO) {
canHaveIcon = false
withIcon = true
iconSize = LINK_ICON_SIZE_MEDIUM
}
if (layout == ObjectType.Layout.NOTE) {
canHaveIcon = false
//canHaveCover = false
canHaveDescription = false
withIcon = false
withCover = false
withDescription = false
iconSize = LINK_ICON_SIZE_MEDIUM
}
return BlockView.Appearance.Params(
canHaveIcon = canHaveIcon,
canHaveCover = canHaveCover,
canHaveDescription = canHaveDescription,
iconSize = iconSize,
style = style,
withIcon = withIcon,
withName = withName,
withCover = withCover,
withDescription = withDescription
)
}

View file

@ -4,19 +4,13 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.Block.Fields.Companion.ICON_SIZE_KEY
import com.anytypeio.anytype.core_models.Block.Fields.Companion.ICON_WITH_KEY
import com.anytypeio.anytype.core_models.Block.Fields.Companion.WITH_DESCRIPTION_KEY
import com.anytypeio.anytype.core_models.Block.Fields.Companion.WITH_NAME_KEY
import com.anytypeio.anytype.core_models.Block.Content.Link.IconSize
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.Payload
import com.anytypeio.anytype.domain.block.interactor.UpdateFields
import com.anytypeio.anytype.domain.block.interactor.SetLinkAppearance
import com.anytypeio.anytype.presentation.editor.Editor
import com.anytypeio.anytype.presentation.editor.editor.ext.getAppearanceParamsOfBlockLink
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView.Appearance.Companion.LINK_ICON_SIZE_LARGE
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView.Appearance.Companion.LINK_ICON_SIZE_MEDIUM
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView.Appearance.Companion.LINK_ICON_SIZE_SMALL
import com.anytypeio.anytype.presentation.objects.ObjectAppearanceSettingView
import com.anytypeio.anytype.presentation.editor.editor.ext.getLinkAppearanceMenu
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView.Appearance.MenuItem
import com.anytypeio.anytype.presentation.objects.ObjectAppearanceSettingView.Icon
import com.anytypeio.anytype.presentation.util.Dispatcher
import kotlinx.coroutines.Job
@ -26,7 +20,7 @@ import timber.log.Timber
class ObjectAppearanceIconViewModel(
private val storage: Editor.Storage,
private val updateFields: UpdateFields,
private val setLinkAppearance: SetLinkAppearance,
private val dispatcher: Dispatcher<Payload>
) : ViewModel() {
@ -35,21 +29,18 @@ class ObjectAppearanceIconViewModel(
fun onStart(blockId: Id) {
jobs += viewModelScope.launch {
val params = storage.document.get().getAppearanceParamsOfBlockLink(
val menu = storage.document.get().getLinkAppearanceMenu(
blockId = blockId,
details = storage.details.current()
)
if (params != null) {
val iconState = params.getObjectAppearanceIconState()
if (menu != null) {
val iconState = menu.icon
state.emit(
State.Success(
items = listOf(
Icon.None(isSelected = iconState == ObjectAppearanceIconState.NONE),
// TODO small icons will be handled later
//Icon.Small(isSelected = iconState == ObjectAppearanceIconState.SMALL),
Icon.Medium(isSelected = iconState == ObjectAppearanceIconState.MEDIUM)
// TODO large icons will be handled later
//Icon.Large(isSelected = iconState == ObjectAppearanceIconState.LARGE)
Icon.None(isSelected = iconState == MenuItem.Icon.NONE),
Icon.Small(isSelected = iconState == MenuItem.Icon.SMALL),
Icon.Medium(isSelected = iconState == MenuItem.Icon.MEDIUM)
)
)
)
@ -64,52 +55,37 @@ class ObjectAppearanceIconViewModel(
jobs.forEach { it.cancel() }
}
fun onItemClicked(item: ObjectAppearanceSettingView, ctx: Id, blockId: Id) {
fun onItemClicked(item: Icon, ctx: Id, blockId: Id) {
val block = storage.document.get().firstOrNull { it.id == blockId }
if (block != null) {
val fields = when (item) {
is Icon.Large ->
block.fields.copy(
map = block.fields.map.toMutableMap().apply {
put(ICON_SIZE_KEY, LINK_ICON_SIZE_LARGE)
put(ICON_WITH_KEY, true)
}
)
is Icon.Medium ->
block.fields.copy(
map = block.fields.map.toMutableMap().apply {
put(ICON_SIZE_KEY, LINK_ICON_SIZE_MEDIUM)
put(ICON_WITH_KEY, true)
}
)
val content = block?.content
if (block != null && content is Block.Content.Link) {
val newContent = when (item) {
is Icon.Medium -> content.copy(
iconSize = IconSize.MEDIUM
)
is Icon.Small ->
block.fields.copy(
map = block.fields.map.toMutableMap().apply {
put(ICON_SIZE_KEY, LINK_ICON_SIZE_SMALL)
put(ICON_WITH_KEY, true)
}
content.copy(
iconSize = IconSize.SMALL
)
is Icon.None ->
block.fields.copy(
map = block.fields.map.toMutableMap().apply {
put(ICON_WITH_KEY, false)
}
content.copy(
iconSize = IconSize.NONE
)
else -> throw UnsupportedOperationException("Wrong item type:$item")
}
proceedWithFieldsUpdate(ctx, blockId, fields)
proceedWithFieldsUpdate(ctx, blockId, newContent)
}
}
private fun proceedWithFieldsUpdate(ctx: Id, blockId: Id, fields: Block.Fields) {
private fun proceedWithFieldsUpdate(ctx: Id, blockId: Id, content: Block.Content.Link) {
viewModelScope.launch {
updateFields.invoke(
UpdateFields.Params(
context = ctx,
fields = listOf(Pair(blockId, fields))
setLinkAppearance(
SetLinkAppearance.Params(
contextId = ctx,
blockId = blockId,
content = content
)
).proceed(
failure = { Timber.e(it, "Error while updating icon size for object") },
failure = { Timber.e(it, "Error while updating icon link appearance") },
success = {
dispatcher.send(it)
state.emit(State.Dismiss)
@ -120,12 +96,12 @@ class ObjectAppearanceIconViewModel(
class Factory(
private val storage: Editor.Storage,
private val updateFields: UpdateFields,
private val setLinkAppearance: SetLinkAppearance,
private val dispatcher: Dispatcher<Payload>
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return ObjectAppearanceIconViewModel(storage, updateFields, dispatcher) as T
return ObjectAppearanceIconViewModel(storage, setLinkAppearance, dispatcher) as T
}
}

View file

@ -4,14 +4,12 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.Block.Fields.Companion.STYLE_KEY
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.Payload
import com.anytypeio.anytype.domain.block.interactor.UpdateFields
import com.anytypeio.anytype.domain.block.interactor.SetLinkAppearance
import com.anytypeio.anytype.presentation.editor.Editor
import com.anytypeio.anytype.presentation.editor.editor.ext.getAppearanceParamsOfBlockLink
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView.Appearance.Companion.LINK_STYLE_CARD
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView.Appearance.Companion.LINK_STYLE_TEXT
import com.anytypeio.anytype.presentation.editor.editor.ext.getLinkAppearanceMenu
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView.Appearance.MenuItem.PreviewLayout
import com.anytypeio.anytype.presentation.objects.ObjectAppearanceSettingView
import com.anytypeio.anytype.presentation.util.Dispatcher
import kotlinx.coroutines.Job
@ -21,7 +19,7 @@ import timber.log.Timber
class ObjectAppearancePreviewLayoutViewModel(
private val storage: Editor.Storage,
private val updateFields: UpdateFields,
private val setLinkAppearance: SetLinkAppearance,
private val dispatcher: Dispatcher<Payload>
) : ViewModel() {
@ -30,20 +28,20 @@ class ObjectAppearancePreviewLayoutViewModel(
fun onStart(blockId: Id) {
jobs += viewModelScope.launch {
val params = storage.document.get().getAppearanceParamsOfBlockLink(
val menu = storage.document.get().getLinkAppearanceMenu(
blockId = blockId,
details = storage.details.current()
)
if (params != null) {
val previewLayout = params.getObjectAppearancePreviewLayoutState()
if (menu != null) {
val previewLayout = menu.preview
state.emit(
State.Success(
items = listOf(
ObjectAppearanceSettingView.PreviewLayout.Text(
isSelected = previewLayout == ObjectAppearancePreviewLayoutState.TEXT
isSelected = previewLayout == PreviewLayout.TEXT
),
ObjectAppearanceSettingView.PreviewLayout.Card(
isSelected = previewLayout == ObjectAppearancePreviewLayoutState.CARD
isSelected = previewLayout == PreviewLayout.CARD
)
)
)
@ -55,37 +53,40 @@ class ObjectAppearancePreviewLayoutViewModel(
}
}
fun onItemClicked(item: ObjectAppearanceSettingView, ctx: Id, blockId: Id) {
fun onItemClicked(
item: ObjectAppearanceSettingView.PreviewLayout,
ctx: Id,
blockId: Id
) {
val block = storage.document.get().find { it.id == blockId }
if (block != null) {
val fields = when (item) {
val content = block?.content
if (block != null && content is Block.Content.Link) {
val newContent = when (item) {
is ObjectAppearanceSettingView.PreviewLayout.Text ->
block.fields.copy(
map = block.fields.map.toMutableMap().apply {
put(STYLE_KEY, LINK_STYLE_TEXT)
}
content.copy(
cardStyle = Block.Content.Link.CardStyle.TEXT
)
is ObjectAppearanceSettingView.PreviewLayout.Card ->
block.fields.copy(
map = block.fields.map.toMutableMap().apply {
put(STYLE_KEY, LINK_STYLE_CARD)
}
content.copy(
cardStyle = Block.Content.Link.CardStyle.CARD
)
else -> throw UnsupportedOperationException("Wrong item type:$item")
}
proceedWithFieldsUpdate(ctx, blockId, fields)
proceedWithFieldsUpdate(ctx, blockId, newContent)
}
}
private fun proceedWithFieldsUpdate(ctx: Id, blockId: Id, fields: Block.Fields) {
private fun proceedWithFieldsUpdate(ctx: Id, blockId: Id, content: Block.Content.Link) {
viewModelScope.launch {
updateFields.invoke(
UpdateFields.Params(
context = ctx,
fields = listOf(Pair(blockId, fields))
setLinkAppearance(
SetLinkAppearance.Params(
contextId = ctx,
blockId = blockId,
content = content
)
).proceed(
failure = { Timber.e(it, "Error while updating preview layout visibility for block") },
failure = {
Timber.e(it, "Error while updating preview layout visibility for block")
},
success = {
dispatcher.send(it)
state.emit(State.Dismiss)
@ -100,12 +101,16 @@ class ObjectAppearancePreviewLayoutViewModel(
class Factory(
private val storage: Editor.Storage,
private val updateFields: UpdateFields,
private val setLinkAppearance: SetLinkAppearance,
private val dispatcher: Dispatcher<Payload>
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return ObjectAppearancePreviewLayoutViewModel(storage, updateFields, dispatcher) as T
return ObjectAppearancePreviewLayoutViewModel(
storage,
setLinkAppearance,
dispatcher
) as T
}
}

View file

@ -0,0 +1,24 @@
package com.anytypeio.anytype.presentation
import com.anytypeio.anytype.core_models.Block.Content.Link
import com.anytypeio.anytype.test_utils.MockDataFactory
object MockBlockContentFactory {
fun StubLinkContent(
target: String = MockDataFactory.randomUuid(),
type: Link.Type = Link.Type.PAGE,
iconSize: Link.IconSize = Link.IconSize.SMALL,
cardStyle: Link.CardStyle = Link.CardStyle.TEXT,
description: Link.Description = Link.Description.NONE,
relations: Set<Link.Relation> = emptySet(),
): Link = Link(
target = target,
type = type,
iconSize = iconSize,
cardStyle = cardStyle,
description = description,
relations = relations
)
}

View file

@ -1,8 +1,10 @@
package com.anytypeio.anytype.presentation
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.Block.Content.Link
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.SmartBlockType
import com.anytypeio.anytype.presentation.MockBlockContentFactory.StubLinkContent
import com.anytypeio.anytype.test_utils.MockDataFactory
object MockBlockFactory {
@ -18,7 +20,7 @@ object MockBlockFactory {
fun paragraph(
children: List<Id> = emptyList()
) : Block = Block(
): Block = Block(
id = MockDataFactory.randomUuid(),
fields = Block.Fields.empty(),
children = children,
@ -197,6 +199,20 @@ object MockBlockFactory {
)
)
fun link(
id: String = MockDataFactory.randomUuid(),
fields: Block.Fields = Block.Fields(emptyMap()),
content: Link = StubLinkContent(),
children: List<Id> = emptyList(),
backgroundColor: String? = null,
) = Block(
id = id,
fields = fields,
content = content,
children = children,
backgroundColor = backgroundColor
)
fun makeOnePageWithTitleAndOnePageLinkBlock(
rootId: String,
titleBlockId: String,
@ -223,10 +239,13 @@ object MockBlockFactory {
Block(
id = pageBlockId,
fields = Block.Fields(emptyMap()),
content = Block.Content.Link(
content = Link(
target = MockDataFactory.randomUuid(),
fields = Block.Fields.empty(),
type = Block.Content.Link.Type.PAGE
type = Link.Type.PAGE,
iconSize = Link.IconSize.SMALL,
cardStyle = Link.CardStyle.TEXT,
description = Link.Description.NONE,
relations = emptySet()
),
children = emptyList()
)

View file

@ -1,10 +1,16 @@
package com.anytypeio.anytype.presentation.dashboard
import com.anytypeio.anytype.core_models.*
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.Event
import com.anytypeio.anytype.core_models.Payload
import com.anytypeio.anytype.core_models.Position
import com.anytypeio.anytype.core_models.SmartBlockType
import com.anytypeio.anytype.core_utils.ext.shift
import com.anytypeio.anytype.domain.base.Either
import com.anytypeio.anytype.domain.block.interactor.Move
import com.anytypeio.anytype.domain.event.interactor.InterceptEvents
import com.anytypeio.anytype.presentation.MockBlockContentFactory.StubLinkContent
import com.anytypeio.anytype.presentation.MockBlockFactory.link
import com.anytypeio.anytype.presentation.mapper.toDashboardViews
import com.anytypeio.anytype.test_utils.MockDataFactory
import com.jraska.livedata.test
@ -13,7 +19,11 @@ import kotlinx.coroutines.flow.flow
import org.junit.Before
import org.junit.Test
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.*
import org.mockito.kotlin.any
import org.mockito.kotlin.eq
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import org.mockito.kotlin.verifyZeroInteractions
class DashboardDragAndDropTest : DashboardTestSetup() {
@ -35,35 +45,8 @@ class DashboardDragAndDropTest : DashboardTestSetup() {
)
val pages = listOf(
Block(
id = MockDataFactory.randomUuid(),
children = emptyList(),
fields = Block.Fields(map = mapOf("name" to MockDataFactory.randomString())),
content = Block.Content.Link(
target = MockDataFactory.randomUuid(),
type = Block.Content.Link.Type.PAGE,
fields = Block.Fields(
map = mapOf(
"name" to MockDataFactory.randomString(),
"icon" to MockDataFactory.randomString()
)
)
)
),
Block(
id = MockDataFactory.randomUuid(),
children = emptyList(),
fields = Block.Fields(map = mapOf("name" to MockDataFactory.randomString())),
content = Block.Content.Link(
target = MockDataFactory.randomUuid(),
type = Block.Content.Link.Type.PAGE,
fields = Block.Fields(
map = mapOf(
"name" to MockDataFactory.randomString()
)
)
)
)
createBlockLink(),
createBlockLink()
)
val dashboard = Block(
@ -144,34 +127,8 @@ class DashboardDragAndDropTest : DashboardTestSetup() {
fun `should start dispatching drag-and-drop actions when the dragged item is dropped`() {
val pages = listOf(
Block(
id = MockDataFactory.randomUuid(),
children = emptyList(),
fields = Block.Fields(map = mapOf("name" to MockDataFactory.randomString())),
content = Block.Content.Link(
type = Block.Content.Link.Type.PAGE,
target = MockDataFactory.randomUuid(),
fields = Block.Fields(
map = mapOf(
"name" to MockDataFactory.randomString()
)
)
)
),
Block(
id = MockDataFactory.randomUuid(),
children = emptyList(),
fields = Block.Fields(map = mapOf("name" to MockDataFactory.randomString())),
content = Block.Content.Link(
type = Block.Content.Link.Type.PAGE,
target = MockDataFactory.randomUuid(),
fields = Block.Fields(
map = mapOf(
"name" to MockDataFactory.randomString(),
)
)
)
)
createBlockLink(),
createBlockLink()
)
val dashboard = Block(
@ -227,48 +184,9 @@ class DashboardDragAndDropTest : DashboardTestSetup() {
fun `should call move use-case for dropping the last block before the first block`() {
val links = listOf(
Block(
id = MockDataFactory.randomUuid(),
children = emptyList(),
fields = Block.Fields(map = mapOf("name" to MockDataFactory.randomString())),
content = Block.Content.Link(
type = Block.Content.Link.Type.PAGE,
target = MockDataFactory.randomUuid(),
fields = Block.Fields(
map = mapOf(
"name" to MockDataFactory.randomString()
)
)
)
),
Block(
id = MockDataFactory.randomUuid(),
children = emptyList(),
fields = Block.Fields(map = mapOf("name" to MockDataFactory.randomString())),
content = Block.Content.Link(
type = Block.Content.Link.Type.PAGE,
target = MockDataFactory.randomUuid(),
fields = Block.Fields(
map = mapOf(
"name" to MockDataFactory.randomString(),
)
)
)
),
Block(
id = MockDataFactory.randomUuid(),
children = emptyList(),
fields = Block.Fields(map = mapOf("name" to MockDataFactory.randomString())),
content = Block.Content.Link(
type = Block.Content.Link.Type.PAGE,
target = MockDataFactory.randomUuid(),
fields = Block.Fields(
map = mapOf(
"name" to MockDataFactory.randomString(),
)
)
)
)
createBlockLink(),
createBlockLink(),
createBlockLink()
)
val dashboard = Block(
@ -324,48 +242,9 @@ class DashboardDragAndDropTest : DashboardTestSetup() {
fun `should call move use-case for dropping the first block after the second block`() {
val links = listOf(
Block(
id = MockDataFactory.randomUuid(),
children = emptyList(),
fields = Block.Fields(map = mapOf("name" to MockDataFactory.randomString())),
content = Block.Content.Link(
type = Block.Content.Link.Type.PAGE,
target = MockDataFactory.randomUuid(),
fields = Block.Fields(
map = mapOf(
"name" to MockDataFactory.randomString()
)
)
)
),
Block(
id = MockDataFactory.randomUuid(),
children = emptyList(),
fields = Block.Fields(map = mapOf("name" to MockDataFactory.randomString())),
content = Block.Content.Link(
type = Block.Content.Link.Type.PAGE,
target = MockDataFactory.randomUuid(),
fields = Block.Fields(
map = mapOf(
"name" to MockDataFactory.randomString(),
)
)
)
),
Block(
id = MockDataFactory.randomUuid(),
children = emptyList(),
fields = Block.Fields(map = mapOf("name" to MockDataFactory.randomString())),
content = Block.Content.Link(
type = Block.Content.Link.Type.PAGE,
target = MockDataFactory.randomUuid(),
fields = Block.Fields(
map = mapOf(
"name" to MockDataFactory.randomString(),
)
)
)
)
createBlockLink(),
createBlockLink(),
createBlockLink()
)
val dashboard = Block(
@ -421,48 +300,9 @@ class DashboardDragAndDropTest : DashboardTestSetup() {
fun `should call move use-case for dropping the first block after the third block`() {
val links = listOf(
Block(
id = MockDataFactory.randomUuid(),
children = emptyList(),
fields = Block.Fields(map = mapOf("name" to MockDataFactory.randomString())),
content = Block.Content.Link(
type = Block.Content.Link.Type.PAGE,
target = MockDataFactory.randomUuid(),
fields = Block.Fields(
map = mapOf(
"name" to MockDataFactory.randomString()
)
)
)
),
Block(
id = MockDataFactory.randomUuid(),
children = emptyList(),
fields = Block.Fields(map = mapOf("name" to MockDataFactory.randomString())),
content = Block.Content.Link(
type = Block.Content.Link.Type.PAGE,
target = MockDataFactory.randomUuid(),
fields = Block.Fields(
map = mapOf(
"name" to MockDataFactory.randomString(),
)
)
)
),
Block(
id = MockDataFactory.randomUuid(),
children = emptyList(),
fields = Block.Fields(map = mapOf("name" to MockDataFactory.randomString())),
content = Block.Content.Link(
type = Block.Content.Link.Type.PAGE,
target = MockDataFactory.randomUuid(),
fields = Block.Fields(
map = mapOf(
"name" to MockDataFactory.randomString(),
)
)
)
)
createBlockLink(),
createBlockLink(),
createBlockLink()
)
val dashboard = Block(
@ -513,4 +353,12 @@ class DashboardDragAndDropTest : DashboardTestSetup() {
onResult = any()
)
}
fun createBlockLink(): Block =
link(
fields = Block.Fields(map = mapOf("name" to MockDataFactory.randomString())),
content = StubLinkContent(
type = Block.Content.Link.Type.PAGE,
)
)
}

View file

@ -32,6 +32,8 @@ import com.anytypeio.anytype.domain.page.CreatePage
import com.anytypeio.anytype.domain.search.CancelSearchSubscription
import com.anytypeio.anytype.domain.search.ObjectSearchSubscriptionContainer
import com.anytypeio.anytype.domain.templates.GetTemplates
import com.anytypeio.anytype.presentation.MockBlockContentFactory.StubLinkContent
import com.anytypeio.anytype.presentation.MockBlockFactory.link
import com.anytypeio.anytype.presentation.navigation.AppNavigation
import com.anytypeio.anytype.presentation.util.CoroutinesTestRule
import com.anytypeio.anytype.test_utils.MockDataFactory
@ -284,15 +286,8 @@ class HomeDashboardViewModelTest {
val targetId = MockDataFactory.randomUuid()
val page = Block(
id = MockDataFactory.randomUuid(),
children = emptyList(),
fields = Block.Fields.empty(),
content = Block.Content.Link(
target = targetId,
type = Block.Content.Link.Type.PAGE,
fields = Block.Fields.empty()
)
val page = link(
content = StubLinkContent(target = targetId)
)
val dashboard = Block(

View file

@ -1,7 +1,12 @@
package com.anytypeio.anytype.presentation.editor
import android.util.Log
import com.anytypeio.anytype.core_models.*
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.Block.Content.Link
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.ObjectType
import com.anytypeio.anytype.core_models.Relations
import com.anytypeio.anytype.core_models.SmartBlockType
import com.anytypeio.anytype.core_models.ext.asMap
import com.anytypeio.anytype.core_models.ext.content
import com.anytypeio.anytype.core_models.restrictions.ObjectRestriction
@ -10,6 +15,8 @@ import com.anytypeio.anytype.domain.config.Gateway
import com.anytypeio.anytype.domain.editor.Editor
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.emojifier.data.DefaultDocumentEmojiIconProvider
import com.anytypeio.anytype.presentation.MockBlockContentFactory.StubLinkContent
import com.anytypeio.anytype.presentation.MockBlockFactory.link
import com.anytypeio.anytype.presentation.MockTypicalDocumentFactory
import com.anytypeio.anytype.presentation.editor.cover.CoverImageHashProvider
import com.anytypeio.anytype.presentation.editor.editor.Markup
@ -2420,23 +2427,13 @@ class DefaultBlockViewRendererTest {
val header = MockTypicalDocumentFactory.header
val target = MockDataFactory.randomUuid()
val a = Block(
id = MockDataFactory.randomUuid(),
children = listOf(),
content = Block.Content.Link(
val a = link(
content = StubLinkContent(
target = target,
type = Block.Content.Link.Type.PAGE,
fields = Block.Fields.empty()
),
fields = Block.Fields(
mapOf(
"withIcon" to false,
"withCover" to true,
"withDescription" to true,
"withName" to true,
"iconSize" to 1.0,
"style" to 1.0
)
cardStyle = Link.CardStyle.CARD,
iconSize = Link.IconSize.NONE,
description = Link.Description.ADDED,
relations = setOf(Link.Relation.NAME)
),
backgroundColor = "red"
)

View file

@ -1,9 +1,11 @@
package com.anytypeio.anytype.presentation.editor
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.Block.Fields.Companion.NAME_KEY
import com.anytypeio.anytype.core_models.Block.Content.Link.IconSize
import com.anytypeio.anytype.core_models.Event
import com.anytypeio.anytype.core_models.ext.content
import com.anytypeio.anytype.presentation.MockBlockContentFactory.StubLinkContent
import com.anytypeio.anytype.presentation.MockBlockFactory
import com.anytypeio.anytype.test_utils.MockDataFactory
import kotlinx.coroutines.runBlocking
import org.junit.Test
@ -216,31 +218,11 @@ class DocumentExternalEventReducerTest {
}
@Test
fun `should update link fields`() {
val title = Block(
id = MockDataFactory.randomUuid(),
fields = Block.Fields.empty(),
children = emptyList(),
content = Block.Content.Text(
text = MockDataFactory.randomString(),
marks = emptyList(),
style = Block.Content.Text.Style.TITLE
)
)
val name = MockDataFactory.randomString()
val link = Block(
id = MockDataFactory.randomUuid(),
fields = Block.Fields.empty(),
children = emptyList(),
content = Block.Content.Link(
fields = Block.Fields(
map = mapOf(NAME_KEY to name)
),
target = MockDataFactory.randomUuid(),
type = Block.Content.Link.Type.PAGE
fun `should update link appearance`() {
val title = MockBlockFactory.title()
val link = MockBlockFactory.link(
content = StubLinkContent(
iconSize = IconSize.NONE
)
)
@ -257,8 +239,8 @@ class DocumentExternalEventReducerTest {
// TESTING
val updated = Block.Fields(
map = mapOf(NAME_KEY to MockDataFactory.randomString())
val updated = link.content.asLink().copy(
iconSize = IconSize.MEDIUM
)
runBlocking {
@ -267,9 +249,7 @@ class DocumentExternalEventReducerTest {
page,
title,
link.copy(
content = link.content<Block.Content.Link>().copy(
fields = updated
)
content = updated
)
)
@ -279,7 +259,10 @@ class DocumentExternalEventReducerTest {
context = page.id,
id = link.id,
target = link.content<Block.Content.Link>().target,
fields = updated
iconSize = IconSize.MEDIUM,
cardStyle = null,
description = null,
relations = null
)
)

View file

@ -6,7 +6,6 @@ import com.anytypeio.anytype.presentation.MockBlockFactory
import com.anytypeio.anytype.presentation.editor.editor.actions.ActionItemType
import com.anytypeio.anytype.presentation.editor.editor.listener.ListenerType
import com.anytypeio.anytype.presentation.util.CoroutinesTestRule
import com.anytypeio.anytype.test_utils.MockDataFactory
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@ -33,17 +32,7 @@ class EditorBlockActionsTest : EditorPresentationTestSetup() {
@Test
fun `preview action should be in actions before style - when link block`() {
val link = Block(
id = MockDataFactory.randomUuid(),
fields = Block.Fields.empty(),
children = emptyList(),
content = Block.Content.Link(
target = MockDataFactory.randomUuid(),
type = Block.Content.Link.Type.PAGE,
fields = Block.Fields.empty()
),
backgroundColor = null
)
val link = MockBlockFactory.link()
val smart = Block(
id = root,

View file

@ -7,6 +7,7 @@ import com.anytypeio.anytype.core_models.Relations
import com.anytypeio.anytype.core_models.SmartBlockType
import com.anytypeio.anytype.core_models.ext.content
import com.anytypeio.anytype.core_utils.common.EventWrapper
import com.anytypeio.anytype.presentation.MockBlockFactory
import com.anytypeio.anytype.presentation.editor.editor.listener.ListenerType
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
import com.anytypeio.anytype.presentation.navigation.AppNavigation
@ -234,21 +235,9 @@ class EditorLockPageTest : EditorPresentationTestSetup() {
@Test
fun `should navigate to target when clicking on link-to-object when page is locked`() {
// SETUP
val target = MockDataFactory.randomUuid()
val link = Block(
id = MockDataFactory.randomUuid(),
fields = Block.Fields(emptyMap()),
content = Block.Content.Link(
target = target,
type = Block.Content.Link.Type.PAGE,
fields = Block.Fields.empty()
),
children = emptyList()
)
val link = MockBlockFactory.link()
val target = link.content.asLink().target
val page = listOf(
Block(

View file

@ -9,7 +9,9 @@ import com.anytypeio.anytype.domain.block.interactor.TurnIntoStyle
import com.anytypeio.anytype.domain.block.interactor.UnlinkBlocks
import com.anytypeio.anytype.domain.block.interactor.UpdateTextStyle
import com.anytypeio.anytype.domain.clipboard.Copy
import com.anytypeio.anytype.presentation.MockBlockContentFactory.StubLinkContent
import com.anytypeio.anytype.presentation.MockBlockFactory
import com.anytypeio.anytype.presentation.MockBlockFactory.link
import com.anytypeio.anytype.presentation.editor.EditorViewModel
import com.anytypeio.anytype.presentation.editor.EditorViewModel.Companion.DELAY_REFRESH_DOCUMENT_TO_ENTER_MULTI_SELECT_MODE
import com.anytypeio.anytype.presentation.editor.EditorViewModel.Companion.TEXT_CHANGES_DEBOUNCE_DURATION
@ -1308,28 +1310,18 @@ class EditorMultiSelectModeTest : EditorPresentationTestSetup() {
fun `should start background style toolbar with null color when all blocks are not texted`() {
val targetA = MockDataFactory.randomUuid()
val backgroundA = "red"
val a = Block(
id = MockDataFactory.randomUuid(),
fields = Block.Fields.empty(),
children = emptyList(),
content = Block.Content.Link(
val a = link(
content = StubLinkContent(
target = targetA,
type = Block.Content.Link.Type.PAGE,
fields = Block.Fields.empty()
),
backgroundColor = backgroundA
)
val targetB = MockDataFactory.randomUuid()
val backgroundB = "teal"
val b = Block(
id = MockDataFactory.randomUuid(),
fields = Block.Fields.empty(),
children = emptyList(),
content = Block.Content.Link(
val b = link(
content = StubLinkContent(
target = targetB,
type = Block.Content.Link.Type.PAGE,
fields = Block.Fields.empty()
),
backgroundColor = backgroundB
)
@ -1389,43 +1381,10 @@ class EditorMultiSelectModeTest : EditorPresentationTestSetup() {
@Test
fun `should start background style toolbar with red color when all blocks are not texted`() {
val targetA = MockDataFactory.randomUuid()
val backgroundA = "red"
val a = Block(
id = MockDataFactory.randomUuid(),
fields = Block.Fields.empty(),
children = emptyList(),
content = Block.Content.Link(
target = targetA,
type = Block.Content.Link.Type.PAGE,
fields = Block.Fields.empty()
),
backgroundColor = backgroundA
)
val targetB = MockDataFactory.randomUuid()
val b = Block(
id = MockDataFactory.randomUuid(),
fields = Block.Fields.empty(),
children = emptyList(),
content = Block.Content.Link(
target = targetB,
type = Block.Content.Link.Type.PAGE,
fields = Block.Fields.empty()
),
backgroundColor = backgroundA
)
val c = Block(
id = MockDataFactory.randomUuid(),
fields = Block.Fields.empty(),
children = emptyList(),
content = Block.Content.File(
state = Block.Content.File.State.EMPTY
),
backgroundColor = backgroundA
)
val a = link(backgroundColor = backgroundA)
val b = link(backgroundColor = backgroundA)
val c = link(backgroundColor = backgroundA)
val page = Block(
id = root,
fields = Block.Fields(emptyMap()),
@ -1470,44 +1429,10 @@ class EditorMultiSelectModeTest : EditorPresentationTestSetup() {
@Test
fun `should start background style toolbar with red color when blocks are mixed`() {
val targetA = MockDataFactory.randomUuid()
val backgroundA = "red"
val a = Block(
id = MockDataFactory.randomUuid(),
fields = Block.Fields.empty(),
children = emptyList(),
content = Block.Content.Link(
target = targetA,
type = Block.Content.Link.Type.PAGE,
fields = Block.Fields.empty()
),
backgroundColor = backgroundA
)
val targetB = MockDataFactory.randomUuid()
val b = Block(
id = MockDataFactory.randomUuid(),
fields = Block.Fields.empty(),
children = emptyList(),
content = Block.Content.Link(
target = targetB,
type = Block.Content.Link.Type.PAGE,
fields = Block.Fields.empty()
),
backgroundColor = backgroundA
)
val c = Block(
id = MockDataFactory.randomUuid(),
fields = Block.Fields.empty(),
children = emptyList(),
content = Block.Content.Link(
target = targetA,
type = Block.Content.Link.Type.PAGE,
fields = Block.Fields.empty()
),
backgroundColor = backgroundA
)
val a = link(backgroundColor = backgroundA)
val b = link(backgroundColor = backgroundA)
val c = link(backgroundColor = backgroundA)
val page = Block(
id = root,
@ -1553,42 +1478,9 @@ class EditorMultiSelectModeTest : EditorPresentationTestSetup() {
@Test
fun `should start background style toolbar with default color when all blocks has nullable backgrounds`() {
val targetA = MockDataFactory.randomUuid()
val a = Block(
id = MockDataFactory.randomUuid(),
fields = Block.Fields.empty(),
children = emptyList(),
content = Block.Content.Link(
target = targetA,
type = Block.Content.Link.Type.PAGE,
fields = Block.Fields.empty()
),
backgroundColor = null
)
val b = Block(
id = MockDataFactory.randomUuid(),
fields = Block.Fields.empty(),
children = emptyList(),
content = Block.Content.Text(
text = MockDataFactory.randomString(),
marks = emptyList(),
style = Block.Content.Text.Style.P
),
backgroundColor = null
)
val c = Block(
id = MockDataFactory.randomUuid(),
fields = Block.Fields.empty(),
children = emptyList(),
content = Block.Content.Link(
target = targetA,
type = Block.Content.Link.Type.PAGE,
fields = Block.Fields.empty()
),
backgroundColor = null
)
val a = link()
val b = link()
val c = link()
val page = Block(
id = root,

View file

@ -8,7 +8,6 @@ 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.restrictions.ObjectRestriction
import com.anytypeio.anytype.domain.`object`.ObjectTypesProvider
import com.anytypeio.anytype.domain.`object`.UpdateDetail

View file

@ -5,6 +5,7 @@ import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.Position
import com.anytypeio.anytype.core_models.ext.content
import com.anytypeio.anytype.domain.block.interactor.Move
import com.anytypeio.anytype.presentation.MockBlockFactory.link
import com.anytypeio.anytype.presentation.editor.EditorViewModel
import com.anytypeio.anytype.presentation.editor.editor.actions.ActionItemType
import com.anytypeio.anytype.presentation.editor.editor.control.ControlPanelState
@ -525,16 +526,7 @@ class EditorScrollAndMoveTest : EditorPresentationTestSetup() {
)
)
val b = Block(
id = MockDataFactory.randomUuid(),
fields = Block.Fields.empty(),
children = emptyList(),
content = Block.Content.Link(
target = MockDataFactory.randomUuid(),
fields = Block.Fields.empty(),
type = Block.Content.Link.Type.PAGE
)
)
val b = link()
val page = Block(
id = root,

View file

@ -1,12 +1,10 @@
package com.anytypeio.anytype.presentation.mapper
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.ObjectType
import com.anytypeio.anytype.domain.config.Gateway
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.presentation.editor.editor.Markup
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
import com.anytypeio.anytype.presentation.objects.appearance.getLinkToObjectAppearanceParams
import com.anytypeio.anytype.test_utils.MockDataFactory
import org.junit.Before
import org.junit.Test
@ -82,7 +80,8 @@ class MapperExtensionKtTest {
type = type
)
val expected = BlockView.MediaPlaceholder.File(id = id, indent = indent, isPreviousBlockMedia = false)
val expected =
BlockView.MediaPlaceholder.File(id = id, indent = indent, isPreviousBlockMedia = false)
val actual = block.toFileView(id, urlBuilder, indent, mode, false, null, false)
assertEquals(expected, actual)
@ -190,7 +189,11 @@ class MapperExtensionKtTest {
)
val expected = BlockView.MediaPlaceholder.Picture(id = id, indent = indent, isPreviousBlockMedia = false)
val expected = BlockView.MediaPlaceholder.Picture(
id = id,
indent = indent,
isPreviousBlockMedia = false
)
val actual = block.toPictureView(id, urlBuilder, indent, mode, false, null, false)
assertEquals(expected, actual)
@ -752,163 +755,4 @@ class MapperExtensionKtTest {
)
)
}
@Test
fun `should create appearance params with default values`() {
val emptyFields = Block.Fields(mapOf())
val layout = ObjectType.Layout.BASIC
val result = emptyFields.getLinkToObjectAppearanceParams(layout)
val expected = BlockView.Appearance.Params(
style = BlockView.Appearance.LINK_STYLE_TEXT,
iconSize = BlockView.Appearance.LINK_ICON_SIZE_MEDIUM,
withIcon = true,
withName = true,
withDescription = null,
canHaveCover = false,
canHaveDescription = true,
canHaveIcon = true
)
assertEquals(expected, result)
}
@Test
fun `should create appearance params with default values when layout is null`() {
val emptyFields = Block.Fields(mapOf())
val layout = null
val result = emptyFields.getLinkToObjectAppearanceParams(layout)
val expected = BlockView.Appearance.Params(
style = BlockView.Appearance.LINK_STYLE_TEXT,
iconSize = BlockView.Appearance.LINK_ICON_SIZE_MEDIUM,
withIcon = true,
withName = true,
withDescription = null,
canHaveCover = false,
canHaveDescription = true,
canHaveIcon = true
)
assertEquals(expected, result)
}
@Test
fun `should create appearance params with random values`() {
val emptyFields = Block.Fields(
mapOf(
"withName" to false,
"withIcon" to null,
"withDescription" to true,
"withCover" to true,
"style" to BlockView.Appearance.LINK_STYLE_CARD,
"iconSize" to BlockView.Appearance.LINK_ICON_SIZE_MEDIUM
)
)
val layout = ObjectType.Layout.BASIC
val result = emptyFields.getLinkToObjectAppearanceParams(layout)
val expected = BlockView.Appearance.Params(
style = BlockView.Appearance.LINK_STYLE_CARD,
iconSize = BlockView.Appearance.LINK_ICON_SIZE_MEDIUM,
withIcon = true,
withName = false,
withDescription = true,
withCover = true,
canHaveCover = false,
canHaveDescription = true,
canHaveIcon = true
)
assertEquals(expected, result)
}
@Test
fun `should create appearance params without cover`() {
val emptyFields = Block.Fields(
mapOf(
"withName" to false,
"withIcon" to null,
"withDescription" to true,
"withCover" to true,
"style" to BlockView.Appearance.LINK_STYLE_TEXT,
"iconSize" to BlockView.Appearance.LINK_ICON_SIZE_MEDIUM
)
)
val layout = ObjectType.Layout.BASIC
val result = emptyFields.getLinkToObjectAppearanceParams(layout)
val expected = BlockView.Appearance.Params(
style = BlockView.Appearance.LINK_STYLE_TEXT,
iconSize = BlockView.Appearance.LINK_ICON_SIZE_MEDIUM,
withIcon = true,
withName = false,
withDescription = true,
withCover = true,
canHaveCover = false,
canHaveDescription = false,
canHaveIcon = true
)
assertEquals(expected, result)
}
@Test
fun `should create appearance params when layout is todo`() {
val emptyFields = Block.Fields(
mapOf(
"withName" to false,
"withIcon" to false,
"withDescription" to true,
"withCover" to true,
"style" to BlockView.Appearance.LINK_STYLE_TEXT,
"iconSize" to BlockView.Appearance.LINK_ICON_SIZE_MEDIUM
)
)
val layout = ObjectType.Layout.TODO
val result = emptyFields.getLinkToObjectAppearanceParams(layout)
val expected = BlockView.Appearance.Params(
style = BlockView.Appearance.LINK_STYLE_TEXT,
iconSize = BlockView.Appearance.LINK_ICON_SIZE_MEDIUM,
withIcon = true,
withName = false,
withDescription = true,
withCover = true,
canHaveCover = false,
canHaveDescription = false,
canHaveIcon = false
)
assertEquals(expected, result)
}
@Test
fun `should create appearance params when layout is note`() {
val emptyFields = Block.Fields(
mapOf(
"withIcon" to true,
"withDescription" to true,
"withCover" to true,
"style" to BlockView.Appearance.LINK_STYLE_TEXT,
"iconSize" to BlockView.Appearance.LINK_ICON_SIZE_LARGE
)
)
val layout = ObjectType.Layout.NOTE
val result = emptyFields.getLinkToObjectAppearanceParams(layout)
val expected = BlockView.Appearance.Params(
style = BlockView.Appearance.LINK_STYLE_TEXT,
iconSize = BlockView.Appearance.LINK_ICON_SIZE_MEDIUM,
withIcon = false,
withName = true,
withDescription = false,
withCover = false,
canHaveCover = false,
canHaveDescription = false,
canHaveIcon = false
)
assertEquals(expected, result)
}
}

View file

@ -0,0 +1,187 @@
package com.anytypeio.anytype.presentation.objects.appearance
import com.anytypeio.anytype.core_models.Block.Content.Link
import com.anytypeio.anytype.core_models.ObjectType
import com.anytypeio.anytype.presentation.MockBlockContentFactory.StubLinkContent
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
import org.junit.Test
import kotlin.test.assertEquals
class LinkAppearanceInEditorTest {
private val defaultLinkAppearance = StubLinkContent(
iconSize = Link.IconSize.NONE,
cardStyle = Link.CardStyle.TEXT,
description = Link.Description.NONE,
relations = emptySet()
)
@Test
fun `should create appearance params with default values`() {
val factory = LinkAppearanceFactory(
content = defaultLinkAppearance,
layout = ObjectType.Layout.BASIC
)
val actual = factory.createInEditorLinkAppearance()
val expected = BlockView.Appearance.InEditor(
isCard = false,
showIcon = false,
showName = true,
showDescription = false,
showCover = false,
)
assertEquals(expected, actual)
}
@Test
fun `when layout is null - create appearance params with default values`() {
val factory = LinkAppearanceFactory(
content = defaultLinkAppearance,
layout = null
)
val actual = factory.createInEditorLinkAppearance()
val expected = BlockView.Appearance.InEditor(
isCard = false,
showIcon = false,
showName = true,
showDescription = false,
showCover = false,
)
assertEquals(expected, actual)
}
@Test
fun `should create appearance params without cover`() {
val factory = LinkAppearanceFactory(
content = defaultLinkAppearance.copy(
relations = defaultLinkAppearance.relations + Link.Relation.COVER
),
layout = ObjectType.Layout.BASIC
)
val actual = factory.createInEditorLinkAppearance()
val expected = BlockView.Appearance.InEditor(
isCard = false,
showIcon = false,
showName = true,
showDescription = false,
showCover = false,
)
assertEquals(expected, actual)
}
@Test
fun `when layout is todo`() {
val factory = LinkAppearanceFactory(
content = defaultLinkAppearance,
layout = ObjectType.Layout.TODO
)
val actual = factory.createInEditorLinkAppearance()
val expected = BlockView.Appearance.InEditor(
isCard = false,
showIcon = true,
showName = true,
showDescription = false,
showCover = false,
)
assertEquals(expected, actual)
}
@Test
fun `when layout is note`() {
val factory = LinkAppearanceFactory(
content = defaultLinkAppearance.copy(
iconSize = Link.IconSize.SMALL
),
layout = ObjectType.Layout.NOTE
)
val actual = factory.createInEditorLinkAppearance()
val expected = BlockView.Appearance.InEditor(
isCard = false,
showIcon = false,
showName = true,
showDescription = false,
showCover = false,
)
assertEquals(expected, actual)
}
@Test
fun `when iconSize is none - no icon`() {
val factory = LinkAppearanceFactory(
content = defaultLinkAppearance.copy(
iconSize = Link.IconSize.NONE
),
layout = ObjectType.Layout.BASIC
)
val actual = factory.createInEditorLinkAppearance()
val expected = BlockView.Appearance.InEditor(
isCard = false,
showIcon = false,
showName = true,
showDescription = false,
showCover = false,
)
assertEquals(expected, actual)
}
@Test
fun `when card style text - no description`() {
val factory = LinkAppearanceFactory(
content = defaultLinkAppearance.copy(
cardStyle = Link.CardStyle.TEXT,
description = Link.Description.ADDED
),
layout = ObjectType.Layout.BASIC
)
val actual = factory.createInEditorLinkAppearance()
val expected = BlockView.Appearance.InEditor(
isCard = false,
showIcon = false,
showName = true,
showDescription = false,
showCover = false,
)
assertEquals(expected, actual)
}
@Test
fun `when card style card - there is description`() {
val factory = LinkAppearanceFactory(
content = defaultLinkAppearance.copy(
cardStyle = Link.CardStyle.CARD,
description = Link.Description.ADDED
),
layout = ObjectType.Layout.BASIC
)
val actual = factory.createInEditorLinkAppearance()
val expected = BlockView.Appearance.InEditor(
isCard = true,
showIcon = false,
showName = true,
showDescription = true,
showCover = false,
)
assertEquals(expected, actual)
}
}

View file

@ -0,0 +1,93 @@
package com.anytypeio.anytype.presentation.objects.appearance
import com.anytypeio.anytype.core_models.Block.Content.Link
import com.anytypeio.anytype.core_models.ObjectType
import com.anytypeio.anytype.presentation.MockBlockContentFactory.StubLinkContent
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView.Appearance.MenuItem
import org.junit.Test
import kotlin.test.assertEquals
class LinkAppearanceMenuTest {
private val defaultLinkAppearance = StubLinkContent(
iconSize = Link.IconSize.SMALL,
cardStyle = Link.CardStyle.TEXT,
description = Link.Description.NONE,
relations = setOf(Link.Relation.NAME)
)
@Test
fun `default`() {
val factory = LinkAppearanceFactory(
content = defaultLinkAppearance,
layout = ObjectType.Layout.BASIC
)
val actual = factory.createAppearanceMenuItems()
val expected = BlockView.Appearance.Menu(
preview = MenuItem.PreviewLayout.TEXT,
icon = MenuItem.Icon.SMALL,
cover = null,
description = null
)
assertEquals(expected, actual)
}
@Test
fun `when is todo layout - no icon`() {
val factory = LinkAppearanceFactory(
content = defaultLinkAppearance,
layout = ObjectType.Layout.TODO
)
val actual = factory.createAppearanceMenuItems()
val expected = BlockView.Appearance.Menu(
preview = MenuItem.PreviewLayout.TEXT,
icon = null,
cover = null,
description = null
)
assertEquals(expected, actual)
}
@Test
fun `when is todo note - no icon and no description`() {
val factory = LinkAppearanceFactory(
content = defaultLinkAppearance.copy(
description = Link.Description.ADDED
),
layout = ObjectType.Layout.NOTE
)
val actual = factory.createAppearanceMenuItems()
val expected = BlockView.Appearance.Menu(
preview = MenuItem.PreviewLayout.TEXT,
icon = null,
cover = null,
description = null
)
assertEquals(expected, actual)
}
@Test
fun `when card style is card - has description`() {
val factory = LinkAppearanceFactory(
content = defaultLinkAppearance.copy(
description = Link.Description.ADDED,
cardStyle = Link.CardStyle.CARD,
),
layout = ObjectType.Layout.BASIC
)
val actual = factory.createAppearanceMenuItems()
val expected = BlockView.Appearance.Menu(
preview = MenuItem.PreviewLayout.CARD,
icon = MenuItem.Icon.SMALL,
cover = null,
description = MenuItem.Description.WITH
)
assertEquals(expected, actual)
}
}