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

DROD-2135 Files as object (#819)

This commit is contained in:
Konstantin Ivanov 2024-02-04 12:12:19 +01:00 committed by GitHub
parent d4d1b3ea3e
commit a19d1bd8f7
Signed by: github
GPG key ID: B5690EEEBB952194
86 changed files with 1256 additions and 1073 deletions

View file

@ -134,6 +134,9 @@ EditDataViewViewerFragment : BaseBottomSheetFragment<FragmentEditDataViewViewerB
EditDataViewViewerViewModel.ViewState.Calendar -> {
toast("TODO")
}
EditDataViewViewerViewModel.ViewState.Graph -> {
toast("TODO")
}
}
}

View file

@ -218,7 +218,7 @@ data class Block(
/**
* File block.
* @property hash file hash
* @property targetObjectId id of the target object
* @property name filename
* @property mime mime type
* @property size file size (in bytes)
@ -226,7 +226,7 @@ data class Block(
* @property state file state
*/
data class File(
val hash: String? = null,
val targetObjectId: Id? = null,
val name: String? = null,
val mime: String? = null,
val size: Long? = null,
@ -297,7 +297,8 @@ data class Block(
LIST("List"),
GALLERY("Gallery"),
BOARD("Board"),
CALENDAR("Calendar")
CALENDAR("Calendar"),
GRAPH("Graph")
}
enum class Size { SMALL, MEDIUM, LARGE }

View file

@ -27,7 +27,7 @@ sealed class Command {
class DownloadFile(
val path: String,
val hash: Hash
val objectId: Id
)
/**

View file

@ -177,11 +177,11 @@ sealed class Event {
*/
data class UpdateFileBlock(
override val context: String,
val id: Id,
val blockId: Id,
val targetObjectId: Id? = null,
val state: Block.Content.File.State? = null,
val type: Block.Content.File.Type? = null,
val name: String? = null,
val hash: String? = null,
val mime: String? = null,
val size: Long? = null
) : Command()

View file

@ -296,4 +296,17 @@ sealed class ObjectWrapper {
else -> emptyList()
}
}
data class File(override val map: Struct) : ObjectWrapper() {
private val default = map.withDefault { null }
val id: Id by default
val name: String? by default
val description: String? by default
val fileExt: String? by default
val fileMimeType: String? by default
val sizeInBytes: Double? by default
val url: String? by default
val isArchived: Boolean? by default
val isDeleted: Boolean? by default
}
}

View file

@ -31,7 +31,7 @@ sealed class Response {
sealed class Media : Response() {
class Upload(
val hash: String
val objectId: Id
)
}

View file

@ -15,8 +15,6 @@ import androidx.recyclerview.widget.RecyclerView
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_ui.BuildConfig
import com.anytypeio.anytype.core_ui.databinding.ItemBlockBookmarkBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockBookmarkErrorBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockBookmarkUploadingBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockBulletedBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockCalloutBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockCheckboxBinding
@ -29,13 +27,12 @@ import com.anytypeio.anytype.core_ui.databinding.ItemBlockDividerDotsBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockDividerLineBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockFeaturedRelationsBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockFileBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockFileErrorBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockFileUploadingBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockHeaderOneBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockHeaderThreeBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockHeaderTwoBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockHighlightBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockLatexBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockMediaErrorBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockMediaPlaceholderBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockNumberedBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockObjectLinkArchiveBinding
@ -47,8 +44,6 @@ import com.anytypeio.anytype.core_ui.databinding.ItemBlockObjectLinkCardSmallIco
import com.anytypeio.anytype.core_ui.databinding.ItemBlockObjectLinkDeleteBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockObjectLinkLoadingBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockPictureBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockPictureErrorBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockPictureUploadingBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockRelationCheckboxBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockRelationDefaultBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockRelationDeletedBinding
@ -66,8 +61,6 @@ import com.anytypeio.anytype.core_ui.databinding.ItemBlockTocBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockToggleBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockUnsupportedBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockVideoBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockVideoErrorBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockVideoUploadingBinding
import com.anytypeio.anytype.core_ui.features.editor.BlockViewDiffUtil.Payload
import com.anytypeio.anytype.core_ui.features.editor.decoration.DecoratableViewHolder
import com.anytypeio.anytype.core_ui.features.editor.holders.dataview.DataViewBlockDefaultHolder
@ -528,12 +521,12 @@ class BlockAdapter(
}
HOLDER_FILE_UPLOAD -> {
FileUpload(
ItemBlockFileUploadingBinding.inflate(inflater, parent, false)
ItemBlockMediaPlaceholderBinding.inflate(inflater, parent, false)
)
}
HOLDER_FILE_ERROR -> {
FileError(
ItemBlockFileErrorBinding.inflate(inflater, parent, false)
ItemBlockMediaErrorBinding.inflate(inflater, parent, false)
)
}
HOLDER_VIDEO -> {
@ -548,12 +541,12 @@ class BlockAdapter(
}
HOLDER_VIDEO_UPLOAD -> {
VideoUpload(
ItemBlockVideoUploadingBinding.inflate(inflater, parent, false)
ItemBlockMediaPlaceholderBinding.inflate(inflater, parent, false)
)
}
HOLDER_VIDEO_ERROR -> {
VideoError(
ItemBlockVideoErrorBinding.inflate(inflater, parent, false)
ItemBlockMediaErrorBinding.inflate(inflater, parent, false)
)
}
HOLDER_OBJECT_LINK_DEFAULT -> {
@ -681,12 +674,12 @@ class BlockAdapter(
}
HOLDER_BOOKMARK_ERROR -> {
BookmarkError(
ItemBlockBookmarkErrorBinding.inflate(inflater, parent, false)
ItemBlockMediaErrorBinding.inflate(inflater, parent, false)
)
}
HOLDER_BOOKMARK_UPLOAD -> {
BookmarkUpload(
ItemBlockBookmarkUploadingBinding.inflate(inflater, parent, false)
ItemBlockMediaPlaceholderBinding.inflate(inflater, parent, false)
)
}
HOLDER_PICTURE -> {
@ -701,12 +694,12 @@ class BlockAdapter(
}
HOLDER_PICTURE_UPLOAD -> {
PictureUpload(
ItemBlockPictureUploadingBinding.inflate(inflater, parent, false)
ItemBlockMediaPlaceholderBinding.inflate(inflater, parent, false)
)
}
HOLDER_PICTURE_ERROR -> {
PictureError(
ItemBlockPictureErrorBinding.inflate(inflater, parent, false)
ItemBlockMediaErrorBinding.inflate(inflater, parent, false)
)
}
HOLDER_DIVIDER_LINE -> {

View file

@ -36,6 +36,19 @@ interface DecoratableCardViewHolder : DecoratableViewHolder {
}
}
interface DecoratableMediaErrorViewHolder : DecoratableViewHolder {
val decoratableCard: View
override fun applyDecorations(decorations: List<BlockView.Decoration>) {
decoratableContainer.decorate(decorations) { rect ->
decoratableCard.applyMediaErrorDecorations<FrameLayout.LayoutParams>(
rect = rect,
res = decoratableCard.resources
)
}
}
}
/**
* Applying decorations for card blocks (media blocks, placeholders, link-to-objects, bookmarks, etc.)
*/
@ -62,4 +75,18 @@ inline fun <reified LP : ViewGroup.MarginLayoutParams> View.applySelectorOffset(
marginEnd = content.marginEnd - selectorLeftRightOffset
topMargin = content.marginTop
bottomMargin = content.marginBottom
}
/**
* Applying decorations for media error blocks
*/
inline fun <reified LP : ViewGroup.MarginLayoutParams> View.applyMediaErrorDecorations(
rect: Rect,
res: Resources
) = updateLayoutParams<LP> {
val defaultIndentOffset = res.getDimension(R.dimen.default_indent).toInt()
marginStart = defaultIndentOffset + rect.left
marginEnd = defaultIndentOffset + rect.right
topMargin = res.getDimension(R.dimen.card_block_extra_space_top).toInt()
bottomMargin = res.getDimension(R.dimen.dp_8).toInt() + rect.bottom
}

View file

@ -1,35 +1,14 @@
package com.anytypeio.anytype.core_ui.features.editor.holders.error
import android.view.View
import android.widget.FrameLayout
import android.widget.TextView
import androidx.core.view.updateLayoutParams
import com.anytypeio.anytype.core_ui.BuildConfig
import com.anytypeio.anytype.core_ui.R
import com.anytypeio.anytype.core_ui.databinding.ItemBlockBookmarkErrorBinding
import com.anytypeio.anytype.core_ui.features.editor.decoration.DecoratableCardViewHolder
import com.anytypeio.anytype.core_ui.features.editor.decoration.EditorDecorationContainer
import com.anytypeio.anytype.core_ui.features.editor.decoration.applySelectorOffset
import com.anytypeio.anytype.core_utils.ext.dimen
import com.anytypeio.anytype.core_utils.ext.indentize
import com.anytypeio.anytype.core_ui.databinding.ItemBlockMediaErrorBinding
import com.anytypeio.anytype.core_ui.extensions.drawable
import com.anytypeio.anytype.presentation.editor.editor.listener.ListenerType
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
class BookmarkError(
val binding: ItemBlockBookmarkErrorBinding
) : MediaError(binding.root), DecoratableCardViewHolder {
override val root: View = binding.bookmarkErrorRoot
private val urlView: TextView = binding.errorBookmarkUrl
override val decoratableContainer: EditorDecorationContainer
get() = binding.decorationContainer
override val decoratableCard: View
get() = binding.card
fun setUrl(url: String) {
urlView.text = url.ifEmpty { null }
}
binding: ItemBlockMediaErrorBinding
) : MediaError(binding) {
override fun errorClick(item: BlockView.Error, clicked: (ListenerType) -> Unit) {
if (item is BlockView.Error.Bookmark) {
@ -37,20 +16,11 @@ class BookmarkError(
}
}
@Deprecated("Pre-nested-styling legacy.")
override fun indentize(item: BlockView.Indentable) {
// Do nothing.
}
override fun select(isSelected: Boolean) {
binding.selected.isSelected = isSelected
}
override fun applyDecorations(decorations: List<BlockView.Decoration>) {
super.applyDecorations(decorations)
binding.selected.applySelectorOffset<FrameLayout.LayoutParams>(
content = binding.card,
res = itemView.resources
)
override fun bind(item: BlockView.Error, clicked: (ListenerType) -> Unit) {
super.bind(item, clicked)
binding.fileIcon.setImageDrawable(itemView.context.drawable(R.drawable.ic_bookmark_inactive))
if (item is BlockView.Error.Bookmark) {
binding.fileName.text = item.url.ifEmpty { null }
}
}
}

View file

@ -1,48 +1,30 @@
package com.anytypeio.anytype.core_ui.features.editor.holders.error
import android.view.View
import android.widget.FrameLayout
import androidx.core.view.updateLayoutParams
import com.anytypeio.anytype.core_ui.BuildConfig
import com.anytypeio.anytype.core_ui.R
import com.anytypeio.anytype.core_ui.databinding.ItemBlockFileErrorBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockMediaErrorBinding
import com.anytypeio.anytype.core_ui.extensions.drawable
import com.anytypeio.anytype.core_ui.features.editor.decoration.DecoratableCardViewHolder
import com.anytypeio.anytype.core_ui.features.editor.decoration.EditorDecorationContainer
import com.anytypeio.anytype.core_ui.features.editor.decoration.applySelectorOffset
import com.anytypeio.anytype.core_utils.ext.dimen
import com.anytypeio.anytype.core_utils.ext.indentize
import com.anytypeio.anytype.presentation.editor.editor.listener.ListenerType
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
class FileError(
val binding: ItemBlockFileErrorBinding
) : MediaError(binding.root), DecoratableCardViewHolder {
override val root: View = itemView
override val decoratableContainer: EditorDecorationContainer
get() = binding.decorationContainer
override val decoratableCard: View
get() = binding.card
binding: ItemBlockMediaErrorBinding
) : MediaError(binding) {
override fun errorClick(item: BlockView.Error, clicked: (ListenerType) -> Unit) {
clicked(ListenerType.File.Error(item.id))
if (item is BlockView.Error.File) {
clicked(ListenerType.File.Error(item.id))
}
}
@Deprecated("Pre-nested-styling legacy.")
override fun indentize(item: BlockView.Indentable) {
// Do nothing.
}
override fun select(isSelected: Boolean) {
binding.selected.isSelected = isSelected
}
override fun applyDecorations(decorations: List<BlockView.Decoration>) {
super.applyDecorations(decorations)
binding.selected.applySelectorOffset<FrameLayout.LayoutParams>(
content = binding.card,
res = itemView.resources
)
override fun bind(item: BlockView.Error, clicked: (ListenerType) -> Unit) {
super.bind(item, clicked)
if (item is BlockView.Error.File) {
binding.fileName.text = if (item.name.isNullOrBlank()) {
itemView.resources.getString(R.string.hint_upload_file)
} else
item.name
}
binding.fileIcon.setImageDrawable(itemView.context.drawable(R.drawable.ic_file_light_inactive))
}
}

View file

@ -1,24 +1,31 @@
package com.anytypeio.anytype.core_ui.features.editor.holders.error
import android.view.View
import android.widget.FrameLayout
import androidx.core.view.marginEnd
import androidx.core.view.marginStart
import androidx.core.view.updateLayoutParams
import com.anytypeio.anytype.core_ui.R
import com.anytypeio.anytype.core_ui.databinding.ItemBlockMediaErrorBinding
import com.anytypeio.anytype.core_ui.features.editor.BlockViewDiffUtil
import com.anytypeio.anytype.core_ui.features.editor.BlockViewHolder
import com.anytypeio.anytype.core_ui.features.editor.EditorTouchProcessor
import com.anytypeio.anytype.core_ui.features.editor.SupportCustomTouchProcessor
import com.anytypeio.anytype.core_ui.features.editor.decoration.DecoratableMediaErrorViewHolder
import com.anytypeio.anytype.core_ui.features.editor.decoration.EditorDecorationContainer
import com.anytypeio.anytype.presentation.editor.editor.listener.ListenerType
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
abstract class MediaError(
view: View
) : BlockViewHolder(view),
val binding: ItemBlockMediaErrorBinding
) : BlockViewHolder(binding.root),
BlockViewHolder.IndentableHolder,
BlockViewHolder.DragAndDropHolder,
SupportCustomTouchProcessor {
SupportCustomTouchProcessor, DecoratableMediaErrorViewHolder {
abstract val root: View
override val decoratableContainer: EditorDecorationContainer get() = binding.decorationContainer
override val decoratableCard: View get() = binding.card
val errorIcon: View get() = binding.errorMessage
abstract fun errorClick(item: BlockView.Error, clicked: (ListenerType) -> Unit)
abstract override fun indentize(item: BlockView.Indentable)
abstract fun select(isSelected: Boolean)
override val editorTouchProcessor = EditorTouchProcessor(
fallback = { e -> itemView.onTouchEvent(e) }
@ -28,23 +35,45 @@ abstract class MediaError(
itemView.setOnTouchListener { v, e -> editorTouchProcessor.process(v, e) }
}
fun bind(
open fun bind(
item: BlockView.Error,
clicked: (ListenerType) -> Unit
) {
indentize(item)
select(item.isSelected)
with(itemView) {
setOnClickListener { errorClick(item, clicked) }
}
}
fun processChangePayload(payloads: List<BlockViewDiffUtil.Payload>, item: BlockView) {
check(item is BlockView.Error) { "Expected error block, but was: $item" }
open fun processChangePayload(payloads: List<BlockViewDiffUtil.Payload>, item: BlockView) {
check(item is BlockView.Error) { "Expected error media block, but was: $item" }
payloads.forEach { payload ->
if (payload.isSelectionChanged) {
itemView.isSelected = item.isSelected
select(item.isSelected)
}
}
}
fun select(isSelected: Boolean) {
binding.selected.isSelected = isSelected
}
override fun applyDecorations(decorations: List<BlockView.Decoration>) {
super.applyDecorations(decorations)
binding.selected.updateLayoutParams<FrameLayout.LayoutParams> {
val selectorLeftRightOffset = itemView.resources.getDimension(R.dimen.selection_left_right_offset).toInt()
marginStart = binding.card.marginStart - selectorLeftRightOffset
marginEnd = binding.card.marginEnd - selectorLeftRightOffset
topMargin = itemView.resources.getDimension(R.dimen.card_block_extra_space_top).toInt()
bottomMargin = 0
}
errorIcon.updateLayoutParams<FrameLayout.LayoutParams> {
marginStart = binding.card.marginStart
}
}
@Deprecated("Pre-nested-styling legacy.")
override fun indentize(item: BlockView.Indentable) {
// Do nothing.
}
}

View file

@ -1,48 +1,30 @@
package com.anytypeio.anytype.core_ui.features.editor.holders.error
import android.view.View
import android.widget.FrameLayout
import androidx.core.view.updateLayoutParams
import com.anytypeio.anytype.core_ui.BuildConfig
import com.anytypeio.anytype.core_ui.R
import com.anytypeio.anytype.core_ui.databinding.ItemBlockPictureErrorBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockMediaErrorBinding
import com.anytypeio.anytype.core_ui.extensions.drawable
import com.anytypeio.anytype.core_ui.features.editor.decoration.DecoratableCardViewHolder
import com.anytypeio.anytype.core_ui.features.editor.decoration.EditorDecorationContainer
import com.anytypeio.anytype.core_ui.features.editor.decoration.applySelectorOffset
import com.anytypeio.anytype.core_utils.ext.dimen
import com.anytypeio.anytype.core_utils.ext.indentize
import com.anytypeio.anytype.presentation.editor.editor.listener.ListenerType
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
class PictureError(
val binding: ItemBlockPictureErrorBinding
) : MediaError(binding.root), DecoratableCardViewHolder {
override val root: View = itemView
override val decoratableContainer: EditorDecorationContainer
get() = binding.decorationContainer
override val decoratableCard: View
get() = binding.card
binding: ItemBlockMediaErrorBinding
) : MediaError(binding) {
override fun errorClick(item: BlockView.Error, clicked: (ListenerType) -> Unit) {
clicked(ListenerType.Picture.Error(item.id))
if (item is BlockView.Error.Picture) {
clicked(ListenerType.Picture.Error(item.id))
}
}
@Deprecated("Pre-nested-styling legacy.")
override fun indentize(item: BlockView.Indentable) {
// Do nothing.
}
override fun select(isSelected: Boolean) {
binding.selected.isSelected = isSelected
}
override fun applyDecorations(decorations: List<BlockView.Decoration>) {
super.applyDecorations(decorations)
binding.selected.applySelectorOffset<FrameLayout.LayoutParams>(
content = binding.card,
res = itemView.resources
)
override fun bind(item: BlockView.Error, clicked: (ListenerType) -> Unit) {
super.bind(item, clicked)
if (item is BlockView.Error.Picture) {
binding.fileName.text = if (item.name.isNullOrBlank()) {
itemView.resources.getString(R.string.hint_upload_image)
} else
item.name
}
binding.fileIcon.setImageDrawable(itemView.context.drawable(R.drawable.ic_picture_inactive))
}
}

View file

@ -1,48 +1,30 @@
package com.anytypeio.anytype.core_ui.features.editor.holders.error
import android.view.View
import android.widget.FrameLayout
import androidx.core.view.updateLayoutParams
import com.anytypeio.anytype.core_ui.BuildConfig
import com.anytypeio.anytype.core_ui.R
import com.anytypeio.anytype.core_ui.databinding.ItemBlockVideoErrorBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockMediaErrorBinding
import com.anytypeio.anytype.core_ui.extensions.drawable
import com.anytypeio.anytype.core_ui.features.editor.decoration.DecoratableCardViewHolder
import com.anytypeio.anytype.core_ui.features.editor.decoration.EditorDecorationContainer
import com.anytypeio.anytype.core_ui.features.editor.decoration.applySelectorOffset
import com.anytypeio.anytype.core_utils.ext.dimen
import com.anytypeio.anytype.core_utils.ext.indentize
import com.anytypeio.anytype.presentation.editor.editor.listener.ListenerType
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
class VideoError(
val binding: ItemBlockVideoErrorBinding
) : MediaError(binding.root), DecoratableCardViewHolder {
override val root: View = itemView
override val decoratableContainer: EditorDecorationContainer
get() = binding.decorationContainer
override val decoratableCard: View
get() = binding.card
binding: ItemBlockMediaErrorBinding
) : MediaError(binding) {
override fun errorClick(item: BlockView.Error, clicked: (ListenerType) -> Unit) {
clicked(ListenerType.Video.Error(item.id))
if (item is BlockView.Error.Video) {
clicked(ListenerType.Video.Error(item.id))
}
}
@Deprecated("Pre-nested-styling legacy.")
override fun indentize(item: BlockView.Indentable) {
// Do nothing.
}
override fun select(isSelected: Boolean) {
binding.selected.isSelected = isSelected
}
override fun applyDecorations(decorations: List<BlockView.Decoration>) {
super.applyDecorations(decorations)
binding.selected.applySelectorOffset<FrameLayout.LayoutParams>(
content = binding.card,
res = itemView.resources
)
override fun bind(item: BlockView.Error, clicked: (ListenerType) -> Unit) {
super.bind(item, clicked)
if (item is BlockView.Error.Video) {
binding.fileName.text = if (item.name.isNullOrBlank()) {
itemView.resources.getString(R.string.hint_upload_video)
} else
item.name
}
binding.fileIcon.setImageDrawable(itemView.context.drawable(R.drawable.ic_video_inactive))
}
}

View file

@ -45,9 +45,22 @@ class File(val binding: ItemBlockFileBinding) : Media(binding.root), Decoratable
name.enableReadMode()
if (item.size != null && item.name != null) {
val size = item.size!!.readableFileSize()
val spannable = SpannableString("${item.name} $size")
val start = item.name!!.length + 2
val end = item.name!!.length + 2 + size.length
val spannable = if (item.fileExt.isNullOrBlank()) {
SpannableString("${item.name} $size")
} else {
SpannableString("${item.name}.${item.fileExt} $size")
}
val start = if (item.fileExt.isNullOrBlank()) {
item.name!!.length + 2
} else {
item.name!!.length + item.fileExt!!.length + 2
}
val end = if (item.fileExt.isNullOrBlank()) {
item.name!!.length + 2 + size.length
} else {
item.name!!.length + item.fileExt!!.length + 3 + size.length
}
spannable.setSpan(
RelativeSizeSpan(0.87f),
start,

View file

@ -2,9 +2,8 @@ package com.anytypeio.anytype.core_ui.features.editor.holders.placeholders
import com.anytypeio.anytype.core_ui.R
import com.anytypeio.anytype.core_ui.databinding.ItemBlockMediaPlaceholderBinding
import com.anytypeio.anytype.core_ui.features.editor.BlockViewDiffUtil
import com.anytypeio.anytype.core_ui.extensions.drawable
import com.anytypeio.anytype.presentation.editor.editor.listener.ListenerType
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
class BookmarkPlaceholder(binding: ItemBlockMediaPlaceholderBinding) : MediaPlaceholder(binding) {
@ -14,15 +13,6 @@ class BookmarkPlaceholder(binding: ItemBlockMediaPlaceholderBinding) : MediaPlac
override fun setup() {
title.text = itemView.resources.getString(R.string.hint_add_a_web_bookmark)
title.setCompoundDrawablesRelativeWithIntrinsicBounds(R.drawable.ic_bookmark_placeholder, 0, 0, 0)
}
override fun processChangePayload(payloads: List<BlockViewDiffUtil.Payload>, item: BlockView) {
super.processChangePayload(payloads, item)
// TODO process loading state changes
}
fun isLoading(boolean: Boolean) {
// TODO process loading state changes
binding.fileIcon.setImageDrawable(itemView.context.drawable(R.drawable.ic_bookmark_placeholder))
}
}

View file

@ -2,6 +2,7 @@ package com.anytypeio.anytype.core_ui.features.editor.holders.placeholders
import com.anytypeio.anytype.core_ui.R
import com.anytypeio.anytype.core_ui.databinding.ItemBlockMediaPlaceholderBinding
import com.anytypeio.anytype.core_ui.extensions.drawable
import com.anytypeio.anytype.presentation.editor.editor.listener.ListenerType
class FilePlaceholder(binding: ItemBlockMediaPlaceholderBinding) : MediaPlaceholder(binding) {
@ -12,6 +13,6 @@ class FilePlaceholder(binding: ItemBlockMediaPlaceholderBinding) : MediaPlacehol
override fun setup() {
title.text = itemView.resources.getString(R.string.hint_upload_file)
title.setCompoundDrawablesRelativeWithIntrinsicBounds(R.drawable.ic_file_light, 0, 0, 0)
binding.fileIcon.setImageDrawable(itemView.context.drawable(R.drawable.ic_file_light))
}
}

View file

@ -3,11 +3,6 @@ package com.anytypeio.anytype.core_ui.features.editor.holders.placeholders
import android.view.View
import android.widget.FrameLayout
import android.widget.TextView
import androidx.core.view.updateLayoutParams
import androidx.core.view.updatePadding
import androidx.recyclerview.widget.RecyclerView
import com.anytypeio.anytype.core_ui.BuildConfig
import com.anytypeio.anytype.core_ui.R
import com.anytypeio.anytype.core_ui.databinding.ItemBlockMediaPlaceholderBinding
import com.anytypeio.anytype.core_ui.extensions.veryLight
import com.anytypeio.anytype.core_ui.features.editor.BlockViewDiffUtil
@ -17,8 +12,7 @@ import com.anytypeio.anytype.core_ui.features.editor.SupportCustomTouchProcessor
import com.anytypeio.anytype.core_ui.features.editor.decoration.DecoratableCardViewHolder
import com.anytypeio.anytype.core_ui.features.editor.decoration.EditorDecorationContainer
import com.anytypeio.anytype.core_ui.features.editor.decoration.applySelectorOffset
import com.anytypeio.anytype.core_utils.ext.dimen
import com.anytypeio.anytype.core_models.ThemeColor
import com.anytypeio.anytype.core_utils.ext.invisible
import com.anytypeio.anytype.presentation.editor.editor.listener.ListenerType
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
import com.google.android.material.card.MaterialCardView
@ -33,7 +27,7 @@ abstract class MediaPlaceholder(
protected val root: View = binding.root
protected val card: MaterialCardView = binding.card
protected val title: TextView = binding.title
protected val title: TextView = binding.fileName
override val decoratableContainer: EditorDecorationContainer
get() = binding.decorationContainer
@ -61,6 +55,7 @@ abstract class MediaPlaceholder(
with(itemView) {
setOnClickListener { placeholderClick(item.id, clicked) }
}
binding.progressBar.invisible()
}
open fun processChangePayload(payloads: List<BlockViewDiffUtil.Payload>, item: BlockView) {

View file

@ -2,6 +2,7 @@ package com.anytypeio.anytype.core_ui.features.editor.holders.placeholders
import com.anytypeio.anytype.core_ui.R
import com.anytypeio.anytype.core_ui.databinding.ItemBlockMediaPlaceholderBinding
import com.anytypeio.anytype.core_ui.extensions.drawable
import com.anytypeio.anytype.presentation.editor.editor.listener.ListenerType
class PicturePlaceholder(binding: ItemBlockMediaPlaceholderBinding) : MediaPlaceholder(binding) {
@ -12,6 +13,6 @@ class PicturePlaceholder(binding: ItemBlockMediaPlaceholderBinding) : MediaPlace
override fun setup() {
title.text = itemView.resources.getString(R.string.hint_upload_image)
title.setCompoundDrawablesRelativeWithIntrinsicBounds(R.drawable.ic_picture, 0, 0, 0)
binding.fileIcon.setImageDrawable(itemView.context.drawable(R.drawable.ic_picture))
}
}

View file

@ -2,6 +2,7 @@ package com.anytypeio.anytype.core_ui.features.editor.holders.placeholders
import com.anytypeio.anytype.core_ui.R
import com.anytypeio.anytype.core_ui.databinding.ItemBlockMediaPlaceholderBinding
import com.anytypeio.anytype.core_ui.extensions.drawable
import com.anytypeio.anytype.presentation.editor.editor.listener.ListenerType
class VideoPlaceholder(binding: ItemBlockMediaPlaceholderBinding) : MediaPlaceholder(binding) {
@ -12,6 +13,6 @@ class VideoPlaceholder(binding: ItemBlockMediaPlaceholderBinding) : MediaPlaceho
override fun setup() {
title.text = itemView.resources.getString(R.string.hint_upload_video)
title.setCompoundDrawablesRelativeWithIntrinsicBounds(R.drawable.ic_video, 0, 0, 0)
binding.fileIcon.setImageDrawable(itemView.context.drawable(R.drawable.ic_video))
}
}

View file

@ -1,54 +1,27 @@
package com.anytypeio.anytype.core_ui.features.editor.holders.upload
import android.view.View
import android.widget.FrameLayout
import com.anytypeio.anytype.core_models.Url
import com.anytypeio.anytype.core_ui.BuildConfig
import com.anytypeio.anytype.core_ui.R
import com.anytypeio.anytype.core_ui.databinding.ItemBlockBookmarkUploadingBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockFileUploadingBinding
import com.anytypeio.anytype.core_ui.features.editor.BlockViewDiffUtil
import com.anytypeio.anytype.core_ui.databinding.ItemBlockMediaPlaceholderBinding
import com.anytypeio.anytype.core_ui.extensions.drawable
import com.anytypeio.anytype.core_ui.features.editor.decoration.DecoratableCardViewHolder
import com.anytypeio.anytype.core_ui.features.editor.decoration.EditorDecorationContainer
import com.anytypeio.anytype.core_ui.features.editor.decoration.applySelectorOffset
import com.anytypeio.anytype.core_utils.ext.dimen
import com.anytypeio.anytype.core_utils.ext.indentize
import com.anytypeio.anytype.presentation.editor.editor.listener.ListenerType
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
class BookmarkUpload(
private val binding: ItemBlockBookmarkUploadingBinding
) : MediaUpload(binding.root), DecoratableCardViewHolder {
override val root: View = itemView
override val decoratableContainer: EditorDecorationContainer
get() = binding.decorationContainer
override val decoratableCard: View = binding.card
binding: ItemBlockMediaPlaceholderBinding
) : MediaUpload(binding), DecoratableCardViewHolder {
override fun uploadClick(target: String, clicked: (ListenerType) -> Unit) {
clicked(ListenerType.File.Upload(target))
clicked(ListenerType.Bookmark.Upload(target))
}
fun setUrl(url: Url?) {
binding.editUrl.text = url
binding.fileName.text = url
}
@Deprecated("Pre-nested-styling legacy.")
override fun indentize(item: BlockView.Indentable) {
// Do nothing.
}
override fun select(isSelected: Boolean) {
binding.selected.isSelected = isSelected
}
override fun applyDecorations(decorations: List<BlockView.Decoration>) {
super.applyDecorations(decorations)
binding.selected.applySelectorOffset<FrameLayout.LayoutParams>(
content = binding.card,
res = itemView.resources
)
override fun bind(item: BlockView.Upload, clicked: (ListenerType) -> Unit) {
super.bind(item, clicked)
binding.fileIcon.setImageDrawable(itemView.context.drawable(R.drawable.ic_bookmark_inactive))
}
}

View file

@ -1,47 +1,22 @@
package com.anytypeio.anytype.core_ui.features.editor.holders.upload
import android.view.View
import android.widget.FrameLayout
import com.anytypeio.anytype.core_ui.BuildConfig
import com.anytypeio.anytype.core_ui.R
import com.anytypeio.anytype.core_ui.databinding.ItemBlockFileUploadingBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockMediaPlaceholderBinding
import com.anytypeio.anytype.core_ui.extensions.drawable
import com.anytypeio.anytype.core_ui.features.editor.decoration.DecoratableCardViewHolder
import com.anytypeio.anytype.core_ui.features.editor.decoration.EditorDecorationContainer
import com.anytypeio.anytype.core_ui.features.editor.decoration.applySelectorOffset
import com.anytypeio.anytype.core_utils.ext.dimen
import com.anytypeio.anytype.core_utils.ext.indentize
import com.anytypeio.anytype.presentation.editor.editor.listener.ListenerType
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
class FileUpload(
private val binding: ItemBlockFileUploadingBinding
) : MediaUpload(binding.root), DecoratableCardViewHolder {
override val root: View = itemView
override val decoratableContainer: EditorDecorationContainer
get() = binding.decorationContainer
override val decoratableCard: View = binding.card
binding: ItemBlockMediaPlaceholderBinding
) : MediaUpload(binding), DecoratableCardViewHolder {
override fun uploadClick(target: String, clicked: (ListenerType) -> Unit) {
clicked(ListenerType.File.Upload(target))
}
@Deprecated("Pre-nested-styling legacy.")
override fun indentize(item: BlockView.Indentable) {
// Do nothing.
}
override fun select(isSelected: Boolean) {
binding.selected.isSelected = isSelected
}
override fun applyDecorations(decorations: List<BlockView.Decoration>) {
super.applyDecorations(decorations)
binding.selected.applySelectorOffset<FrameLayout.LayoutParams>(
content = binding.card,
res = itemView.resources
)
override fun bind(item: BlockView.Upload, clicked: (ListenerType) -> Unit) {
super.bind(item, clicked)
binding.fileIcon.setImageDrawable(itemView.context.drawable(R.drawable.ic_file_light_inactive))
}
}

View file

@ -1,29 +1,27 @@
package com.anytypeio.anytype.core_ui.features.editor.holders.upload
import android.view.View
import androidx.core.view.updateLayoutParams
import androidx.core.view.updatePadding
import androidx.recyclerview.widget.RecyclerView
import com.anytypeio.anytype.core_ui.BuildConfig
import com.anytypeio.anytype.core_ui.R
import android.widget.FrameLayout
import com.anytypeio.anytype.core_ui.databinding.ItemBlockMediaPlaceholderBinding
import com.anytypeio.anytype.core_ui.features.editor.BlockViewDiffUtil
import com.anytypeio.anytype.core_ui.features.editor.BlockViewHolder
import com.anytypeio.anytype.core_ui.features.editor.EditorTouchProcessor
import com.anytypeio.anytype.core_ui.features.editor.SupportCustomTouchProcessor
import com.anytypeio.anytype.core_utils.ext.dimen
import com.anytypeio.anytype.core_ui.features.editor.decoration.DecoratableCardViewHolder
import com.anytypeio.anytype.core_ui.features.editor.decoration.EditorDecorationContainer
import com.anytypeio.anytype.core_ui.features.editor.decoration.applySelectorOffset
import com.anytypeio.anytype.presentation.editor.editor.listener.ListenerType
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
abstract class MediaUpload(
view: View
) : BlockViewHolder(view),
val binding: ItemBlockMediaPlaceholderBinding
) : BlockViewHolder(binding.root),
BlockViewHolder.IndentableHolder,
SupportCustomTouchProcessor {
SupportCustomTouchProcessor, DecoratableCardViewHolder {
abstract val root: View
override val decoratableContainer: EditorDecorationContainer get() = binding.decorationContainer
override val decoratableCard: View get() = binding.card
abstract fun uploadClick(target: String, clicked: (ListenerType) -> Unit)
abstract override fun indentize(item: BlockView.Indentable)
abstract fun select(isSelected: Boolean)
override val editorTouchProcessor = EditorTouchProcessor(
fallback = { e -> itemView.onTouchEvent(e) }
@ -33,11 +31,10 @@ abstract class MediaUpload(
itemView.setOnTouchListener { v, e -> editorTouchProcessor.process(v, e) }
}
fun bind(
open fun bind(
item: BlockView.Upload,
clicked: (ListenerType) -> Unit
) {
indentize(item)
select(item.isSelected)
with(itemView) {
setOnClickListener { uploadClick(item.id, clicked) }
@ -52,4 +49,21 @@ abstract class MediaUpload(
}
}
}
fun select(isSelected: Boolean) {
binding.selected.isSelected = isSelected
}
@Deprecated("Pre-nested-styling legacy.")
override fun indentize(item: BlockView.Indentable) {
// Do nothing.
}
override fun applyDecorations(decorations: List<BlockView.Decoration>) {
super.applyDecorations(decorations)
binding.selected.applySelectorOffset<FrameLayout.LayoutParams>(
content = binding.card,
res = itemView.resources
)
}
}

View file

@ -1,38 +1,22 @@
package com.anytypeio.anytype.core_ui.features.editor.holders.upload
import android.view.View
import com.anytypeio.anytype.core_ui.BuildConfig
import com.anytypeio.anytype.core_ui.R
import com.anytypeio.anytype.core_ui.databinding.ItemBlockPictureUploadingBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockMediaPlaceholderBinding
import com.anytypeio.anytype.core_ui.extensions.drawable
import com.anytypeio.anytype.core_ui.features.editor.decoration.DecoratableCardViewHolder
import com.anytypeio.anytype.core_ui.features.editor.decoration.DecoratableViewHolder
import com.anytypeio.anytype.core_ui.features.editor.decoration.EditorDecorationContainer
import com.anytypeio.anytype.core_utils.ext.dimen
import com.anytypeio.anytype.core_utils.ext.indentize
import com.anytypeio.anytype.presentation.editor.editor.listener.ListenerType
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
class PictureUpload(
private val binding: ItemBlockPictureUploadingBinding
) : MediaUpload(binding.root), DecoratableViewHolder, DecoratableCardViewHolder {
override val root: View = itemView
override val decoratableContainer: EditorDecorationContainer
get() = binding.decorationContainer
override val decoratableCard: View get() = binding.card
binding: ItemBlockMediaPlaceholderBinding
) : MediaUpload(binding), DecoratableCardViewHolder {
override fun uploadClick(target: String, clicked: (ListenerType) -> Unit) {
clicked(ListenerType.Video.Upload(target))
}
@Deprecated("Pre-nested-styling legacy.")
override fun indentize(item: BlockView.Indentable) {
// Do nothing.
}
override fun select(isSelected: Boolean) {
root.isSelected = isSelected
override fun bind(item: BlockView.Upload, clicked: (ListenerType) -> Unit) {
super.bind(item, clicked)
binding.fileIcon.setImageDrawable(itemView.context.drawable(R.drawable.ic_picture_inactive))
}
}

View file

@ -1,47 +1,22 @@
package com.anytypeio.anytype.core_ui.features.editor.holders.upload
import android.view.View
import android.widget.FrameLayout
import com.anytypeio.anytype.core_ui.BuildConfig
import com.anytypeio.anytype.core_ui.R
import com.anytypeio.anytype.core_ui.databinding.ItemBlockVideoUploadingBinding
import com.anytypeio.anytype.core_ui.databinding.ItemBlockMediaPlaceholderBinding
import com.anytypeio.anytype.core_ui.extensions.drawable
import com.anytypeio.anytype.core_ui.features.editor.decoration.DecoratableCardViewHolder
import com.anytypeio.anytype.core_ui.features.editor.decoration.EditorDecorationContainer
import com.anytypeio.anytype.core_ui.features.editor.decoration.applySelectorOffset
import com.anytypeio.anytype.core_utils.ext.dimen
import com.anytypeio.anytype.core_utils.ext.indentize
import com.anytypeio.anytype.presentation.editor.editor.listener.ListenerType
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
class VideoUpload(
val binding: ItemBlockVideoUploadingBinding
) : MediaUpload(binding.root), DecoratableCardViewHolder {
override val root: View = itemView
override val decoratableContainer: EditorDecorationContainer
get() = binding.decorationContainer
override val decoratableCard: View = binding.card
binding: ItemBlockMediaPlaceholderBinding
) : MediaUpload(binding), DecoratableCardViewHolder {
override fun uploadClick(target: String, clicked: (ListenerType) -> Unit) {
clicked(ListenerType.Video.Upload(target))
}
@Deprecated("Pre-nested-styling legacy.")
override fun indentize(item: BlockView.Indentable) {
// Do nothing.
}
override fun select(isSelected: Boolean) {
binding.selected.isSelected = isSelected
}
override fun applyDecorations(decorations: List<BlockView.Decoration>) {
super.applyDecorations(decorations)
binding.selected.applySelectorOffset<FrameLayout.LayoutParams>(
content = binding.card,
res = itemView.resources
)
override fun bind(item: BlockView.Upload, clicked: (ListenerType) -> Unit) {
super.bind(item, clicked)
binding.fileIcon.setImageDrawable(itemView.context.drawable(R.drawable.ic_video_inactive))
}
}

View file

@ -1,11 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="32"
android:viewportHeight="32">
<path
android:pathData="M15.627,21.766L8.75,25.706V9C8.75,7.205 10.205,5.75 12,5.75H20C21.795,5.75 23.25,7.205 23.25,9V25.706L16.373,21.766L16,21.552L15.627,21.766Z"
android:strokeWidth="1.5"
android:fillColor="#00000000"
android:strokeColor="@color/glyph_inactive"/>
</vector>

View file

@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="25dp"
android:viewportWidth="24"
android:viewportHeight="25">
<path
android:pathData="M11.6272,18.0496L4.75,21.9896V5.2837C4.75,3.4888 6.2051,2.0337 8,2.0337H16C17.7949,2.0337 19.25,3.4888 19.25,5.2837V21.9896L12.3728,18.0496L12,17.836L11.6272,18.0496Z"
android:strokeWidth="1.5"
android:fillColor="#00000000"
android:strokeColor="@color/glyph_inactive"/>
</vector>

View file

@ -0,0 +1,27 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="25dp"
android:viewportWidth="24"
android:viewportHeight="25">
<path
android:pathData="M12.2487,2.6655L13.2208,3.6377L5.1197,11.7388L4.1476,10.7667L12.2487,2.6655Z"
android:fillColor="@color/glyph_inactive"/>
<path
android:pathData="M18.7246,9.1415C20.0866,7.6118 20.0341,5.2662 18.5673,3.7994C17.1005,2.3326 14.7549,2.2801 13.2252,3.6421L12.2519,2.6687C14.3196,0.7706 17.5356,0.8235 19.5394,2.8273C21.5432,4.8311 21.5961,8.0471 19.698,10.1148L18.7246,9.1415Z"
android:fillColor="@color/glyph_inactive"/>
<path
android:pathData="M13.5449,20.164C11.2183,22.4905 7.4463,22.4905 5.1197,20.164C2.7932,17.8374 2.7932,14.0653 5.1197,11.7388L4.1476,10.7667C1.2841,13.6301 1.2841,18.2727 4.1476,21.1361C7.011,23.9996 11.6536,23.9996 14.517,21.1361L13.5449,20.164Z"
android:fillColor="@color/glyph_inactive"/>
<path
android:pathData="M14.193,6.5541L15.1651,7.5262L8.6842,14.0071L7.7121,13.035L14.193,6.5541Z"
android:fillColor="@color/glyph_inactive"/>
<path
android:pathData="M11.2766,16.5995C10.5607,17.3153 9.4001,17.3153 8.6842,16.5995C7.9683,15.8836 7.9683,14.723 8.6842,14.0071L7.7121,13.035C6.4593,14.2877 6.4593,16.3189 7.7121,17.5716C8.9648,18.8244 10.996,18.8244 12.2487,17.5716L11.2766,16.5995Z"
android:fillColor="@color/glyph_inactive"/>
<path
android:pathData="M18.7296,9.1464L19.7017,10.1186L12.2487,17.5716L11.2766,16.5995L18.7296,9.1464Z"
android:fillColor="@color/glyph_inactive"/>
<path
android:pathData="M20.6739,13.035L21.646,14.0071L14.517,21.1361L13.5449,20.164L20.6739,13.035Z"
android:fillColor="@color/glyph_inactive"/>
</vector>

View file

@ -0,0 +1,17 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="25dp"
android:viewportWidth="24"
android:viewportHeight="25">
<path
android:pathData="M9,2.7837H15C16.4249,2.7837 17.4033,2.7849 18.1618,2.8468C18.9028,2.9074 19.3009,3.0184 19.589,3.1652C20.2475,3.5007 20.783,4.0362 21.1185,4.6947C21.2653,4.9828 21.3763,5.3808 21.4369,6.1219C21.4988,6.8804 21.5,7.8588 21.5,9.2837V15.2837C21.5,16.7086 21.4988,17.687 21.4369,18.4455C21.3763,19.1865 21.2653,19.5846 21.1185,19.8727C20.783,20.5312 20.2475,21.0667 19.589,21.4022C19.3009,21.549 18.9028,21.66 18.1618,21.7206C17.4033,21.7825 16.4249,21.7837 15,21.7837H9C7.5751,21.7837 6.5967,21.7825 5.8382,21.7206C5.0972,21.66 4.6991,21.549 4.411,21.4022C3.7525,21.0667 3.217,20.5312 2.8815,19.8727C2.7347,19.5846 2.6237,19.1865 2.5631,18.4455C2.5012,17.687 2.5,16.7086 2.5,15.2837V9.2837C2.5,7.8588 2.5012,6.8804 2.5631,6.1219C2.6237,5.3808 2.7347,4.9828 2.8815,4.6947C3.217,4.0362 3.7525,3.5007 4.411,3.1652C4.6991,3.0184 5.0972,2.9074 5.8382,2.8468C6.5967,2.7849 7.5751,2.7837 9,2.7837ZM22.455,4.0137C23,5.0833 23,6.4834 23,9.2837V15.2837C23,18.084 23,19.4841 22.455,20.5536C21.9757,21.4945 21.2108,22.2594 20.27,22.7387C19.2004,23.2837 17.8003,23.2837 15,23.2837H9C6.1997,23.2837 4.7996,23.2837 3.7301,22.7387C2.7892,22.2594 2.0243,21.4945 1.545,20.5536C1,19.4841 1,18.084 1,15.2837V9.2837C1,6.4834 1,5.0833 1.545,4.0137C2.0243,3.0729 2.7892,2.308 3.7301,1.8287C4.7996,1.2837 6.1997,1.2837 9,1.2837H15C17.8003,1.2837 19.2004,1.2837 20.27,1.8287C21.2108,2.308 21.9757,3.0729 22.455,4.0137Z"
android:fillColor="@color/glyph_inactive"
android:fillType="evenOdd"/>
<path
android:pathData="M8.5,10.2837C7.3954,10.2837 6.5,9.3883 6.5,8.2837C6.5,7.1791 7.3954,6.2837 8.5,6.2837C9.6046,6.2837 10.5,7.1791 10.5,8.2837C10.5,9.3883 9.6046,10.2837 8.5,10.2837Z"
android:fillColor="@color/glyph_inactive"/>
<path
android:pathData="M14.5015,11.8428C14.5126,11.8539 14.5244,11.8656 14.5368,11.878L21.4712,18.773L22.5288,17.7094L15.5945,10.8143C15.5897,10.8096 15.585,10.8048 15.5802,10.8001C15.4938,10.7141 15.4001,10.6209 15.3124,10.5469C15.2131,10.463 15.0734,10.361 14.8849,10.3002C14.6341,10.2192 14.3642,10.2196 14.1137,10.3013C13.9254,10.3626 13.786,10.4651 13.6869,10.5492C13.5994,10.6235 13.506,10.717 13.4198,10.8031C13.4151,10.8079 13.4103,10.8127 13.4056,10.8174L9.5004,14.7226L8.0817,13.3018C7.9954,13.2153 7.9018,13.1215 7.8142,13.047C7.715,12.9627 7.5753,12.8599 7.3866,12.7985C7.1356,12.7167 6.8652,12.7165 6.6141,12.7979C6.4253,12.8591 6.2854,12.9616 6.1861,13.0458C6.0984,13.1202 6.0047,13.2138 5.9183,13.3001L1.47,17.7417L2.5299,18.8031L6.9638,14.3759C6.9763,14.3634 6.988,14.3517 6.9992,14.3406C7.0103,14.3517 7.0221,14.3635 7.0345,14.3759L9.4996,16.8447L14.4662,11.8781C14.4787,11.8656 14.4904,11.8539 14.5015,11.8428Z"
android:fillColor="@color/glyph_inactive"
android:fillType="evenOdd"/>
</vector>

View file

@ -0,0 +1,18 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="25dp"
android:viewportWidth="24"
android:viewportHeight="25">
<group>
<clip-path
android:pathData="M0,0.2837h24v24h-24z"/>
<path
android:pathData="M12,1.0337L12,1.0337A11.25,11.25 0,0 1,23.25 12.2837L23.25,12.2837A11.25,11.25 0,0 1,12 23.5337L12,23.5337A11.25,11.25 0,0 1,0.75 12.2837L0.75,12.2837A11.25,11.25 0,0 1,12 1.0337z"
android:strokeWidth="1.5"
android:fillColor="#00000000"
android:strokeColor="@color/glyph_inactive"/>
<path
android:pathData="M17.7526,12.7162C18.0852,12.5236 18.0852,12.0434 17.7526,11.8508L9.7505,7.2182C9.4172,7.0252 9,7.2657 9,7.6509V16.9165C9,17.3016 9.4172,17.5422 9.7505,17.3492L17.7526,12.7162Z"
android:fillColor="@color/glyph_inactive"/>
</group>
</vector>

View file

@ -1,68 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/fileUploadingPlaceholderRoot"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/item_block_multi_select_mode_selector">
<com.anytypeio.anytype.core_ui.features.editor.decoration.EditorDecorationContainer
android:id="@+id/decorationContainer"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/card"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_48"
android:background="@drawable/rectangle_media_block_placeholder">
<ImageView
android:id="@+id/ivBookmark"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginTop="12dp"
android:layout_marginBottom="12dp"
android:contentDescription="@string/content_description_bookmark_image"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_bookmark_block_loading" />
<TextView
android:id="@+id/editUrl"
style="@style/TextView.ContentStyle.Relations.3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:inputType="none"
android:hint="@string/loading_wait"
android:textColor="@color/glyph_inactive"
android:textColorHint="@color/glyph_inactive"
app:layout_constraintBottom_toBottomOf="@+id/ivBookmark"
app:layout_constraintStart_toEndOf="@+id/ivBookmark"
app:layout_constraintTop_toTopOf="@+id/ivBookmark" />
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:indeterminateTint="@color/glyph_inactive"
app:layout_constraintBottom_toBottomOf="@+id/editUrl"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/editUrl" />
</androidx.constraintlayout.widget.ConstraintLayout>
<View
android:id="@+id/selected"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/item_block_multi_select_mode_selector"
tools:background="@drawable/item_block_multi_select_selected" />
</FrameLayout>

View file

@ -18,6 +18,8 @@
android:paddingEnd="@dimen/default_document_content_padding_start"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/dp_4"
android:paddingBottom="@dimen/dp_4"
android:background="@drawable/item_block_multi_select_mode_selector"
tools:background="@drawable/item_block_multi_select_selected">
@ -30,17 +32,25 @@
<ImageView
android:id="@+id/graphic"
style="@style/DefaultGraphicTextBlockGraphicStyle"
android:layout_width="@dimen/dp_24"
android:layout_height="@dimen/dp_24"
android:contentDescription="@string/content_description_file_icon"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/guideline"
app:layout_constraintTop_toTopOf="parent"
tools:background="@drawable/ic_mime_image" />
<com.anytypeio.anytype.core_ui.widgets.text.TextInputWidget
android:id="@+id/text"
style="@style/DefaultGraphicTextBlockTextStyle"
style="@style/TextView.ContentStyle.Body.Regular"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:ellipsize="middle"
android:maxLines="1"
android:singleLine="true"
android:background="@null"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/graphic"
app:layout_constraintTop_toTopOf="parent"

View file

@ -1,75 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/fileUploadingPlaceholderRoot"
android:background="@drawable/item_block_multi_select_mode_selector"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.anytypeio.anytype.core_ui.features.editor.decoration.EditorDecorationContainer
android:id="@+id/decorationContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/card"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_48"
android:background="@drawable/rectangle_media_block_placeholder">
<ImageView
android:id="@+id/icFile"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginTop="12dp"
android:layout_marginBottom="12dp"
android:contentDescription="@string/content_description_file_icon"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_file_light" />
<TextView
android:id="@+id/editUrl"
style="@style/UploadMediaInActiveTextStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:text="@string/loading_wait"
app:layout_constraintBottom_toBottomOf="@+id/icFile"
app:layout_constraintStart_toEndOf="@+id/icFile"
app:layout_constraintTop_toTopOf="@+id/icFile" />
<ImageView
android:id="@+id/icMore"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:layout_marginEnd="12dp"
android:visibility="invisible"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_block_more" />
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleSmall"
android:indeterminateTint="@color/glyph_inactive"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
app:layout_constraintBottom_toBottomOf="@+id/editUrl"
app:layout_constraintStart_toEndOf="@+id/editUrl"
app:layout_constraintTop_toTopOf="@+id/editUrl" />
</androidx.constraintlayout.widget.ConstraintLayout>
<View
android:id="@+id/selected"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/item_block_multi_select_mode_selector"
tools:background="@drawable/item_block_multi_select_selected" />
</FrameLayout>

View file

@ -0,0 +1,87 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.anytypeio.anytype.core_ui.features.editor.decoration.EditorDecorationContainer
android:id="@+id/decorationContainer"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.google.android.material.card.MaterialCardView
android:id="@+id/card"
style="@style/DefaultMediaPlaceholderBlockCardStyle"
android:layout_width="match_parent"
android:layout_height="52dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/fileIcon"
android:layout_width="@dimen/dp_32"
android:layout_height="@dimen/dp_32"
android:scaleType="center"
android:layout_marginStart="@dimen/dp_12"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@drawable/ic_picture" />
<TextView
android:id="@+id/fileName"
style="@style/TextView.ContentStyle.Body.Callout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="4dp"
android:layout_marginEnd="8dp"
android:drawablePadding="@dimen/default_media_placeholder_title_icon_padding"
android:ellipsize="middle"
android:gravity="center_vertical"
android:maxLines="1"
android:textColor="@color/text_tertiary"
android:text="@string/loading_please_wait"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/progressBar"
app:layout_constraintStart_toEndOf="@+id/fileIcon"
app:layout_constraintTop_toTopOf="parent"
tools:text="https://github.com/anyproto/anytype-kotlin"/>
<TextView
android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/try_again"
style="@style/TextView.UXStyle.Captions.1.Medium"
android:textColor="@color/text_primary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginEnd="@dimen/dp_12"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
<TextView
android:id="@+id/errorMessage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="66dp"
android:layout_marginStart="20dp"
android:textColor="@color/palette_system_red"
style="@style/TextView.UXStyle.Captions.2.Regular"
android:text="@string/block_media_error"/>
<View
android:id="@+id/selected"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/item_block_multi_select_mode_selector"
tools:background="@drawable/item_block_multi_select_selected" />
</FrameLayout>

View file

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root"
android:layout_width="match_parent"
@ -14,22 +15,59 @@
android:id="@+id/card"
style="@style/DefaultMediaPlaceholderBlockCardStyle"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="52dp">
<TextView
android:id="@+id/title"
style="@style/TextView.ContentStyle.Body.Callout"
android:textColor="@color/glyph_active"
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:layout_marginStart="@dimen/default_media_placeholder_title_margin_start"
android:layout_marginTop="14dp"
android:layout_marginBottom="14dp"
android:drawablePadding="@dimen/default_media_placeholder_title_icon_padding"
android:gravity="center_vertical"
tools:drawableStart="@drawable/ic_picture"
tools:text="Add a web bookmark" />
android:layout_height="match_parent">
<ImageView
android:id="@+id/fileIcon"
android:layout_width="@dimen/dp_32"
android:layout_height="@dimen/dp_32"
android:scaleType="center"
android:layout_marginStart="@dimen/dp_12"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@drawable/ic_picture" />
<TextView
android:id="@+id/fileName"
style="@style/TextView.ContentStyle.Body.Callout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="4dp"
android:layout_marginEnd="8dp"
android:drawablePadding="@dimen/default_media_placeholder_title_icon_padding"
android:ellipsize="middle"
android:gravity="center_vertical"
android:maxLines="1"
android:textColor="@color/text_tertiary"
android:text="@string/loading_please_wait"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/progressBar"
app:layout_constraintStart_toEndOf="@+id/fileIcon"
app:layout_constraintTop_toTopOf="parent"
tools:text="https://github.com/anyproto/anytype-kotlin"/>
<com.google.android.material.progressindicator.CircularProgressIndicator
android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminate="true"
app:trackColor="@color/glyph_inactive"
app:indicatorColor="#B6B6B6"
app:indicatorSize="20dp"
app:trackThickness="@dimen/dp_2"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginEnd="@dimen/dp_12"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
<View

View file

@ -1,58 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
style="@style/BlockMediaRootLayoutStyle"
android:background="@drawable/item_block_multi_select_mode_selector"
android:layout_height="wrap_content">
<com.anytypeio.anytype.core_ui.features.editor.decoration.EditorDecorationContainer
android:id="@+id/decorationContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/card"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_48"
android:background="@drawable/rectangle_media_block_placeholder">
<ImageView
android:id="@+id/icPicture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginTop="12dp"
android:layout_marginBottom="12dp"
android:contentDescription="@string/content_description_file_icon"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_file_light" />
<TextView
android:id="@+id/editUrl"
style="@style/UploadMediaTextStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:inputType="none"
android:text="@string/loading_wait"
android:textColor="@color/glyph_active"
app:layout_constraintBottom_toBottomOf="@+id/icPicture"
app:layout_constraintStart_toEndOf="@+id/icPicture"
app:layout_constraintTop_toTopOf="@+id/icPicture" />
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleSmall"
android:indeterminateTint="@color/glyph_inactive"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
app:layout_constraintBottom_toBottomOf="@+id/editUrl"
app:layout_constraintStart_toEndOf="@+id/editUrl"
app:layout_constraintTop_toTopOf="@+id/editUrl" />
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>

View file

@ -1,63 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/videoUploadingPlaceholderRoot"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/item_block_multi_select_mode_selector">
<com.anytypeio.anytype.core_ui.features.editor.decoration.EditorDecorationContainer
android:id="@+id/decorationContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/card"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_48"
android:background="@drawable/rectangle_media_block_placeholder">
<ImageView
android:id="@+id/icVideo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginTop="12dp"
android:layout_marginBottom="12dp"
android:contentDescription="@string/content_description_file_icon"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_video" />
<TextView
android:id="@+id/editUrl"
style="@style/UploadMediaInActiveTextStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:text="@string/loading_wait"
app:layout_constraintBottom_toBottomOf="@+id/icVideo"
app:layout_constraintStart_toEndOf="@+id/icVideo"
app:layout_constraintTop_toTopOf="@+id/icVideo" />
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleSmall"
android:indeterminateTint="@color/glyph_inactive"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
app:layout_constraintBottom_toBottomOf="@+id/editUrl"
app:layout_constraintStart_toEndOf="@+id/editUrl"
app:layout_constraintTop_toTopOf="@+id/editUrl" />
</androidx.constraintlayout.widget.ConstraintLayout>
<View
android:id="@+id/selected"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/item_block_multi_select_mode_selector" />
</FrameLayout>

View file

@ -652,16 +652,6 @@
<item name="android:layout_marginTop">@dimen/default_graphic_text_graphic_margin_top</item>
</style>
<!-- text -->
<style name="DefaultGraphicTextBlockTextStyle" parent="TextView.ContentStyle.Body.Regular">
<item name="android:layout_width">@dimen/default_graphic_text_graphic_width</item>
<item name="android:layout_height">@dimen/default_graphic_text_graphic_width</item>
<item name="android:layout_marginStart">@dimen/default_graphic_text_text_margin_start</item>
<item name="android:paddingTop">@dimen/default_graphic_text_text_padding_top</item>
<item name="android:paddingBottom">@dimen/default_graphic_text_text_padding_bottom</item>
<item name="android:background">@null</item>
</style>
<!-- Media placeholder {root {card {title}} selected}-->
<!-- root -->
<style name="DefaultMediaPlaceholderBlockRootStyle">

View file

@ -2281,7 +2281,7 @@ class BlockAdapterTest {
val views = listOf(
BlockView.Media.File(
id = MockDataFactory.randomString(),
hash = MockDataFactory.randomString(),
targetObjectId = MockDataFactory.randomString(),
indent = MockDataFactory.randomInt(),
mime = MockDataFactory.randomString(),
size = MockDataFactory.randomLong(),
@ -2292,7 +2292,7 @@ class BlockAdapterTest {
),
BlockView.Media.File(
id = MockDataFactory.randomString(),
hash = MockDataFactory.randomString(),
targetObjectId = MockDataFactory.randomString(),
indent = MockDataFactory.randomInt(),
mime = MockDataFactory.randomString(),
size = MockDataFactory.randomLong(),
@ -2330,7 +2330,7 @@ class BlockAdapterTest {
val file = BlockView.Media.File(
id = MockDataFactory.randomString(),
hash = MockDataFactory.randomString(),
targetObjectId = MockDataFactory.randomString(),
indent = MockDataFactory.randomInt(),
mime = MockDataFactory.randomString(),
size = MockDataFactory.randomLong(),
@ -2594,7 +2594,7 @@ class BlockAdapterTest {
private fun givenPicture() = BlockView.Media.Picture(
id = MockDataFactory.randomUuid(),
hash = MockDataFactory.randomString(),
targetObjectId = MockDataFactory.randomString(),
indent = MockDataFactory.randomInt(),
mime = MockDataFactory.randomString(),
name = MockDataFactory.randomString(),
@ -2606,7 +2606,7 @@ class BlockAdapterTest {
private fun givenVideo() = BlockView.Media.Video(
id = MockDataFactory.randomUuid(),
indent = MockDataFactory.randomInt(),
hash = MockDataFactory.randomString(),
targetObjectId = MockDataFactory.randomString(),
url = MockDataFactory.randomString(),
mime = MockDataFactory.randomString(),
name = MockDataFactory.randomString(),

View file

@ -646,7 +646,7 @@ class BlockViewDiffUtilTest {
val oldBlock = BlockView.Media.File(
id = id,
hash = MockDataFactory.randomString(),
targetObjectId = MockDataFactory.randomString(),
indent = MockDataFactory.randomInt(),
mime = MockDataFactory.randomString(),
size = MockDataFactory.randomLong(),

View file

@ -114,7 +114,7 @@ class BlockViewSearchTextTest {
id = MockDataFactory.randomString(),
name = MockDataFactory.randomString(),
searchFields = listOf(field1),
hash = MockDataFactory.randomString(),
targetObjectId = MockDataFactory.randomString(),
mime = MockDataFactory.randomString(),
size = MockDataFactory.randomLong(),
indent = 0,

View file

@ -34,7 +34,7 @@ class BlockViewTest {
fun `should return video block with view type Done`() {
val block = BlockView.Media.Video(
id = MockDataFactory.randomUuid(),
hash = MockDataFactory.randomString(),
targetObjectId = MockDataFactory.randomString(),
url = MockDataFactory.randomString(),
size = MockDataFactory.randomLong(),
mime = MockDataFactory.randomString(),

View file

@ -10,29 +10,29 @@ import com.anytypeio.anytype.domain.config.Gateway
class UrlBuilder(val gateway: Gateway) {
/**
* Builds image url for given [hash]
* Builds image url for given [path]
*/
fun image(hash: String?): Url = gateway.provide() + IMAGE_PATH + hash + DEFAULT_WIDTH_PARAM
fun image(path: String): Url = gateway.provide() + IMAGE_PATH + path + DEFAULT_WIDTH_PARAM
/**
* Builds original image url for given [hash]
* Builds original image url for given [path]
*/
fun original(hash: String?): Url = gateway.provide() + IMAGE_PATH + hash
fun original(path: String): Url = gateway.provide() + IMAGE_PATH + path
/**
* Builds small image url for given [hash]
* Builds small image url for given [path]
*/
fun thumbnail(hash: String): Url = gateway.provide() + IMAGE_PATH + hash + THUMBNAIL_WIDTH_PARAM
fun thumbnail(path: String): Url = gateway.provide() + IMAGE_PATH + path + THUMBNAIL_WIDTH_PARAM
/**
* Builds file url for given [hash]
* Builds file url for given [path]
*/
fun file(hash: String?): Url = gateway.provide() + FILE_PATH + hash
fun file(path: String): Url = gateway.provide() + FILE_PATH + path
/**
* Builds video url for given [hash]
* Builds video url for given [path]
*/
fun video(hash: String?): Url = gateway.provide() + FILE_PATH + hash
fun video(path: String): Url = gateway.provide() + FILE_PATH + path
companion object {
const val IMAGE_PATH = "/image/"

View file

@ -30,16 +30,6 @@ class UrlBuilderTest {
assertEquals(expected, actual)
}
@Test
fun `should return url with null at the end when image hash is null`() {
val hash = null
val expected =
gateway.provide() + UrlBuilder.IMAGE_PATH + null + UrlBuilder.DEFAULT_WIDTH_PARAM
val actual = urlBuilder.image(hash)
assertEquals(expected, actual)
}
@Test
fun `should return url without hash when image hash is empty`() {
val hash = ""
@ -58,15 +48,6 @@ class UrlBuilderTest {
assertEquals(expected, actual)
}
@Test
fun `should return url with null at the end when file hash is null`() {
val hash = null
val expected = gateway.provide() + UrlBuilder.FILE_PATH + null
val actual = urlBuilder.file(hash)
assertEquals(expected, actual)
}
@Test
fun `should return url without hash when file hash is empty`() {
val hash = ""
@ -85,15 +66,6 @@ class UrlBuilderTest {
assertEquals(expected, actual)
}
@Test
fun `should return url with null at the end when video hash is null`() {
val hash = null
val expected = gateway.provide() + UrlBuilder.FILE_PATH + null
val actual = urlBuilder.video(hash)
assertEquals(expected, actual)
}
@Test
fun `should return url without hash when video hash is empty`() {
val hash = ""

View file

@ -1,5 +1,5 @@
[versions]
middlewareVersion = "v0.30.11"
middlewareVersion = "v0.31.3"
kotlinVersion = '1.8.22'
androidxCoreVersion = "1.12.0"

View file

@ -808,6 +808,7 @@
<string name="anytype_analytics">Anytype Analytics</string>
<string name="anytype_analytics_msg">Understanding how people use Anytype helps us improve the product. This version of Anytype includes the analytics code that protects your privacy.\nIt doesn\'t record the actual document\'s content but still allows us to understand how you use Anytype.\nStay subscribed to our mailing list, as we will soon announce a new release that enables you to opt-out.</string>
<string name="retry">Retry</string>
<string name="try_again">Try again</string>
<string name="unsplash">Unsplash</string>
<string name="cancel_deletion">Cancel deletion</string>
<string name="logout_and_clear_local_data">Logout and clear local data</string>
@ -1249,6 +1250,7 @@
<string name="any_object_creation_menu_move_right">Move right</string>
<string name="clipboard_panel_create_object_from_clipboard">Create object from clipboard</string>
<string name="block_media_error">Something went wrong, please try again</string>
<string name="relation_value_create_new">Create \"%1$s\"</string>
</resources>

View file

@ -28,6 +28,6 @@ class UnsplashMiddleware @Inject constructor(
override fun download(id: Id): Hash {
val request = Download.Request(pictureId = id).also { logger.logRequest(it) }
val response = service.unsplashDownload(request = request).also { logger.logResponse(it) }
return response.hash
return response.objectId
}
}

View file

@ -220,7 +220,7 @@ class BlockMiddleware(
override suspend fun uploadFile(
command: Command.UploadFile
): String = middleware.fileUpload(command).hash
): String = middleware.fileUpload(command).objectId
override suspend fun downloadFile(
command: Command.DownloadFile

View file

@ -826,13 +826,13 @@ class Middleware @Inject constructor(
if (BuildConfig.DEBUG) logRequest(request)
val response = service.fileUpload(request)
if (BuildConfig.DEBUG) logResponse(response)
return Response.Media.Upload(response.hash)
return Response.Media.Upload(response.objectId)
}
@Throws(Exception::class)
fun fileDownload(command: Command.DownloadFile): Rpc.File.Download.Response {
val request = Rpc.File.Download.Request(
hash = command.hash,
objectId = command.objectId,
path = command.path
)
if (BuildConfig.DEBUG) logRequest(request)

View file

@ -126,11 +126,11 @@ fun anytype.Event.Message.toCoreModels(
with(event) {
Event.Command.UpdateFileBlock(
context = context,
id = id,
blockId = id,
targetObjectId = targetObjectId?.value_,
state = state?.value_?.toCoreModels(),
type = type?.value_?.toCoreModels(),
name = name?.value_,
hash = hash?.value_,
mime = mime?.value_,
size = size?.value_
)

View file

@ -330,7 +330,7 @@ fun MBlock.toCoreModelsDivider(): Block.Content.Divider {
fun MBlock.toCoreModelsFile(): Block.Content.File {
val content = checkNotNull(file_)
return Block.Content.File(
hash = content.hash,
targetObjectId = content.targetObjectId,
name = content.name,
mime = content.mime,
size = content.size,
@ -551,6 +551,7 @@ fun MDVViewType.toCoreModels(): DVViewerType = when (this) {
MDVViewType.Gallery -> DVViewerType.GALLERY
MDVViewType.Kanban -> DVViewerType.BOARD
MDVViewType.Calendar -> DVViewerType.CALENDAR
MDVViewType.Graph -> DVViewerType.GRAPH
}
fun MDVFilter.toCoreModels(): DVFilter = DVFilter(

View file

@ -113,7 +113,7 @@ fun Block.Content.Bookmark.State.toMiddlewareModel(): MBookmarkState = when (thi
}
fun Block.Content.File.toMiddlewareModel(): MBFile = MBFile(
hash = hash.orEmpty(),
targetObjectId = targetObjectId.orEmpty(),
name = name.orEmpty(),
mime = mime.orEmpty(),
size = size ?: 0,
@ -340,6 +340,7 @@ fun Block.Content.DataView.Viewer.Type.toMiddlewareModel(): MDVViewType = when (
Block.Content.DataView.Viewer.Type.GALLERY -> MDVViewType.Gallery
Block.Content.DataView.Viewer.Type.BOARD -> MDVViewType.Kanban
Block.Content.DataView.Viewer.Type.CALENDAR -> MDVViewType.Calendar
Block.Content.DataView.Viewer.Type.GRAPH -> MDVViewType.Graph
}
fun Block.Content.DataView.Sort.toMiddlewareModel(): MDVSort =

View file

@ -150,7 +150,7 @@ class MiddlewareEventChannelTest {
@Test
fun `should return UpdateBlockFile event`() {
val hash = "785687346534hfjdbsjfbds"
val targetObjectId = "785687346534hfjdbsjfbds"
val name = "video1.mp4"
val mime = "video/*"
val size = 999111L
@ -162,7 +162,7 @@ class MiddlewareEventChannelTest {
val msg = anytype.Event.Block.Set.File(
id = id,
hash = anytype.Event.Block.Set.File.Hash(hash),
targetObjectId = anytype.Event.Block.Set.File.TargetObjectId(targetObjectId),
mime = anytype.Event.Block.Set.File.Mime(mime),
size = anytype.Event.Block.Set.File.Size(size),
type = anytype.Event.Block.Set.File.Type(type),
@ -181,8 +181,8 @@ class MiddlewareEventChannelTest {
val expected = listOf(
Event.Command.UpdateFileBlock(
context = context,
id = id,
hash = hash,
blockId = id,
targetObjectId = targetObjectId,
mime = mime,
size = size,
type = com.anytypeio.anytype.core_models.Block.Content.File.Type.VIDEO,
@ -220,7 +220,7 @@ class MiddlewareEventChannelTest {
val expected = listOf(
Event.Command.UpdateFileBlock(
context = context,
id = id
blockId = id
)
)

View file

@ -74,7 +74,7 @@ class DocumentExternalEventReducer : StateReducer<List<Block>, Event> {
val content = block.content<Block.Content.File>()
block.copy(
content = content.copy(
hash = event.hash ?: content.hash,
targetObjectId = event.targetObjectId ?: content.targetObjectId,
name = event.name ?: content.name,
mime = event.mime ?: content.mime,
size = event.size ?: content.size,
@ -83,7 +83,7 @@ class DocumentExternalEventReducer : StateReducer<List<Block>, Event> {
)
)
},
target = { block -> block.id == event.id }
target = { block -> block.id == event.blockId }
)
is Event.Command.BookmarkGranularChange -> state.replace(
replacement = { block ->

View file

@ -196,6 +196,7 @@ import com.anytypeio.anytype.presentation.editor.selection.updateTableBlockTab
import com.anytypeio.anytype.presentation.editor.template.SelectTemplateViewState
import com.anytypeio.anytype.presentation.editor.toggle.ToggleStateHolder
import com.anytypeio.anytype.presentation.extension.getProperObjectName
import com.anytypeio.anytype.presentation.extension.getUrlForFileBlock
import com.anytypeio.anytype.presentation.extension.sendAnalyticsBlockActionEvent
import com.anytypeio.anytype.presentation.extension.sendAnalyticsBlockAlignEvent
import com.anytypeio.anytype.presentation.extension.sendAnalyticsBlockBackgroundEvent
@ -1806,6 +1807,7 @@ class EditorViewModel(
needSortByDownloads = true
if (content.state == Content.File.State.DONE) {
targetActions.addIfNotExists(ActionItemType.Download)
targetActions.addIfNotExists(ActionItemType.OpenObject)
} else {
excludedActions.add(ActionItemType.Download)
}
@ -3608,6 +3610,13 @@ class EditorViewModel(
else -> Unit
}
}
is ListenerType.Bookmark.Upload -> {
when (mode) {
EditorMode.Edit -> Unit
EditorMode.Select -> onBlockMultiSelectClicked(clicked.target)
else -> Unit
}
}
is ListenerType.Bookmark.Error -> {
when (mode) {
EditorMode.Edit -> onFailedBookmarkClicked(clicked.item)
@ -3647,18 +3656,21 @@ class EditorViewModel(
is ListenerType.Picture.View -> {
when (mode) {
EditorMode.Edit, EditorMode.Locked -> {
val target = blocks.find { it.id == clicked.target }
if (target != null) {
val content = target.content
check(content is Content.File)
val fileBlock = blocks.find { it.id == clicked.target }
val url = urlBuilder.getUrlForFileBlock(
fileBlock = fileBlock,
isOriginalImage = true
)
if (url != null ) {
dispatch(
Command.OpenFullScreenImage(
target = clicked.target,
url = urlBuilder.original(content.hash)
url = url
)
)
} else {
Timber.e("Could not find target for picture")
Timber.e("Block is not File or with wrong state, can't proceed with download")
sendToast("Something went wrong. Couldn't open image")
}
}
EditorMode.Select -> onBlockMultiSelectClicked(clicked.target)
@ -4118,17 +4130,21 @@ class EditorViewModel(
}
}
private fun onFileClicked(id: String) {
val file = blocks.find { it.id == id }
if (file != null && file.content is Content.File) {
val cnt = (file.content as Content.File)
private fun onFileClicked(blockId: String) {
val fileBlock = blocks.find { it.id == blockId }
val url = urlBuilder.getUrlForFileBlock(
fileBlock = fileBlock
)
if (url != null) {
dispatch(
Command.OpenFileByDefaultApp(
id = id,
mime = cnt.mime.orEmpty(),
uri = urlBuilder.file(cnt.hash)
id = blockId,
uri = url
)
)
} else {
Timber.e("Block is not File or with wrong state, can't proceed with open")
sendToast("Something went wrong. Couldn't open file.")
}
}
@ -4145,7 +4161,7 @@ class EditorViewModel(
viewModelScope.launch {
orchestrator.proxies.intents.send(
Media.ShareFile(
hash = content.hash.orEmpty(),
objectId = content.targetObjectId.orEmpty(),
name = content.name.orEmpty(),
type = content.type,
onDownloaded = onDownloaded
@ -4157,30 +4173,29 @@ class EditorViewModel(
}
}
fun startDownloadingFile(id: String) {
fun startDownloadingFile(blockId: Id) {
Timber.d("startDownloadingFile, id:[$id]")
Timber.d("startDownloadingFile, for block:[$blockId]")
sendToast("Downloading file in background...")
val block = blocks.firstOrNull { it.id == id }
val content = block?.content
if (content is Content.File && content.state == Content.File.State.DONE) {
val fileBlock = blocks.firstOrNull { it.id == blockId }
val fileContent = fileBlock?.content as? Content.File
val url = urlBuilder.getUrlForFileBlock(fileBlock)
if (fileContent != null && url != null) {
viewModelScope.launch {
orchestrator.proxies.intents.send(
Media.DownloadFile(
url = when (content.type) {
Content.File.Type.IMAGE -> urlBuilder.image(content.hash)
else -> urlBuilder.file(content.hash)
},
name = content.name.orEmpty(),
type = content.type
url = url,
name = fileContent.name.orEmpty(),
type = fileContent.type
)
)
}
} else {
Timber.e("Block is not File or with wrong state, can't proceed with download")
sendToast("Something went wrong. Couldn't download file.")
}
}
@ -5581,6 +5596,18 @@ class EditorViewModel(
sendToast("This bookmark doesnt have a source.")
}
}
is Content.File -> {
val target = content.targetObjectId
if (target != null) {
proceedWithOpeningObject(target)
viewModelScope.sendAnalyticsOpenAsObject(
analytics = analytics,
type = EventsDictionary.Type.bookmark
)
} else {
sendToast("This object doesnt have a target id")
}
}
else -> sendToast("Unexpected object")
}
} else {

View file

@ -31,7 +31,6 @@ sealed class Command {
*/
data class OpenFileByDefaultApp(
val id: String,
val mime: String,
val uri: String
) : Command()

View file

@ -183,7 +183,7 @@ sealed class Intent {
) : Media()
class ShareFile(
val hash: Hash,
val objectId: Id,
val name: String,
val type: Block.Content.File.Type?,
val onDownloaded: (Uri) -> Unit

View file

@ -449,7 +449,7 @@ class Orchestrator(
is Intent.Media.ShareFile -> {
documentFileShareDownloader.async(
params = MiddlewareShareDownloader.Params(
hash = intent.hash,
objectId = intent.objectId,
name = intent.name
)
).suspendFold(

View file

@ -113,6 +113,10 @@ fun List<BlockView>.singleStylingMode(
mode = BlockView.Mode.READ,
isSelected = isSelected
)
is BlockView.Upload.Bookmark -> view.copy(
mode = BlockView.Mode.READ,
isSelected = isSelected
)
is BlockView.MediaPlaceholder.File -> view.copy(
mode = BlockView.Mode.READ,
isSelected = isSelected
@ -263,6 +267,10 @@ fun List<BlockView>.enterSAM(
mode = BlockView.Mode.READ,
isSelected = isSelected
)
is BlockView.Upload.Bookmark -> view.copy(
mode = BlockView.Mode.READ,
isSelected = isSelected
)
is BlockView.MediaPlaceholder.File -> view.copy(
mode = BlockView.Mode.READ,
isSelected = isSelected
@ -458,6 +466,7 @@ fun List<BlockView>.updateCursorAndEditMode(
is BlockView.Upload.File -> view.copy(mode = BlockView.Mode.EDIT, isSelected = false)
is BlockView.Upload.Video -> view.copy(mode = BlockView.Mode.EDIT, isSelected = false)
is BlockView.Upload.Picture -> view.copy(mode = BlockView.Mode.EDIT, isSelected = false)
is BlockView.Upload.Bookmark -> view.copy(mode = BlockView.Mode.EDIT, isSelected = false)
is BlockView.MediaPlaceholder.File -> view.copy(
mode = BlockView.Mode.EDIT,
isSelected = false
@ -518,6 +527,7 @@ fun List<BlockView>.toReadMode(): List<BlockView> = map { view ->
is BlockView.Upload.File -> view.copy(mode = BlockView.Mode.READ)
is BlockView.Upload.Video -> view.copy(mode = BlockView.Mode.READ)
is BlockView.Upload.Picture -> view.copy(mode = BlockView.Mode.READ)
is BlockView.Upload.Bookmark -> view.copy(mode = BlockView.Mode.READ)
is BlockView.MediaPlaceholder.File -> view.copy(mode = BlockView.Mode.READ)
is BlockView.MediaPlaceholder.Video -> view.copy(mode = BlockView.Mode.READ)
is BlockView.MediaPlaceholder.Bookmark -> view.copy(mode = BlockView.Mode.READ)
@ -550,6 +560,7 @@ fun List<BlockView>.toEditMode(): List<BlockView> = map { view ->
is BlockView.Upload.File -> view.copy(mode = BlockView.Mode.EDIT, isSelected = false)
is BlockView.Upload.Video -> view.copy(mode = BlockView.Mode.EDIT, isSelected = false)
is BlockView.Upload.Picture -> view.copy(mode = BlockView.Mode.EDIT, isSelected = false)
is BlockView.Upload.Bookmark -> view.copy(mode = BlockView.Mode.EDIT, isSelected = false)
is BlockView.MediaPlaceholder.File -> view.copy(
mode = BlockView.Mode.EDIT,
isSelected = false
@ -1065,6 +1076,7 @@ fun BlockView.updateSelection(newSelection: Boolean) = when (this) {
is BlockView.MediaPlaceholder.Picture -> copy(isSelected = newSelection)
is BlockView.Error.Picture -> copy(isSelected = newSelection)
is BlockView.Upload.Picture -> copy(isSelected = newSelection)
is BlockView.Upload.Bookmark -> copy(isSelected = newSelection)
is BlockView.DividerLine -> copy(isSelected = newSelection)
is BlockView.DividerDots -> copy(isSelected = newSelection)
is BlockView.Code -> copy(isSelected = newSelection)

View file

@ -11,6 +11,7 @@ sealed interface ListenerType {
sealed class Bookmark : ListenerType {
data class View(val item: BlockView.Media.Bookmark) : Bookmark()
data class Placeholder(val target: String) : Bookmark()
data class Upload(val target: String) : File()
data class Error(val item: BlockView.Error.Bookmark) : Bookmark()
}

View file

@ -144,7 +144,7 @@ private fun createBaseMentionMark(
)
}
if (!image.isNullOrEmpty()) {
if (!image.isNullOrBlank()) {
return Markup.Mark.Mention.WithImage(
from = from,
to = to,

View file

@ -780,7 +780,8 @@ sealed class BlockView : ViewType {
override val mode: Mode = Mode.EDIT,
override val isSelected: Boolean = false,
override val background: ThemeColor = ThemeColor.DEFAULT,
override val decorations: List<Decoration> = emptyList()
override val decorations: List<Decoration> = emptyList(),
val name: String? = null
) : Error() {
override fun getViewType() = HOLDER_FILE_ERROR
}
@ -795,7 +796,8 @@ sealed class BlockView : ViewType {
override val mode: Mode = Mode.EDIT,
override val isSelected: Boolean = false,
override val background: ThemeColor = ThemeColor.DEFAULT,
override val decorations: List<Decoration> = emptyList()
override val decorations: List<Decoration> = emptyList(),
val name: String? = null
) : Error() {
override fun getViewType() = HOLDER_VIDEO_ERROR
}
@ -809,7 +811,8 @@ sealed class BlockView : ViewType {
override val mode: Mode = Mode.EDIT,
override val isSelected: Boolean = false,
override val background: ThemeColor = ThemeColor.DEFAULT,
override val decorations: List<Decoration> = emptyList()
override val decorations: List<Decoration> = emptyList(),
val name: String? = null
) : Error() {
override fun getViewType() = HOLDER_PICTURE_ERROR
}
@ -997,8 +1000,9 @@ sealed class BlockView : ViewType {
val size: Long?,
val name: String?,
val mime: String?,
val hash: String?,
val url: String
val targetObjectId: Id,
val url: String,
val fileExt: String? = null
) : Media(), Searchable {
override fun getViewType() = HOLDER_FILE
}
@ -1016,7 +1020,7 @@ sealed class BlockView : ViewType {
val size: Long?,
val name: String?,
val mime: String?,
val hash: String?,
val targetObjectId: Id,
val url: String
) : Media() {
override fun getViewType() = HOLDER_VIDEO
@ -1067,8 +1071,8 @@ sealed class BlockView : ViewType {
val size: Long?,
val name: String?,
val mime: String?,
val hash: String?,
val url: String
val targetObjectId: Id,
val url: String,
) : Media() {
override fun getViewType() = HOLDER_PICTURE
}

View file

@ -37,7 +37,6 @@ import com.anytypeio.anytype.presentation.relations.BasicObjectCoverWrapper
import com.anytypeio.anytype.presentation.relations.BlockFieldsCoverWrapper
import com.anytypeio.anytype.presentation.relations.ObjectRelationView
import com.anytypeio.anytype.presentation.relations.getCover
import com.anytypeio.anytype.presentation.relations.isSystemKey
import com.anytypeio.anytype.presentation.relations.linksFeaturedRelation
import com.anytypeio.anytype.presentation.relations.objectTypeRelation
import com.anytypeio.anytype.presentation.relations.view
@ -635,7 +634,8 @@ class DefaultBlockViewRenderer @Inject constructor(
indent = indent,
selection = selection,
isPreviousBlockMedia = isPreviousBlockMedia,
schema = blockDecorationScheme
schema = blockDecorationScheme,
details = details
)
)
isPreviousBlockMedia = true
@ -1265,8 +1265,8 @@ class DefaultBlockViewRenderer @Inject constructor(
url = obj.source.orEmpty(),
title = obj.name,
description = obj.description,
imageUrl = obj.picture?.ifEmpty { null }?.let { urlBuilder.image(it) },
faviconUrl = obj.iconImage?.ifEmpty { null }?.let { urlBuilder.image(it) },
imageUrl = obj.picture?.takeIf { it.isNotBlank() }?.let { urlBuilder.image(it) },
faviconUrl = obj.iconImage?.takeIf { it.isNotBlank() }?.let { urlBuilder.image(it) },
indent = indent,
mode = if (mode == EditorMode.Edit) BlockView.Mode.EDIT else BlockView.Mode.READ,
isSelected = checkIfSelected(
@ -1336,10 +1336,11 @@ class DefaultBlockViewRenderer @Inject constructor(
indent: Int,
selection: Set<Id>,
isPreviousBlockMedia: Boolean,
schema: NestedDecorationData
schema: NestedDecorationData,
details: Block.Details
): BlockView = when (content.type) {
Content.File.Type.IMAGE -> content.toPictureView(
id = block.id,
blockId = block.id,
urlBuilder = urlBuilder,
indent = indent,
mode = if (mode == EditorMode.Edit) BlockView.Mode.EDIT else BlockView.Mode.READ,
@ -1350,10 +1351,11 @@ class DefaultBlockViewRenderer @Inject constructor(
),
background = block.parseThemeBackgroundColor(),
isPreviousBlockMedia = isPreviousBlockMedia,
decorations = schema.toBlockViewDecoration(block)
decorations = schema.toBlockViewDecoration(block),
details = details
)
Content.File.Type.FILE -> content.toFileView(
id = block.id,
blockId = block.id,
urlBuilder = urlBuilder,
indent = indent,
mode = if (mode == EditorMode.Edit) BlockView.Mode.EDIT else BlockView.Mode.READ,
@ -1364,10 +1366,11 @@ class DefaultBlockViewRenderer @Inject constructor(
),
background = block.parseThemeBackgroundColor(),
isPrevBlockMedia = isPreviousBlockMedia,
decorations = schema.toBlockViewDecoration(block)
decorations = schema.toBlockViewDecoration(block),
details = details
)
Content.File.Type.VIDEO -> content.toVideoView(
id = block.id,
blockId = block.id,
urlBuilder = urlBuilder,
indent = indent,
mode = if (mode == EditorMode.Edit) BlockView.Mode.EDIT else BlockView.Mode.READ,
@ -1378,10 +1381,11 @@ class DefaultBlockViewRenderer @Inject constructor(
),
background = block.parseThemeBackgroundColor(),
isPrevBlockMedia = isPreviousBlockMedia,
decorations = schema.toBlockViewDecoration(block)
decorations = schema.toBlockViewDecoration(block),
details = details
)
Content.File.Type.AUDIO -> content.toFileView(
id = block.id,
blockId = block.id,
urlBuilder = urlBuilder,
indent = indent,
mode = if (mode == EditorMode.Edit) BlockView.Mode.EDIT else BlockView.Mode.READ,
@ -1392,10 +1396,11 @@ class DefaultBlockViewRenderer @Inject constructor(
),
background = block.parseThemeBackgroundColor(),
isPrevBlockMedia = isPreviousBlockMedia,
decorations = schema.toBlockViewDecoration(block)
decorations = schema.toBlockViewDecoration(block),
details = details
)
Content.File.Type.PDF -> content.toFileView(
id = block.id,
blockId = block.id,
urlBuilder = urlBuilder,
indent = indent,
mode = if (mode == EditorMode.Edit) BlockView.Mode.EDIT else BlockView.Mode.READ,
@ -1406,10 +1411,11 @@ class DefaultBlockViewRenderer @Inject constructor(
),
background = block.parseThemeBackgroundColor(),
isPrevBlockMedia = isPreviousBlockMedia,
decorations = schema.toBlockViewDecoration(block)
decorations = schema.toBlockViewDecoration(block),
details = details
)
Content.File.Type.NONE -> content.toFileView(
id = block.id,
blockId = block.id,
urlBuilder = urlBuilder,
indent = indent,
mode = if (mode == EditorMode.Edit) BlockView.Mode.EDIT else BlockView.Mode.READ,
@ -1420,7 +1426,8 @@ class DefaultBlockViewRenderer @Inject constructor(
),
background = block.parseThemeBackgroundColor(),
isPrevBlockMedia = isPreviousBlockMedia,
decorations = schema.toBlockViewDecoration(block)
decorations = schema.toBlockViewDecoration(block),
details = details
)
else -> throw IllegalStateException("Unexpected file type: ${content.type}")
}
@ -1474,15 +1481,9 @@ class DefaultBlockViewRenderer @Inject constructor(
mode = blockMode,
id = block.id,
text = content.text,
emoji = details.details[root.id]?.iconEmoji?.let { name ->
name.ifEmpty { null }
},
image = details.details[root.id]?.iconImage?.let { name ->
if (name.isNotEmpty())
urlBuilder.thumbnail(name)
else
null
},
emoji = details.details[root.id]?.iconEmoji?.takeIf { it.isNotBlank() },
image = details.details[root.id]?.iconImage?.takeIf { it.isNotBlank() }
?.let { urlBuilder.thumbnail(it) },
isFocused = resolveIsFocused(focus, block),
cursor = cursor,
coverColor = coverContainer.coverColor,
@ -1512,11 +1513,8 @@ class DefaultBlockViewRenderer @Inject constructor(
mode = blockMode,
id = block.id,
text = content.text,
image = details.details[root.id]?.iconImage?.let { name ->
if (name.isNotEmpty())
urlBuilder.thumbnail(name)
else
null
image = details.details[root.id]?.iconImage?.takeIf { it.isNotBlank() }?.let {
urlBuilder.thumbnail(it)
},
spaceGradient = null,
isFocused = resolveIsFocused(focus, block),
@ -1533,11 +1531,9 @@ class DefaultBlockViewRenderer @Inject constructor(
mode = blockMode,
id = block.id,
text = content.text,
emoji = details.details[root.id]?.iconEmoji?.let { name ->
name.ifEmpty { null }
},
emoji = details.details[root.id]?.iconEmoji?.takeIf { it.isNotBlank() },
image = details.details[root.id]?.iconImage?.let { image ->
if (image.isNotEmpty() && layout != ObjectType.Layout.BOOKMARK)
if (image.isNotBlank() && layout != ObjectType.Layout.BOOKMARK)
urlBuilder.thumbnail(image)
else
null
@ -1557,14 +1553,9 @@ class DefaultBlockViewRenderer @Inject constructor(
mode = blockMode,
id = block.id,
text = content.text,
emoji = details.details[root.id]?.iconEmoji?.let { name ->
name.ifEmpty { null }
},
image = details.details[root.id]?.iconImage?.let { name ->
if (name.isNotEmpty())
urlBuilder.thumbnail(name)
else
null
emoji = details.details[root.id]?.iconEmoji?.takeIf { it.isNotBlank() },
image = details.details[root.id]?.iconImage?.takeIf { it.isNotBlank() }?.let {
urlBuilder.thumbnail(it)
},
isFocused = resolveIsFocused(focus, block),
cursor = cursor,

View file

@ -0,0 +1,51 @@
package com.anytypeio.anytype.presentation.extension
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.Url
import com.anytypeio.anytype.domain.misc.UrlBuilder
fun UrlBuilder.getUrlForFileBlock(
fileBlock: Block?,
isOriginalImage: Boolean = false,
isThumbnail: Boolean = false
): Url? {
if (fileBlock == null) return null
val fileContent = fileBlock.content as? Block.Content.File ?: return null
return getUrlForFileContent(fileContent, isOriginalImage, isThumbnail)
}
fun UrlBuilder.getUrlForFileContent(
fileContent: Block.Content.File,
isOriginalImage: Boolean = false,
isThumbnail: Boolean = false
): Url? {
val targetObjectId = fileContent.targetObjectId
if (fileContent.state != Block.Content.File.State.DONE || targetObjectId.isNullOrBlank()) {
return null
}
return getUrlBasedOnType(fileContent.type, targetObjectId, isOriginalImage, isThumbnail)
}
private fun UrlBuilder.getUrlBasedOnType(
fileType: Block.Content.File.Type?,
targetObjectId: String,
isOriginalImage: Boolean,
isThumbnail: Boolean
): Url? {
return when (fileType) {
Block.Content.File.Type.IMAGE -> {
when {
isOriginalImage -> original(targetObjectId)
isThumbnail -> thumbnail(targetObjectId)
else -> image(targetObjectId)
}
}
Block.Content.File.Type.VIDEO -> video(targetObjectId)
Block.Content.File.Type.FILE,
Block.Content.File.Type.PDF,
Block.Content.File.Type.AUDIO -> file(targetObjectId)
Block.Content.File.Type.NONE, null -> null
}
}

View file

@ -17,6 +17,8 @@ import com.anytypeio.anytype.presentation.editor.editor.mention.createMentionMar
import com.anytypeio.anytype.presentation.editor.editor.model.Alignment
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
import com.anytypeio.anytype.presentation.editor.editor.model.UiBlock
import com.anytypeio.anytype.presentation.extension.getUrlForFileBlock
import com.anytypeio.anytype.presentation.extension.getUrlForFileContent
import com.anytypeio.anytype.presentation.objects.ObjectLayoutView
import com.anytypeio.anytype.presentation.objects.ObjectTypeView
import com.anytypeio.anytype.presentation.sets.buildGridRow
@ -28,17 +30,18 @@ import com.anytypeio.anytype.presentation.templates.TemplateObjectTypeView
import timber.log.Timber
fun Block.Content.File.toPictureView(
id: String,
blockId: String,
urlBuilder: UrlBuilder,
indent: Int,
mode: BlockView.Mode,
isSelected: Boolean = false,
background: ThemeColor,
isPreviousBlockMedia: Boolean,
decorations: List<BlockView.Decoration>
decorations: List<BlockView.Decoration>,
details: Block.Details = Block.Details()
): BlockView = when (state) {
Block.Content.File.State.EMPTY -> BlockView.MediaPlaceholder.Picture(
id = id,
id = blockId,
indent = indent,
mode = mode,
isSelected = isSelected,
@ -47,49 +50,70 @@ fun Block.Content.File.toPictureView(
decorations = decorations
)
Block.Content.File.State.UPLOADING -> BlockView.Upload.Picture(
id = id,
indent = indent,
mode = mode,
isSelected = isSelected,
background = background,
decorations = decorations
)
Block.Content.File.State.DONE -> BlockView.Media.Picture(
id = id,
size = size,
name = name,
mime = mime,
hash = hash,
url = urlBuilder.image(hash),
id = blockId,
indent = indent,
mode = mode,
isSelected = isSelected,
background = background,
decorations = decorations
)
Block.Content.File.State.DONE -> {
val url = urlBuilder.getUrlForFileContent(this)
val targetId = this.targetObjectId
val struct = details.details[targetId]?.map
if (url != null && targetId != null && !struct.isNullOrEmpty()) {
val targetObject = ObjectWrapper.File(struct)
BlockView.Media.Picture(
id = blockId,
targetObjectId = targetId,
url = url,
indent = indent,
mode = mode,
isSelected = isSelected,
background = background,
decorations = decorations,
size = targetObject.sizeInBytes?.toLong(),
name = targetObject.name,
mime = targetObject.fileMimeType
)
} else {
Timber.w("Could not build picture view for block $blockId")
BlockView.Error.Picture(
id = blockId,
indent = indent,
mode = mode,
isSelected = isSelected,
background = background,
decorations = decorations
)
}
}
Block.Content.File.State.ERROR -> BlockView.Error.Picture(
id = id,
id = blockId,
indent = indent,
mode = mode,
isSelected = isSelected,
background = background,
decorations = decorations
decorations = decorations,
name = name
)
else -> throw IllegalStateException("Unexpected state: $state")
}
fun Block.Content.File.toVideoView(
id: String,
blockId: Id,
urlBuilder: UrlBuilder,
indent: Int,
mode: BlockView.Mode,
isSelected: Boolean = false,
background: ThemeColor,
isPrevBlockMedia: Boolean,
decorations: List<BlockView.Decoration>
decorations: List<BlockView.Decoration>,
details: Block.Details = Block.Details()
): BlockView = when (state) {
Block.Content.File.State.EMPTY -> BlockView.MediaPlaceholder.Video(
id = id,
id = blockId,
indent = indent,
mode = mode,
isSelected = isSelected,
@ -98,49 +122,70 @@ fun Block.Content.File.toVideoView(
decorations = decorations
)
Block.Content.File.State.UPLOADING -> BlockView.Upload.Video(
id = id,
indent = indent,
mode = mode,
isSelected = isSelected,
background = background,
decorations = decorations
)
Block.Content.File.State.DONE -> BlockView.Media.Video(
id = id,
size = size,
name = name,
mime = mime,
hash = hash,
url = urlBuilder.video(hash),
id = blockId,
indent = indent,
mode = mode,
isSelected = isSelected,
background = background,
decorations = decorations
)
Block.Content.File.State.DONE -> {
val url = urlBuilder.getUrlForFileContent(this)
val targetId = this.targetObjectId
val struct = details.details[targetId]?.map
if (url != null && targetId != null && !struct.isNullOrEmpty()) {
val targetObject = ObjectWrapper.File(struct)
BlockView.Media.Video(
id = blockId,
targetObjectId = targetId,
url = url,
indent = indent,
mode = mode,
isSelected = isSelected,
background = background,
decorations = decorations,
size = targetObject.sizeInBytes?.toLong(),
name = targetObject.name,
mime = targetObject.fileMimeType
)
} else {
Timber.w("Could not build video view for block $blockId")
BlockView.Error.Video(
id = blockId,
indent = indent,
mode = mode,
isSelected = isSelected,
background = background,
decorations = decorations
)
}
}
Block.Content.File.State.ERROR -> BlockView.Error.Video(
id = id,
id = blockId,
indent = indent,
mode = mode,
isSelected = isSelected,
background = background,
decorations
decorations = decorations,
name = name
)
else -> throw IllegalStateException("Unexpected state: $state")
}
fun Block.Content.File.toFileView(
id: String,
blockId: String,
urlBuilder: UrlBuilder,
indent: Int,
mode: BlockView.Mode,
isSelected: Boolean = false,
background: ThemeColor,
isPrevBlockMedia: Boolean,
decorations: List<BlockView.Decoration>
decorations: List<BlockView.Decoration>,
details: Block.Details = Block.Details()
): BlockView = when (state) {
Block.Content.File.State.EMPTY -> BlockView.MediaPlaceholder.File(
id = id,
id = blockId,
indent = indent,
mode = mode,
isSelected = isSelected,
@ -149,33 +194,54 @@ fun Block.Content.File.toFileView(
decorations = decorations
)
Block.Content.File.State.UPLOADING -> BlockView.Upload.File(
id = id,
indent = indent,
mode = mode,
isSelected = isSelected,
background = background,
decorations = decorations
)
Block.Content.File.State.DONE -> BlockView.Media.File(
id = id,
size = size,
name = name,
mime = mime,
hash = hash,
url = urlBuilder.video(hash),
id = blockId,
indent = indent,
mode = mode,
isSelected = isSelected,
background = background,
decorations = decorations
)
Block.Content.File.State.DONE -> {
val url = urlBuilder.getUrlForFileContent(this)
val targetId = this.targetObjectId
val struct = details.details[targetId]?.map
if (url != null && targetId != null && !struct.isNullOrEmpty()) {
val targetObject = ObjectWrapper.File(struct)
BlockView.Media.File(
id = blockId,
targetObjectId = targetId,
url = url,
indent = indent,
mode = mode,
isSelected = isSelected,
background = background,
decorations = decorations,
size = targetObject.sizeInBytes?.toLong(),
name = targetObject.name,
mime = targetObject.fileMimeType,
fileExt = targetObject.fileExt
)
} else {
Timber.w("Could not build file view for block $blockId")
BlockView.Error.File(
id = blockId,
indent = indent,
mode = mode,
isSelected = isSelected,
background = background,
decorations = decorations
)
}
}
Block.Content.File.State.ERROR -> BlockView.Error.File(
id = id,
id = blockId,
indent = indent,
mode = mode,
isSelected = isSelected,
background = background,
decorations = decorations
decorations = decorations,
name = name
)
else -> throw IllegalStateException("Unexpected state: $state")
}

View file

@ -1,23 +0,0 @@
package com.anytypeio.anytype.presentation.mapper
import com.anytypeio.anytype.core_models.ObjectWrapper
import com.anytypeio.anytype.domain.misc.UrlBuilder
fun ObjectWrapper.Basic.getImagePath(urlBuilder: UrlBuilder): String? {
val image = this.iconImage
return if (image.isNullOrBlank()) {
null
} else {
urlBuilder.image(iconImage)
}
}
fun ObjectWrapper.Basic.getEmojiPath(): String? {
val emoji = this.iconEmoji
return if (emoji.isNullOrBlank()) {
null
} else {
emoji
}
}

View file

@ -152,7 +152,7 @@ class ObjectMenuViewModel(
override fun onDiagnosticsClicked(ctx: Id) {
jobs += viewModelScope.launch {
debugTreeShareDownloader.stream(
MiddlewareShareDownloader.Params(hash = ctx, name = "$ctx.zip")
MiddlewareShareDownloader.Params(objectId = ctx, name = "$ctx.zip")
).collect { result ->
result.fold(
onSuccess = { success ->

View file

@ -339,7 +339,7 @@ abstract class ObjectMenuViewModelBase(
fun onDiagnosticsGoroutinesClicked(ctx: Id) {
jobs += viewModelScope.launch {
debugGoroutinesShareDownloader.stream(
MiddlewareShareDownloader.Params(hash = ctx, name = "goroutines")
MiddlewareShareDownloader.Params(objectId = ctx, name = "goroutines")
).collect { result ->
result.fold(
onSuccess = { success -> commands.emit(Command.ShareDebugGoroutines(success.path)) },

View file

@ -21,15 +21,18 @@ fun CoverWrapper.getCover(
when (type) {
CoverType.UPLOADED_IMAGE,
CoverType.UNSPLASH_IMAGE -> {
coverImage = coverId?.let { id ->
urlBuilder.image(id)
val targetObjectId = coverId
coverImage = if (!targetObjectId.isNullOrBlank()) {
urlBuilder.image(targetObjectId)
} else {
null
}
}
CoverType.BUNDLED_IMAGE -> {
val hash = coverId?.let { id ->
coverImageHashProvider.provide(id)
}
if (hash != null) coverImage = urlBuilder.image(hash)
if (!hash.isNullOrBlank()) coverImage = urlBuilder.image(hash)
}
CoverType.COLOR -> {
coverColor = coverId?.let { id ->

View file

@ -209,9 +209,7 @@ fun title(
id = title.id,
text = wrapper.name.orEmpty(),
emoji = wrapper.iconEmoji.orNull(),
image = wrapper.iconImage.orNull()?.let { hash ->
urlBuilder.thumbnail(hash = hash)
},
image = wrapper.iconImage?.takeIf { it.isNotBlank() }?.let { urlBuilder.thumbnail(it) },
coverImage = coverContainer.coverImage,
coverColor = coverContainer.coverColor,
coverGradient = coverContainer.coverGradient

View file

@ -235,6 +235,7 @@ class EditDataViewViewerViewModel(
Block.Content.DataView.Viewer.Type.GALLERY -> ViewState.Gallery
Block.Content.DataView.Viewer.Type.BOARD -> ViewState.Kanban
Block.Content.DataView.Viewer.Type.CALENDAR -> ViewState.Calendar
Block.Content.DataView.Viewer.Type.GRAPH -> ViewState.Graph
}
}
@ -281,6 +282,7 @@ class EditDataViewViewerViewModel(
object List : ViewState()
object Kanban : ViewState()
object Calendar: ViewState()
object Graph : ViewState()
data class Error(val msg: String) : ViewState()
}
}

View file

@ -138,7 +138,7 @@ private suspend fun ObjectWrapper.Basic.mapToCoverItem(
val preview = details[id]
preview != null && preview.type.contains(IMAGE)
}
if (previewId != null) {
if (!previewId.isNullOrBlank()) {
coverImage = urlBuilder.image(previewId)
}
}

View file

@ -457,7 +457,7 @@ fun ObjectWrapper.Basic.toTemplateView(
name = name.orEmpty(),
targetTypeId = TypeId(targetObjectType.orEmpty()),
emoji = if (!iconEmoji.isNullOrBlank()) iconEmoji else null,
image = if (!iconImage.isNullOrBlank()) urlBuilder.thumbnail(iconImage!!) else null,
image = iconImage?.takeIf { it.isNotBlank() }?.let { urlBuilder.thumbnail(it) },
layout = layout ?: ObjectType.Layout.BASIC,
coverColor = coverContainer?.coverColor,
coverImage = coverContainer?.coverImage,

View file

@ -207,7 +207,7 @@ suspend fun List<ColumnView>.buildGridRow(
type = type,
name = name,
emoji = emoji,
image = image?.let { if (it.isEmpty()) null else builder.thumbnail(it) },
image = image?.takeIf { it.isNotBlank() }?.let { builder.thumbnail(it) },
cells = cells,
layout = layout,
isChecked = done,

View file

@ -2,6 +2,7 @@ package com.anytypeio.anytype.presentation.util.downloader
import android.content.Context
import com.anytypeio.anytype.core_models.Command
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
import com.anytypeio.anytype.domain.block.repo.BlockRepository
@ -12,7 +13,7 @@ class DocumentFileShareDownloader(
dispatchers: AppCoroutineDispatchers
) : MiddlewareShareDownloader(context, uriFileProvider, dispatchers) {
override suspend fun downloadFile(hash: String, path: String) = repo.downloadFile(
Command.DownloadFile(hash = hash, path = path)
override suspend fun downloadFile(objectId: Id, path: String) = repo.downloadFile(
Command.DownloadFile(objectId = objectId, path = path)
)
}

View file

@ -2,7 +2,7 @@ package com.anytypeio.anytype.presentation.util.downloader
import android.content.Context
import android.net.Uri
import com.anytypeio.anytype.core_models.Hash
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
import com.anytypeio.anytype.domain.base.ResultInteractor
import java.io.File
@ -18,7 +18,7 @@ abstract class MiddlewareShareDownloader(
) : ResultInteractor<MiddlewareShareDownloader.Params, MiddlewareShareDownloader.Response>(dispatchers.io) {
data class Params(
val hash: Hash,
val objectId: Id,
val name: String
)
@ -28,20 +28,20 @@ abstract class MiddlewareShareDownloader(
)
/**
* @param hash is a some middleware id
* @param objectId id of Object File
* @param path is local storage path to the file created
* @return path to the file in the local storage
* */
abstract suspend fun downloadFile(hash: String, path: String): String
abstract suspend fun downloadFile(objectId: Id, path: String): String
override suspend fun doWork(params: Params): Response {
val cacheDir = context.cacheDir
require(cacheDir != null) { "Impossible to cache files!" }
val downloadFolder = File("${cacheDir.path}/${params.hash}").apply { mkdirs() }
val downloadFolder = File("${cacheDir.path}/${params.objectId}").apply { mkdirs() }
val resultFilePath = "${cacheDir.path}/${params.hash}/${params.name}"
val resultFilePath = "${cacheDir.path}/${params.objectId}/${params.name}"
val resultFile = File(resultFilePath)
if (!resultFile.exists()) {
@ -50,7 +50,12 @@ abstract class MiddlewareShareDownloader(
if (tempDir.exists()) tempDir.deleteRecursively()
tempDir.mkdirs()
val tempResult = File(downloadFile(params.hash, tempFileFolderPath))
val tempResult = File(
downloadFile(
objectId = params.objectId,
path = tempFileFolderPath
)
)
tempResult.renameTo(resultFile)
}

View file

@ -8,6 +8,7 @@ import com.anytypeio.anytype.analytics.base.Analytics
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.Event
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.NetworkModeConfig
import com.anytypeio.anytype.core_models.Payload
import com.anytypeio.anytype.core_models.Position
import com.anytypeio.anytype.core_models.Relation
@ -398,6 +399,7 @@ open class EditorViewModelTest {
fun setup() {
MockitoAnnotations.openMocks(this)
builder = UrlBuilder(gateway)
stubNetworkMode()
}
@Test
@ -406,6 +408,12 @@ open class EditorViewModelTest {
verifyNoInteractions(interceptEvents)
}
fun stubNetworkMode() {
getNetworkMode.stub {
onBlocking { run(Unit) } doReturn NetworkModeConfig()
}
}
@Test
fun `should start opening page when requested`() {
val param = OpenPage.Params(root, true)
@ -2568,7 +2576,7 @@ open class EditorViewModelTest {
params = eq(
MiddlewareShareDownloader.Params(
name = file.content<Block.Content.File>().name.orEmpty(),
hash = file.content<Block.Content.File>().hash.orEmpty(),
objectId = file.content<Block.Content.File>().targetObjectId.orEmpty(),
)
)
)
@ -2618,7 +2626,7 @@ open class EditorViewModelTest {
// TESTING
vm.startDownloadingFile(id = file.id)
vm.startDownloadingFile(blockId = file.id)
runBlockingTest {
verify(downloadFile, times(1)).invoke(
@ -2626,7 +2634,7 @@ open class EditorViewModelTest {
DownloadFile.Params(
name = file.content<Block.Content.File>().name.orEmpty(),
url = builder.file(
hash = file.content<Block.Content.File>().hash
path = file.content<Block.Content.File>().targetObjectId!!
)
)
)
@ -3394,7 +3402,7 @@ open class EditorViewModelTest {
id = MockDataFactory.randomUuid(),
fields = Block.Fields(emptyMap()),
content = Block.Content.File(
hash = MockDataFactory.randomString(),
targetObjectId = MockDataFactory.randomString(),
type = Block.Content.File.Type.IMAGE,
state = Block.Content.File.State.ERROR
),
@ -3469,7 +3477,7 @@ open class EditorViewModelTest {
id = MockDataFactory.randomUuid(),
fields = Block.Fields(emptyMap()),
content = Block.Content.File(
hash = MockDataFactory.randomString(),
targetObjectId = MockDataFactory.randomString(),
type = Block.Content.File.Type.VIDEO,
state = Block.Content.File.State.ERROR
),
@ -3587,7 +3595,8 @@ open class EditorViewModelTest {
background = ThemeColor.DEFAULT,
style = BlockView.Decoration.Style.Card
)
)
),
name = file.content<Block.Content.File>().name.orEmpty()
)
)

View file

@ -37,7 +37,7 @@ class EditorErrorMessageTest : EditorPresentationTestSetup() {
val file = Block(
id = MockDataFactory.randomUuid(),
content = Block.Content.File(
hash = MockDataFactory.randomUuid(),
targetObjectId = MockDataFactory.randomUuid(),
type = Block.Content.File.Type.FILE,
state = Block.Content.File.State.DONE
),
@ -69,7 +69,7 @@ class EditorErrorMessageTest : EditorPresentationTestSetup() {
// Launching operation that triggers a toast
vm.startDownloadingFile(id = file.id)
vm.startDownloadingFile(blockId = file.id)
val subscription2 = launch { vm.toasts.collect { consumed.add(it) } }

View file

@ -9,7 +9,6 @@ import com.anytypeio.anytype.core_models.StubTitle
import com.anytypeio.anytype.core_models.ThemeColor
import com.anytypeio.anytype.core_models.ext.content
import com.anytypeio.anytype.core_utils.common.EventWrapper
import com.anytypeio.anytype.presentation.BuildConfig
import com.anytypeio.anytype.presentation.MockBlockFactory
import com.anytypeio.anytype.presentation.editor.editor.listener.ListenerType
import com.anytypeio.anytype.presentation.editor.editor.model.BlockView
@ -21,6 +20,7 @@ import com.anytypeio.anytype.presentation.util.TXT
import com.anytypeio.anytype.test_utils.MockDataFactory
import com.jraska.livedata.test
import kotlin.test.assertEquals
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@ -48,6 +48,7 @@ class EditorLockPageTest : EditorPresentationTestSetup() {
@Before
fun setup() {
MockitoAnnotations.openMocks(this)
stubGetNetworkMode()
}
@Test
@ -543,15 +544,18 @@ class EditorLockPageTest : EditorPresentationTestSetup() {
}
@Test
fun `should open file when clicking on file when page is locked`() {
fun `should open file when clicking on file when page is locked`() = runTest {
// SETUP
val fileBlockId = MockDataFactory.randomUuid()
val targetObjectId = MockDataFactory.randomUuid()
val file = Block(
id = MockDataFactory.randomUuid(),
id = fileBlockId,
fields = Block.Fields(emptyMap()),
content = Block.Content.File(
hash = MockDataFactory.randomString(),
targetObjectId = targetObjectId,
type = Block.Content.File.Type.FILE,
state = Block.Content.File.State.DONE
),
@ -565,17 +569,33 @@ class EditorLockPageTest : EditorPresentationTestSetup() {
mapOf(Block.Fields.IS_LOCKED_KEY to true)
),
content = Block.Content.Smart,
children = listOf(header.id, file.id)
children = listOf(header.id, fileBlockId)
),
header,
title,
file
)
val mimeType = "application/pdf"
val fileName = "file.pdf"
val fileSize = 1000.0
stubInterceptEvents()
stubInterceptThreadStatus()
stubOpenDocument(
document = page
document = page,
details = Block.Details(
mapOf(
targetObjectId to Block.Fields(
mapOf(
Relations.ID to targetObjectId,
Relations.FILE_MIME_TYPE to mimeType,
Relations.NAME to fileName,
Relations.SIZE_IN_BYTES to fileSize
)
)
)
)
)
val vm = buildViewModel()
@ -593,13 +613,13 @@ class EditorLockPageTest : EditorPresentationTestSetup() {
mode = BlockView.Mode.READ
),
BlockView.Media.File(
id = file.id,
id = fileBlockId,
mode = BlockView.Mode.READ,
hash = file.content<Block.Content.File>().hash,
mime = null,
name = null,
size = null,
url = builder.file(file.content<Block.Content.File>().hash),
targetObjectId = targetObjectId,
mime = mimeType,
name = fileName,
size = fileSize.toLong(),
url = builder.file(targetObjectId),
decorations = listOf(
BlockView.Decoration(
background = ThemeColor.DEFAULT,
@ -618,15 +638,14 @@ class EditorLockPageTest : EditorPresentationTestSetup() {
val testObserver = vm.commands.test()
vm.onClickListener(ListenerType.File.View(file.id))
vm.onClickListener(ListenerType.File.View(fileBlockId))
// checking open-by-default-app command
testObserver.assertValue { value ->
value is EventWrapper && value.peekContent() == Command.OpenFileByDefaultApp(
id = file.id,
uri = builder.file(file.content<Block.Content.File>().hash),
mime = file.content<Block.Content.File>().mime.orEmpty()
id = fileBlockId,
uri = builder.file(targetObjectId)
)
}
}
@ -636,11 +655,17 @@ class EditorLockPageTest : EditorPresentationTestSetup() {
// SETUP
val fileBlockId = MockDataFactory.randomUuid()
val targetObjectId = MockDataFactory.randomUuid()
val mimeType = "*/png"
val fileName = "image.png"
val fileSize = 1000.0
val picture = Block(
id = MockDataFactory.randomUuid(),
id = fileBlockId,
fields = Block.Fields(emptyMap()),
content = Block.Content.File(
hash = MockDataFactory.randomString(),
targetObjectId = targetObjectId,
type = Block.Content.File.Type.IMAGE,
state = Block.Content.File.State.DONE
),
@ -664,7 +689,19 @@ class EditorLockPageTest : EditorPresentationTestSetup() {
stubInterceptEvents()
stubInterceptThreadStatus()
stubOpenDocument(
document = page
document = page,
details = Block.Details(
mapOf(
targetObjectId to Block.Fields(
mapOf(
Relations.ID to targetObjectId,
Relations.FILE_MIME_TYPE to mimeType,
Relations.NAME to fileName,
Relations.SIZE_IN_BYTES to fileSize
)
)
)
)
)
val vm = buildViewModel()
@ -684,11 +721,11 @@ class EditorLockPageTest : EditorPresentationTestSetup() {
BlockView.Media.Picture(
id = picture.id,
mode = BlockView.Mode.READ,
hash = picture.content<Block.Content.File>().hash,
mime = null,
name = null,
size = null,
url = builder.image(picture.content<Block.Content.File>().hash),
targetObjectId = targetObjectId,
mime = mimeType,
name = fileName,
size = fileSize.toLong(),
url = builder.image(targetObjectId),
indent = 0,
decorations = listOf(
BlockView.Decoration(
@ -715,7 +752,7 @@ class EditorLockPageTest : EditorPresentationTestSetup() {
testObserver.assertValue { value ->
value is EventWrapper && value.peekContent() == Command.OpenFullScreenImage(
target = picture.id,
url = builder.original(picture.content<Block.Content.File>().hash)
url = builder.original(targetObjectId)
)
}
}

View file

@ -1,6 +1,9 @@
package com.anytypeio.anytype.presentation.mapper
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.ObjectWrapper
import com.anytypeio.anytype.core_models.Relations
import com.anytypeio.anytype.domain.config.Gateway
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.presentation.editor.editor.Markup
@ -20,6 +23,8 @@ class MapperExtensionKtTest {
private val urlBuilder: UrlBuilder get() = UrlBuilder(gateway)
private val targetObjectId : Id = "647tyhfgehf7ru"
@Before
fun before() {
MockitoAnnotations.openMocks(this)
@ -33,18 +38,28 @@ class MapperExtensionKtTest {
val indent = MockDataFactory.randomInt()
val name = "name"
val size = 10000L
val mime = "*/txt"
val hash = "647tyhfgehf7ru"
val state = Block.Content.File.State.DONE
val type = Block.Content.File.Type.FILE
val mode = BlockView.Mode.EDIT
val details = Block.Details(
mapOf(
targetObjectId to Block.Fields(
mapOf(
Relations.NAME to name,
Relations.SIZE_IN_BYTES to 10000.0,
Relations.FILE_MIME_TYPE to mime,
)
)
)
)
val block = Block.Content.File(
name = name,
size = size,
size = 10000L,
mime = mime,
hash = hash,
targetObjectId = targetObjectId,
state = state,
type = type
@ -53,15 +68,15 @@ class MapperExtensionKtTest {
val expected = BlockView.Media.File(
id = id,
name = name,
size = size,
size = 10000L,
mime = mime,
hash = hash,
targetObjectId = targetObjectId,
mode = BlockView.Mode.EDIT,
url = urlBuilder.video(hash),
url = urlBuilder.video(targetObjectId),
indent = indent,
decorations = emptyList()
)
val actual = block.toFileView(id, urlBuilder, indent, mode, false, ThemeColor.DEFAULT, false, emptyList())
val actual = block.toFileView(id, urlBuilder, indent, mode, false, ThemeColor.DEFAULT, false, emptyList(), details)
assertEquals(expected, actual)
}
@ -79,7 +94,8 @@ class MapperExtensionKtTest {
val block = Block.Content.File(
state = state,
type = type
type = type,
targetObjectId = targetObjectId
)
val expected =
@ -102,8 +118,8 @@ class MapperExtensionKtTest {
val block = Block.Content.File(
state = state,
type = type
type = type,
targetObjectId = targetObjectId
)
val expected = BlockView.Error.File(id = id, indent = indent, decorations = emptyList())
@ -125,7 +141,8 @@ class MapperExtensionKtTest {
val block = Block.Content.File(
state = state,
type = type
type = type,
targetObjectId = targetObjectId
)
val expected = BlockView.Upload.File(id = id, indent = indent)
@ -142,7 +159,7 @@ class MapperExtensionKtTest {
val indent = MockDataFactory.randomInt()
val name = "name"
val size = 10000L
val size = 10000.0
val mime = "image/jpeg"
val hash = "647tyhfgehf7ru"
val state = Block.Content.File.State.DONE
@ -150,27 +167,36 @@ class MapperExtensionKtTest {
val mode = BlockView.Mode.EDIT
val block = Block.Content.File(
name = name,
size = size,
mime = mime,
hash = hash,
targetObjectId = targetObjectId,
state = state,
type = type
)
val details = Block.Details(
mapOf(
targetObjectId to Block.Fields(
mapOf(
Relations.NAME to name,
Relations.SIZE_IN_BYTES to size,
Relations.FILE_MIME_TYPE to mime,
)
)
)
)
val expected = BlockView.Media.Picture(
id = id,
name = name,
size = size,
size = size.toLong(),
mime = mime,
hash = hash,
targetObjectId = targetObjectId,
url = urlBuilder.image(hash),
indent = indent,
decorations = emptyList()
)
val actual = block.toPictureView(id, urlBuilder, indent, mode, false, ThemeColor.DEFAULT, false, emptyList())
val actual = block.toPictureView(id, urlBuilder, indent, mode, false, ThemeColor.DEFAULT, false, emptyList(), details)
assertEquals(expected, actual)
}
@ -188,8 +214,8 @@ class MapperExtensionKtTest {
val block = Block.Content.File(
state = state,
type = type
type = type,
targetObjectId = targetObjectId
)
val expected = BlockView.MediaPlaceholder.Picture(
@ -215,8 +241,8 @@ class MapperExtensionKtTest {
val block = Block.Content.File(
state = state,
type = type
type = type,
targetObjectId = targetObjectId
)
val expected = BlockView.Error.Picture(
@ -243,7 +269,8 @@ class MapperExtensionKtTest {
val block = Block.Content.File(
state = state,
type = type
type = type,
targetObjectId = targetObjectId
)
val expected = BlockView.Upload.Picture(id = id, indent = indent)
@ -262,38 +289,47 @@ class MapperExtensionKtTest {
val name = "name"
val size = 10000L
val mime = "video/mp4"
val hash = "647tyhfgehf7ru"
val targetObjectId = "647tyhfgehf7ru"
val state = Block.Content.File.State.DONE
val type = Block.Content.File.Type.VIDEO
val mode = BlockView.Mode.EDIT
val block = Block.Content.File(
name = name,
size = size,
mime = mime,
hash = hash,
targetObjectId = targetObjectId,
state = state,
type = type
)
val details = Block.Details(
mapOf(
targetObjectId to Block.Fields(
mapOf(
Relations.NAME to name,
Relations.SIZE_IN_BYTES to 10000.0,
Relations.FILE_MIME_TYPE to mime,
)
)
)
)
val expected = BlockView.Media.Video(
id = id,
name = name,
size = size,
mime = mime,
hash = hash,
url = urlBuilder.video(hash),
targetObjectId = targetObjectId,
url = urlBuilder.video(targetObjectId),
indent = indent,
decorations = emptyList()
)
val actual = block.toVideoView(id, urlBuilder, indent, mode, false, ThemeColor.DEFAULT, false, emptyList())
val actual = block.toVideoView(id, urlBuilder, indent, mode, false, ThemeColor.DEFAULT, false, emptyList(), details)
assertEquals(expected, actual)
}
@Test
fun `should return video block view empty params`() {
fun `should return video block error view when target object id is empty`() {
val id = MockDataFactory.randomUuid()
@ -304,22 +340,13 @@ class MapperExtensionKtTest {
val mode = BlockView.Mode.EDIT
val block = Block.Content.File(
name = null,
size = null,
mime = null,
hash = null,
targetObjectId = null,
state = state,
type = type
)
val expected = BlockView.Media.Video(
val expected = BlockView.Error.Video(
id = id,
name = null,
size = null,
mime = null,
hash = null,
url = urlBuilder.video(null),
indent = indent,
decorations = emptyList()
)
@ -329,6 +356,90 @@ class MapperExtensionKtTest {
assertEquals(expected, actual)
}
@Test
fun `should return image block error view when target object id is empty`() {
val id = MockDataFactory.randomUuid()
val indent = MockDataFactory.randomInt()
val state = Block.Content.File.State.DONE
val type = Block.Content.File.Type.IMAGE
val mode = BlockView.Mode.EDIT
val block = Block.Content.File(
targetObjectId = null,
state = state,
type = type
)
val expected = BlockView.Error.Picture(
id = id,
indent = indent,
decorations = emptyList()
)
val actual = block.toPictureView(id, urlBuilder, indent, mode, false, ThemeColor.DEFAULT, false, emptyList())
assertEquals(expected, actual)
}
@Test
fun `should return file block error view when target object id is empty`() {
val id = MockDataFactory.randomUuid()
val indent = MockDataFactory.randomInt()
val state = Block.Content.File.State.DONE
val type = Block.Content.File.Type.FILE
val mode = BlockView.Mode.EDIT
val block = Block.Content.File(
targetObjectId = null,
state = state,
type = type
)
val expected = BlockView.Error.File(
id = id,
indent = indent,
decorations = emptyList()
)
val actual = block.toFileView(id, urlBuilder, indent, mode, false, ThemeColor.DEFAULT, false, emptyList())
assertEquals(expected, actual)
}
@Test
fun `should return file block error view when target object id is empty for pdf`() {
val id = MockDataFactory.randomUuid()
val indent = MockDataFactory.randomInt()
val state = Block.Content.File.State.DONE
val type = Block.Content.File.Type.PDF
val mode = BlockView.Mode.EDIT
val block = Block.Content.File(
targetObjectId = null,
state = state,
type = type
)
val expected = BlockView.Error.File(
id = id,
indent = indent,
decorations = emptyList()
)
val actual = block.toFileView(id, urlBuilder, indent, mode, false, ThemeColor.DEFAULT, false, emptyList())
assertEquals(expected, actual)
}
@Test
fun `should return placeholder video block view`() {
@ -344,7 +455,7 @@ class MapperExtensionKtTest {
name = null,
size = null,
mime = null,
hash = null,
targetObjectId = null,
state = state,
type = type
)
@ -375,7 +486,7 @@ class MapperExtensionKtTest {
name = null,
size = null,
mime = null,
hash = null,
targetObjectId = null,
state = state,
type = type
)
@ -405,7 +516,7 @@ class MapperExtensionKtTest {
name = null,
size = null,
mime = null,
hash = null,
targetObjectId = null,
state = state,
type = type
)
@ -435,7 +546,7 @@ class MapperExtensionKtTest {
name = null,
size = null,
mime = null,
hash = null,
targetObjectId = null,
state = null,
type = type
)

View file

@ -9,7 +9,6 @@ import org.junit.Test
import org.mockito.Mock
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.doThrow
import org.mockito.kotlin.stub
import kotlin.test.assertEquals
import kotlin.test.assertNull
@ -37,7 +36,7 @@ class ObjectWrapperExtensionsKtTest {
stubUrlBuilder(imageHash)
val result = obj.getImagePath(urlBuilder)
val result = urlBuilder.image(obj.iconImage!!)
val expected = URL + imageHash
@ -47,87 +46,6 @@ class ObjectWrapperExtensionsKtTest {
)
}
@Test
fun `should return null image path from object wrapper when hash is null`() {
val imageHash: String? = null
val obj = ObjectWrapper.Basic(
mapOf("iconImage" to imageHash)
)
stubUrlBuilder(imageHash)
val result = obj.getImagePath(urlBuilder)
assertNull(actual = result)
}
@Test
fun `should return null image path from object wrapper when hash is empty`() {
val imageHash = ""
val obj = ObjectWrapper.Basic(
mapOf("iconImage" to imageHash)
)
stubUrlBuilder(imageHash)
val result = obj.getImagePath(urlBuilder)
assertNull(actual = result)
}
@Test
fun `should return null image path from object wrapper when hash is blank`() {
val imageHash = " "
val obj = ObjectWrapper.Basic(
mapOf("iconImage" to imageHash)
)
stubUrlBuilder(imageHash)
val result = obj.getImagePath(urlBuilder)
assertNull(actual = result)
}
@Test
fun `should return proper emoji from object wrapper`() {
val emojiHash = "ycd79"
val obj = ObjectWrapper.Basic(
mapOf("iconEmoji" to emojiHash)
)
val result = obj.getEmojiPath()
val expected = emojiHash
assertEquals(
expected = expected,
actual = result
)
}
@Test
fun `should return null emoji from object wrapper when emoji is null`() {
val emojiHash: String? = null
val obj = ObjectWrapper.Basic(
mapOf("iconEmoji" to emojiHash)
)
val result = obj.getEmojiPath()
assertNull(result)
}
@Test
fun `should return null emoji from object wrapper when emoji is empty`() {
@ -137,7 +55,7 @@ class ObjectWrapperExtensionsKtTest {
mapOf("iconEmoji" to emojiHash)
)
val result = obj.getEmojiPath()
val result = urlBuilder.image(obj.iconEmoji!!)
assertNull(result)
}
@ -151,7 +69,7 @@ class ObjectWrapperExtensionsKtTest {
mapOf("iconEmoji" to emojiHash)
)
val result = obj.getEmojiPath()
val result = urlBuilder.image(obj.iconEmoji!!)
assertNull(result)
}
@ -226,15 +144,9 @@ class ObjectWrapperExtensionsKtTest {
)
}
fun stubUrlBuilder(hash: String?) {
if (hash == null) {
urlBuilder.stub {
on { image(null) } doThrow RuntimeException("Should not happened")
}
} else {
urlBuilder.stub {
on { image(hash) } doReturn URL + hash
}
fun stubUrlBuilder(targetObjectId: String) {
urlBuilder.stub {
on { image(targetObjectId) } doReturn URL + targetObjectId
}
}
}

View file

@ -59,6 +59,7 @@ message Change {
StoreSliceUpdate storeSliceUpdate = 109;
OriginalCreatedTimestampSet originalCreatedTimestampSet = 110;
SetFileInfo setFileInfo = 111;
}
reserved 102,103,104; // old unsupported relation changes
}
@ -151,4 +152,8 @@ message Change {
message OriginalCreatedTimestampSet {
int64 ts = 1;
}
message SetFileInfo {
model.FileInfo fileInfo = 1;
}
}

View file

@ -210,12 +210,16 @@ message Rpc {
message CreateSession {
message Request {
option (no_auth) = true;
string mnemonic = 1;
oneof auth {
string mnemonic = 1; // cold auth
string appKey = 2; // persistent app key, that can be used to restore session
}
}
message Response {
Error error = 1;
string token = 2;
string appToken = 3; // in case of mnemonic auth, need to be persisted by client
message Error {
Code code = 1;
string description = 2;
@ -640,8 +644,63 @@ message Rpc {
LocalOnly = 1; // disable any-sync network and use only local p2p nodes
CustomConfig = 2; // use config provided in networkConfigFilePath
}
}
message LocalLink {
message NewChallenge {
message Request {
option (no_auth) = true;
string appName = 1; // just for info, not secure to rely on
}
message Response {
Error error = 1;
string challengeId = 2;
message Error {
Code code = 1;
string description = 2;
enum Code {
NULL = 0;
UNKNOWN_ERROR = 1;
BAD_INPUT = 2;
ACCOUNT_IS_NOT_RUNNING = 101;
TOO_MANY_REQUESTS = 102; // protection from overuse
}
}
}
}
message SolveChallenge {
message Request {
option (no_auth) = true;
string challengeId = 1;
string answer = 2;
}
message Response {
Error error = 1;
string sessionToken = 2; // ephemeral token for the session
string appKey = 3; // persistent key, that can be used to restore session via CreateSession
message Error {
Code code = 1;
string description = 2;
enum Code {
NULL = 0;
UNKNOWN_ERROR = 1;
BAD_INPUT = 2;
ACCOUNT_IS_NOT_RUNNING = 101;
INVALID_CHALLENGE_ID = 102;
CHALLENGE_ATTEMPTS_EXCEEDED = 103;
INCORRECT_ANSWER = 104;
}
}
}
}
}
}
message Workspace {
message GetCurrent {
message Request {
@ -1120,6 +1179,33 @@ message Rpc {
}
}
message CreateFromUrl {
message Request {
string spaceId = 1;
string objectTypeUniqueKey = 2;
string url = 3;
google.protobuf.Struct details = 4;
}
message Response {
Error error = 1;
string objectId = 2;
google.protobuf.Struct details = 3;
message Error {
Code code = 1;
string description = 2;
enum Code {
NULL = 0;
UNKNOWN_ERROR = 1;
BAD_INPUT = 2;
// ...
}
}
}
}
message BookmarkFetch {
message Request {
string contextId = 1;
@ -2572,6 +2658,32 @@ message Rpc {
}
}
}
message SpaceOffload {
message Request {
string spaceId = 1;
}
message Response {
Error error = 1;
int32 filesOffloaded = 2;
uint64 bytesOffloaded = 3;
message Error {
Code code = 1;
string description = 2;
enum Code {
NULL = 0;
UNKNOWN_ERROR = 1;
BAD_INPUT = 2;
// ...
NODE_NOT_STARTED = 103;
}
}
}
}
message ListOffload {
message Request {
repeated string onlyIds = 1; // empty means all
@ -2605,12 +2717,13 @@ message Rpc {
anytype.model.Block.Content.File.Type type = 3;
bool disableEncryption = 4; // deprecated, has no affect, GO-1926
anytype.model.Block.Content.File.Style style = 5;
google.protobuf.Struct details = 7; // additional details for file object
}
message Response {
Error error = 1;
string hash = 2;
string objectId = 2;
google.protobuf.Struct details = 3;
message Error {
Code code = 1;
@ -2626,7 +2739,7 @@ message Rpc {
}
message Download {
message Request {
string hash = 1;
string objectId = 1;
string path = 2; // path to save file. Temp directory is used if empty
}
@ -2942,7 +3055,7 @@ message Rpc {
message Response {
Error error = 1;
string hash = 2;
string objectId = 2;
message Error {
Code code = 1;
@ -3658,6 +3771,27 @@ message Rpc {
message Response {
Error error = 1;
message Error {
Code code = 1;
string description = 2;
enum Code {
NULL = 0;
UNKNOWN_ERROR = 1;
BAD_INPUT = 2;
}
}
}
}
message Preview {
message Request {
string html = 1;
}
message Response {
Error error = 1;
repeated anytype.model.Block blocks = 2;
message Error {
Code code = 1;
string description = 2;

View file

@ -20,6 +20,7 @@ message Event {
Account.Details accountDetails = 201;
Account.Config.Update accountConfigUpdate = 202;
Account.Update accountUpdate = 203;
Account.LinkChallenge accountLinkChallenge = 204;
Object.Details.Set objectDetailsSet = 16;
Object.Details.Amend objectDetailsAmend = 50;
@ -125,6 +126,16 @@ message Event {
anytype.model.Account.Config config = 1;
anytype.model.Account.Status status = 2;
}
message LinkChallenge {
message ClientInfo {
string processName = 1;
string processPath = 2;
bool signatureVerified = 3;
}
string challenge = 1;
ClientInfo clientInfo = 2;
}
}
message Object {
@ -370,6 +381,7 @@ message Event {
Name name = 6;
Size size = 7;
Style style = 8;
TargetObjectId targetObjectId = 9;
message Name {
string value = 1;
@ -403,6 +415,9 @@ message Event {
int64 value = 1;
}
message TargetObjectId {
string value = 1;
}
}

View file

@ -7,15 +7,15 @@ import "google/protobuf/struct.proto";
message SmartBlockSnapshotBase {
repeated Block blocks = 1;
google.protobuf.Struct details = 2;
google.protobuf.Struct fileKeys = 3;
// deprecated
repeated Relation extraRelations = 4;
google.protobuf.Struct fileKeys = 3 [deprecated=true];
repeated Relation extraRelations = 4 [deprecated=true];
repeated string objectTypes = 5;
google.protobuf.Struct collections = 6;
repeated string removedCollectionKeys = 8;
repeated RelationLink relationLinks = 7;
string key = 9; // only used for pb backup purposes, ignored in other cases
int64 originalCreatedTimestamp = 10; // ignored in import/export in favor of createdDate relation. Used to store original user-side object creation timestamp
FileInfo fileInfo = 11;
}
enum SmartBlockType {
@ -45,6 +45,8 @@ enum SmartBlockType {
Identity = 0x214;
MissingObject = 0x207;
FileObject = 0x215;
}
message Block {
@ -277,6 +279,7 @@ message Block {
string mime = 4;
int64 size = 5;
int64 addedAt = 6;
string targetObjectId = 9;
State state = 7;
Style style = 8;
@ -342,6 +345,7 @@ message Block {
Gallery = 2;
Kanban = 3;
Calendar = 4;
Graph = 5;
}
enum Size {
@ -996,4 +1000,20 @@ message Import {
LIMIT_OF_ROWS_OR_RELATIONS_EXCEEDED = 7;
FILE_LOAD_ERROR = 8;
}
}
}
message FileEncryptionKey {
string path = 1;
string key = 2;
}
message FileInfo {
string fileId = 1;
repeated FileEncryptionKey encryptionKeys = 2;
}
enum ImageKind {
Basic = 0;
Cover = 1;
Icon = 2;
}

View file

@ -92,7 +92,7 @@ fun StubFile(
id: Id = MockDataFactory.randomUuid(),
children: List<Id> = emptyList(),
backgroundColor: String? = null,
hash: String = MockDataFactory.randomString(),
targetObjectId: Id = MockDataFactory.randomString(),
name: String = MockDataFactory.randomString(),
size: Long = MockDataFactory.randomLong(),
type: Block.Content.File.Type? = null,
@ -105,7 +105,7 @@ fun StubFile(
content = Block.Content.File(
size = size,
name = name,
hash = hash,
targetObjectId = targetObjectId,
type = type,
state = state
)

View file

@ -88,7 +88,7 @@ object MockBlockFactory {
id = MockDataFactory.randomUuid(),
fields = Block.Fields(emptyMap()),
content = Block.Content.File(
hash = MockDataFactory.randomUuid(),
targetObjectId = MockDataFactory.randomUuid(),
name = MockDataFactory.randomString(),
state = Block.Content.File.State.DONE,
mime = MockDataFactory.randomString(),