From bdac2ba5c070e0f1d571f6ba852ae56eb7e9efaa Mon Sep 17 00:00:00 2001 From: konstantiniiv Date: Mon, 16 Dec 2024 17:11:57 +0100 Subject: [PATCH 01/23] DROID-2898 open file block --- .../anytype/core_models/SupportedLayouts.kt | 4 ++ .../core_ui/features/editor/BlockAdapter.kt | 23 +++++++++++ .../editor/holders/upload/OpenFile.kt | 41 +++++++++++++++++++ .../main/res/drawable/bg_button_open_file.xml | 8 ++++ .../main/res/layout/item_block_open_file.xml | 23 +++++++++++ localization/src/main/res/values/strings.xml | 1 + .../editor/editor/model/BlockView.kt | 20 +++++++++ .../editor/editor/model/types/Types.kt | 2 + .../editor/selection/TableCellExt.kt | 2 + 9 files changed, 124 insertions(+) create mode 100644 core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/upload/OpenFile.kt create mode 100644 core-ui/src/main/res/drawable/bg_button_open_file.xml create mode 100644 core-ui/src/main/res/layout/item_block_open_file.xml diff --git a/core-models/src/main/java/com/anytypeio/anytype/core_models/SupportedLayouts.kt b/core-models/src/main/java/com/anytypeio/anytype/core_models/SupportedLayouts.kt index 5b305ac115..0fff11d579 100644 --- a/core-models/src/main/java/com/anytypeio/anytype/core_models/SupportedLayouts.kt +++ b/core-models/src/main/java/com/anytypeio/anytype/core_models/SupportedLayouts.kt @@ -75,4 +75,8 @@ object SupportedLayouts { fun isEditorOrFileLayout(layout: ObjectType.Layout?) : Boolean { return editorLayouts.contains(layout) || fileLayouts.contains(layout) } + + fun isFileLayout(layout: ObjectType.Layout?) : Boolean { + return fileLayouts.contains(layout) + } } \ No newline at end of file diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/BlockAdapter.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/BlockAdapter.kt index 4d865876b2..fefc3fb8e1 100644 --- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/BlockAdapter.kt +++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/BlockAdapter.kt @@ -43,6 +43,7 @@ import com.anytypeio.anytype.core_ui.databinding.ItemBlockObjectLinkCardSmallIco import com.anytypeio.anytype.core_ui.databinding.ItemBlockObjectLinkCardSmallIconCoverBinding import com.anytypeio.anytype.core_ui.databinding.ItemBlockObjectLinkDeleteBinding import com.anytypeio.anytype.core_ui.databinding.ItemBlockObjectLinkLoadingBinding +import com.anytypeio.anytype.core_ui.databinding.ItemBlockOpenFileBinding import com.anytypeio.anytype.core_ui.databinding.ItemBlockPictureBinding import com.anytypeio.anytype.core_ui.databinding.ItemBlockRelationCheckboxBinding import com.anytypeio.anytype.core_ui.databinding.ItemBlockRelationDefaultBinding @@ -113,6 +114,8 @@ import com.anytypeio.anytype.core_ui.features.editor.holders.text.Text import com.anytypeio.anytype.core_ui.features.editor.holders.text.Toggle import com.anytypeio.anytype.core_ui.features.editor.holders.upload.BookmarkUpload import com.anytypeio.anytype.core_ui.features.editor.holders.upload.FileUpload +import com.anytypeio.anytype.core_ui.features.editor.holders.upload.OpenFile +import com.anytypeio.anytype.core_ui.features.editor.holders.upload.OpenImage import com.anytypeio.anytype.core_ui.features.editor.holders.upload.PictureUpload import com.anytypeio.anytype.core_ui.features.editor.holders.upload.VideoUpload import com.anytypeio.anytype.core_ui.features.table.holders.TableBlockHolder @@ -163,6 +166,8 @@ import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_OBJECT_LINK_DEFAULT import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_OBJECT_LINK_DELETED import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_OBJECT_LINK_LOADING +import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_OPEN_FILE +import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_OPEN_IMAGE import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_PARAGRAPH import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_PICTURE import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_PICTURE_ERROR @@ -530,6 +535,12 @@ class BlockAdapter( ItemBlockMediaErrorBinding.inflate(inflater, parent, false) ) } + HOLDER_OPEN_FILE -> { + OpenFile(ItemBlockOpenFileBinding.inflate(inflater, parent, false)) + } + HOLDER_OPEN_IMAGE -> { + OpenImage(ItemBlockOpenFileBinding.inflate(inflater, parent, false)) + } HOLDER_VIDEO -> { Video( ItemBlockVideoBinding.inflate(inflater, parent, false) @@ -1622,6 +1633,18 @@ class BlockAdapter( clicked = onClickListener ) } + is OpenFile -> { + holder.bind( + item = blocks[position] as BlockView.OpenFile.File, + click = onClickListener + ) + } + is OpenImage -> { + holder.bind( + item = blocks[position] as BlockView.OpenFile.Image, + click = onClickListener + ) + } } if (holder is Text<*>) { diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/upload/OpenFile.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/upload/OpenFile.kt new file mode 100644 index 0000000000..45c01de913 --- /dev/null +++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/upload/OpenFile.kt @@ -0,0 +1,41 @@ +package com.anytypeio.anytype.core_ui.features.editor.holders.upload + +import android.view.View +import com.anytypeio.anytype.core_ui.databinding.ItemBlockOpenFileBinding +import com.anytypeio.anytype.core_ui.features.editor.BlockViewHolder +import com.anytypeio.anytype.presentation.editor.editor.listener.ListenerType +import com.anytypeio.anytype.presentation.editor.editor.model.BlockView + +class OpenFile( + binding: ItemBlockOpenFileBinding +) : BlockViewHolder(binding.root) { + + private val root: View = itemView + + fun bind(item: BlockView.OpenFile.File, click: (ListenerType) -> Unit) { + root.setOnClickListener { + click( + ListenerType.File.View( + target = item.id + ) + ) + } + } +} + +class OpenImage( + binding: ItemBlockOpenFileBinding +) : BlockViewHolder(binding.root) { + + private val root: View = itemView + + fun bind(item: BlockView.OpenFile.Image, click: (ListenerType) -> Unit) { + root.setOnClickListener { + click( + ListenerType.Picture.View( + target = item.id + ) + ) + } + } +} \ No newline at end of file diff --git a/core-ui/src/main/res/drawable/bg_button_open_file.xml b/core-ui/src/main/res/drawable/bg_button_open_file.xml new file mode 100644 index 0000000000..44dcebe59f --- /dev/null +++ b/core-ui/src/main/res/drawable/bg_button_open_file.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/core-ui/src/main/res/layout/item_block_open_file.xml b/core-ui/src/main/res/layout/item_block_open_file.xml new file mode 100644 index 0000000000..4cc3982984 --- /dev/null +++ b/core-ui/src/main/res/layout/item_block_open_file.xml @@ -0,0 +1,23 @@ + + + + + + \ No newline at end of file diff --git a/localization/src/main/res/values/strings.xml b/localization/src/main/res/values/strings.xml index 22958be181..ebd509c104 100644 --- a/localization/src/main/res/values/strings.xml +++ b/localization/src/main/res/values/strings.xml @@ -525,6 +525,7 @@ Open source Reload object content Open link + Open file Copy email Copy phone number Send email diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/model/BlockView.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/model/BlockView.kt index 0dff6a24f5..54748cee90 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/model/BlockView.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/model/BlockView.kt @@ -46,6 +46,8 @@ import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_OBJECT_TYPE_COLLECTION import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_OBJECT_TYPE_DELETED import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_OBJECT_TYPE_SET +import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_OPEN_FILE +import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_OPEN_IMAGE import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_PARAGRAPH import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_PICTURE import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_PICTURE_ERROR @@ -1268,6 +1270,24 @@ sealed class BlockView : ViewType { override fun getViewType(): Int = HOLDER_FEATURED_RELATION } + sealed class OpenFile( + override val id: String, + val isSelected: Boolean = false + ) : BlockView() { + + data class Image( + override val id: String + ) : OpenFile(id) { + override fun getViewType(): Int = HOLDER_OPEN_IMAGE + } + + data class File( + override val id: String + ) : OpenFile(id) { + override fun getViewType(): Int = HOLDER_OPEN_FILE + } + } + sealed class Relation : BlockView(), Selectable, Indentable, Decoratable { abstract val background: ThemeColor diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/model/types/Types.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/model/types/Types.kt index d34e1ecaa7..9174ea0354 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/model/types/Types.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/model/types/Types.kt @@ -45,6 +45,8 @@ object Types { const val HOLDER_FILE_PLACEHOLDER = 32 const val HOLDER_FILE_UPLOAD = 33 const val HOLDER_FILE_ERROR = 34 + const val HOLDER_OPEN_FILE = 134 + const val HOLDER_OPEN_IMAGE = 135 const val HOLDER_DIVIDER_LINE = 16 const val HOLDER_DIVIDER_DOTS = 38 diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/selection/TableCellExt.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/selection/TableCellExt.kt index 16a70566b7..d120e80bc8 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/selection/TableCellExt.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/selection/TableCellExt.kt @@ -352,6 +352,8 @@ fun List.toggleTableMode( is BlockView.DataView.EmptyData -> view.copy(isSelected = false) is BlockView.DataView.EmptySource -> view.copy(isSelected = false) is BlockView.DataView.Deleted -> view.copy(isSelected = false) + is BlockView.OpenFile.Image -> view + is BlockView.OpenFile.File -> view } } } From b782dfee90b473e114445d8045edd1d009c72f08 Mon Sep 17 00:00:00 2001 From: konstantiniiv Date: Mon, 16 Dec 2024 17:27:42 +0100 Subject: [PATCH 02/23] DROID-2898 file block is hidden --- .../presentation/editor/render/DefaultBlockViewRenderer.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/render/DefaultBlockViewRenderer.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/render/DefaultBlockViewRenderer.kt index b172809f2f..7beb045796 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/render/DefaultBlockViewRenderer.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/render/DefaultBlockViewRenderer.kt @@ -9,6 +9,7 @@ import com.anytypeio.anytype.core_models.ObjectTypeIds.BOOKMARK import com.anytypeio.anytype.core_models.ObjectWrapper import com.anytypeio.anytype.core_models.RelationLink import com.anytypeio.anytype.core_models.Relations +import com.anytypeio.anytype.core_models.SupportedLayouts import com.anytypeio.anytype.core_models.ThemeColor import com.anytypeio.anytype.core_models.ext.parseThemeTextColor import com.anytypeio.anytype.core_models.ext.textColor @@ -622,6 +623,11 @@ class DefaultBlockViewRenderer @Inject constructor( isPreviousBlockMedia = link is BlockView.LinkToObject.Default.Card } is Content.File -> { + val detail = details.details.getOrDefault(root.id, Block.Fields.empty()) + val obj = ObjectWrapper.Basic(detail.map) + if (SupportedLayouts.isFileLayout(obj.layout)) { + return@forEach + } mCounter = 0 val blockDecorationScheme = buildNestedDecorationData( block = block, From 87d99e368b398250e0ae80003426187c272ace69 Mon Sep 17 00:00:00 2001 From: konstantiniiv Date: Fri, 20 Dec 2024 13:10:32 +0100 Subject: [PATCH 03/23] DROID-3185 test --- .../anytypeio/anytype/domain/auth/CreateAccountTest.kt | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/domain/src/test/java/com/anytypeio/anytype/domain/auth/CreateAccountTest.kt b/domain/src/test/java/com/anytypeio/anytype/domain/auth/CreateAccountTest.kt index 5987f3426b..bf909ac8fe 100644 --- a/domain/src/test/java/com/anytypeio/anytype/domain/auth/CreateAccountTest.kt +++ b/domain/src/test/java/com/anytypeio/anytype/domain/auth/CreateAccountTest.kt @@ -20,12 +20,10 @@ import org.junit.Rule import org.junit.Test import org.mockito.Mock import org.mockito.MockitoAnnotations -import org.mockito.kotlin.any import org.mockito.kotlin.doReturn import org.mockito.kotlin.stub import org.mockito.kotlin.times import org.mockito.kotlin.verify -import org.mockito.kotlin.verifyNoMoreInteractions class CreateAccountTest { @@ -97,8 +95,7 @@ class CreateAccountTest { name = name, avatarPath = path, icon = icon, - networkMode = NetworkMode.DEFAULT, - preferYamuxTransport = false + networkMode = NetworkMode.DEFAULT ) onBlocking { createAccount(command) } doReturn setup } @@ -114,8 +111,7 @@ class CreateAccountTest { name = name, avatarPath = path, icon = icon, - networkMode = NetworkMode.DEFAULT, - preferYamuxTransport = false + networkMode = NetworkMode.DEFAULT ) verify(repo, times(1)).getNetworkMode() verify(repo, times(1)).createAccount(command) From 1542d1769e81a5c8f346ca619095142abfaae45d Mon Sep 17 00:00:00 2001 From: konstantiniiv Date: Fri, 20 Dec 2024 13:27:53 +0100 Subject: [PATCH 04/23] DROID-3185 test --- .../editor/DefaultBlockViewRendererTest.kt | 86 ++++++++++++++++++- 1 file changed, 83 insertions(+), 3 deletions(-) diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/DefaultBlockViewRendererTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/DefaultBlockViewRendererTest.kt index 9259d1487f..e2f4ae65eb 100644 --- a/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/DefaultBlockViewRendererTest.kt +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/DefaultBlockViewRendererTest.kt @@ -5754,12 +5754,88 @@ class DefaultBlockViewRendererTest { //endregion @Test - fun `should not render file block in case of file layout`() { + fun `should render file open block in case of file, pdf, audio or video layouts`(){ + val fileTypeExceptImage = listOf( + Block.Content.File.Type.FILE to ObjectType.Layout.FILE, + Block.Content.File.Type.PDF to ObjectType.Layout.PDF, + Block.Content.File.Type.VIDEO to ObjectType.Layout.VIDEO, + Block.Content.File.Type.AUDIO to ObjectType.Layout.AUDIO, + ) + + fileTypeExceptImage.forEach { (fileType, layout) -> + testFileLayout(type = fileType, layout = layout) + } + } + + private fun testFileLayout(type: Block.Content.File.Type, layout: Layout) { val file = StubFile( backgroundColor = null, state = Block.Content.File.State.DONE, - type = Block.Content.File.Type.FILE + type = type + ) + + val paragraph = StubParagraph() + + val page = StubSmartBlock(children = listOf(paragraph.id, file.id)) + + val details = mapOf(page.id to Block.Fields( + mapOf( + Relations.NAME to "file-name", + Relations.LAYOUT to layout.code.toDouble() + ) + )) + + val blocks = listOf(page, paragraph, file) + + val map = blocks.asMap() + + wrapper = BlockViewRenderWrapper( + blocks = map, + renderer = renderer + ) + + val result = runBlocking { + wrapper.render( + root = page, + anchor = page.id, + focus = Editor.Focus.empty(), + indent = 0, + details = Block.Details(details) + ) + } + + val expected = listOf( + BlockView.Text.Paragraph( + indent = 0, + isFocused = false, + id = paragraph.id, + marks = emptyList(), + background = paragraph.parseThemeBackgroundColor(), + text = paragraph.content().text, + decorations = listOf( + BlockView.Decoration( + style = BlockView.Decoration.Style.None, + background = paragraph.parseThemeBackgroundColor() + ) + ), + ), + BlockView.OpenFile.File( + id = file.id, + targetId = (file.content as Block.Content.File).targetObjectId + ) + ) + + assertEquals(expected = expected, actual = result) + } + + @Test + fun `should render image open block in case of image layout`() { + + val file = StubFile( + backgroundColor = null, + state = Block.Content.File.State.DONE, + type = Block.Content.File.Type.IMAGE ) val paragraph = StubParagraph() @@ -5769,7 +5845,7 @@ class DefaultBlockViewRendererTest { val details = mapOf(page.id to Block.Fields( mapOf( Relations.NAME to "file-name", - Relations.LAYOUT to Layout.FILE.code.toDouble() + Relations.LAYOUT to Layout.IMAGE.code.toDouble() ) )) @@ -5806,6 +5882,10 @@ class DefaultBlockViewRendererTest { background = paragraph.parseThemeBackgroundColor() ) ), + ), + BlockView.OpenFile.Image( + id = file.id, + targetId = (file.content as Block.Content.File).targetObjectId ) ) From 131dd17bfbcd6b47eb9f272d648e002441ad8725 Mon Sep 17 00:00:00 2001 From: konstantiniiv Date: Fri, 20 Dec 2024 14:17:31 +0100 Subject: [PATCH 05/23] DROID-3185 fixes --- .../presentation/editor/EditorViewModel.kt | 51 +++++++++----- .../editor/editor/model/BlockView.kt | 8 ++- .../editor/render/DefaultBlockViewRenderer.kt | 68 ++++++++++++------- 3 files changed, 84 insertions(+), 43 deletions(-) diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModel.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModel.kt index e067c1a86b..9a5620000f 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModel.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModel.kt @@ -4303,27 +4303,46 @@ class EditorViewModel( } fun startSharingFile(id: String, onDownloaded: (Uri) -> Unit = {}) { - - Timber.d("startDownloadingFile, id:[$id]") - + Timber.d("startSharingFile, fileBlockId: [$id]") sendToast("Preparing file to share...") val block = blocks.firstOrNull { it.id == id } - val content = block?.content + if (block == null) { + Timber.e("No block found with id $id") + return + } - if (content is Content.File && content.state == Content.File.State.DONE) { - viewModelScope.launch { - orchestrator.proxies.intents.send( - Media.ShareFile( - objectId = content.targetObjectId.orEmpty(), - name = content.name.orEmpty(), - type = content.type, - onDownloaded = onDownloaded - ) + val content = block.content + if (content !is Content.File || content.state != Content.File.State.DONE) { + Timber.e("Block content is not a file or is not in the DONE state; cannot proceed.") + return + } + + val targetObjectId = content.targetObjectId + if (targetObjectId == null) { + Timber.e("Target object ID is null; cannot proceed with file sharing.") + return + } + + val fileObject = orchestrator.stores.details.getAsObject(target = targetObjectId) + if (fileObject == null) { + Timber.e("Object with id $targetObjectId not found.") + return + } + + val fileName = fieldParser.getObjectName(fileObject) + + Timber.d("startDownloadingFile, fileObjectId: [$targetObjectId], fileName: [$fileName]") + + viewModelScope.launch { + orchestrator.proxies.intents.send( + Media.ShareFile( + objectId = targetObjectId, + name = fileName, + type = content.type, + onDownloaded = onDownloaded ) - } - } else { - Timber.e("Block is not File or with wrong state, can't proceed with share!") + ) } } diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/model/BlockView.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/model/BlockView.kt index 54748cee90..8d1720dab2 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/model/BlockView.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/model/BlockView.kt @@ -1275,14 +1275,18 @@ sealed class BlockView : ViewType { val isSelected: Boolean = false ) : BlockView() { + abstract val targetId: Id? + data class Image( - override val id: String + override val id: String, + override val targetId: Id? ) : OpenFile(id) { override fun getViewType(): Int = HOLDER_OPEN_IMAGE } data class File( - override val id: String + override val id: String, + override val targetId: Id? ) : OpenFile(id) { override fun getViewType(): Int = HOLDER_OPEN_FILE } diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/render/DefaultBlockViewRenderer.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/render/DefaultBlockViewRenderer.kt index 7947c3aea5..5fc8d63935 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/render/DefaultBlockViewRenderer.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/render/DefaultBlockViewRenderer.kt @@ -626,33 +626,51 @@ class DefaultBlockViewRenderer @Inject constructor( val detail = details.details.getOrDefault(root.id, Block.Fields.empty()) val obj = ObjectWrapper.Basic(detail.map) if (SupportedLayouts.fileLayouts.contains(obj.layout)) { - return@forEach - } - mCounter = 0 - val blockDecorationScheme = buildNestedDecorationData( - block = block, - parentScheme = parentScheme, - currentDecoration = DecorationData( - style = if ((content.type == Content.File.Type.FILE || content.type == Content.File.Type.PDF) && content.state == Content.File.State.DONE) - DecorationData.Style.None - else - DecorationData.Style.Card, - background = block.parseThemeBackgroundColor() - ) - ) - result.add( - file( - mode = mode, - content = content, + when (obj.layout) { + ObjectType.Layout.IMAGE -> { + result.add( + BlockView.OpenFile.Image( + id = block.id, + targetId = content.targetObjectId + ) + ) + } + else -> { + result.add( + BlockView.OpenFile.File( + id = block.id, + targetId = content.targetObjectId + ) + ) + } + } + } else { + mCounter = 0 + val blockDecorationScheme = buildNestedDecorationData( block = block, - indent = indent, - selection = selection, - isPreviousBlockMedia = isPreviousBlockMedia, - schema = blockDecorationScheme, - details = details + parentScheme = parentScheme, + currentDecoration = DecorationData( + style = if ((content.type == Content.File.Type.FILE || content.type == Content.File.Type.PDF) && content.state == Content.File.State.DONE) + DecorationData.Style.None + else + DecorationData.Style.Card, + background = block.parseThemeBackgroundColor() + ) ) - ) - isPreviousBlockMedia = true + result.add( + file( + mode = mode, + content = content, + block = block, + indent = indent, + selection = selection, + isPreviousBlockMedia = isPreviousBlockMedia, + schema = blockDecorationScheme, + details = details + ) + ) + isPreviousBlockMedia = true + } } is Content.Layout -> { isPreviousBlockMedia = false From f864410015fe75ea0ab836e04aa32d765ec13f29 Mon Sep 17 00:00:00 2001 From: konstantiniiv Date: Fri, 20 Dec 2024 15:28:36 +0100 Subject: [PATCH 06/23] DROID-3185 fixes --- .../login/OnboardingMnemonicLoginDI.kt | 2 ++ .../di/feature/spaces/SpaceSettingsDI.kt | 1 - .../anytypeio/anytype/di/main/DataModule.kt | 5 +++-- .../providers/DefaultUriFileProvider.kt | 19 +++++++++++++------ app/src/main/res/xml/provider_paths.xml | 2 +- 5 files changed, 19 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/anytypeio/anytype/di/feature/onboarding/login/OnboardingMnemonicLoginDI.kt b/app/src/main/java/com/anytypeio/anytype/di/feature/onboarding/login/OnboardingMnemonicLoginDI.kt index 982ce219cb..65b6d247a8 100644 --- a/app/src/main/java/com/anytypeio/anytype/di/feature/onboarding/login/OnboardingMnemonicLoginDI.kt +++ b/app/src/main/java/com/anytypeio/anytype/di/feature/onboarding/login/OnboardingMnemonicLoginDI.kt @@ -14,6 +14,7 @@ import com.anytypeio.anytype.domain.config.ConfigStorage import com.anytypeio.anytype.domain.config.UserSettingsRepository import com.anytypeio.anytype.domain.debugging.DebugAccountSelectTrace import com.anytypeio.anytype.domain.debugging.DebugGoroutines +import com.anytypeio.anytype.domain.debugging.Logger import com.anytypeio.anytype.domain.device.PathProvider import com.anytypeio.anytype.domain.misc.LocaleProvider import com.anytypeio.anytype.domain.multiplayer.UserPermissionProvider @@ -99,4 +100,5 @@ interface OnboardingMnemonicLoginDependencies : ComponentDependencies { fun spaceManager(): SpaceManager fun globalSubscriptionManager(): GlobalSubscriptionManager fun debugAccountSelectTrace(): DebugAccountSelectTrace + fun logger(): Logger } \ No newline at end of file diff --git a/app/src/main/java/com/anytypeio/anytype/di/feature/spaces/SpaceSettingsDI.kt b/app/src/main/java/com/anytypeio/anytype/di/feature/spaces/SpaceSettingsDI.kt index 4b3d17363e..6b80e69322 100644 --- a/app/src/main/java/com/anytypeio/anytype/di/feature/spaces/SpaceSettingsDI.kt +++ b/app/src/main/java/com/anytypeio/anytype/di/feature/spaces/SpaceSettingsDI.kt @@ -25,7 +25,6 @@ import com.anytypeio.anytype.presentation.analytics.AnalyticSpaceHelperDelegate import com.anytypeio.anytype.presentation.spaces.SpaceGradientProvider import com.anytypeio.anytype.presentation.spaces.SpaceSettingsViewModel import com.anytypeio.anytype.presentation.util.downloader.UriFileProvider -import com.anytypeio.anytype.providers.DefaultUriFileProvider import com.anytypeio.anytype.ui.settings.space.SpaceSettingsFragment import dagger.Binds import dagger.BindsInstance diff --git a/app/src/main/java/com/anytypeio/anytype/di/main/DataModule.kt b/app/src/main/java/com/anytypeio/anytype/di/main/DataModule.kt index 19ab47deea..cfd408f3a5 100644 --- a/app/src/main/java/com/anytypeio/anytype/di/main/DataModule.kt +++ b/app/src/main/java/com/anytypeio/anytype/di/main/DataModule.kt @@ -337,8 +337,9 @@ object DataModule { @Provides @Singleton fun provideFileProvider( - context: Context - ): UriFileProvider = DefaultUriFileProvider(context) + context: Context, + logger: Logger + ): UriFileProvider = DefaultUriFileProvider(context, logger) @JvmStatic @Provides diff --git a/app/src/main/java/com/anytypeio/anytype/providers/DefaultUriFileProvider.kt b/app/src/main/java/com/anytypeio/anytype/providers/DefaultUriFileProvider.kt index 23ebf32276..5b0aa4184d 100644 --- a/app/src/main/java/com/anytypeio/anytype/providers/DefaultUriFileProvider.kt +++ b/app/src/main/java/com/anytypeio/anytype/providers/DefaultUriFileProvider.kt @@ -4,19 +4,26 @@ import android.content.Context import android.net.Uri import androidx.core.content.FileProvider import com.anytypeio.anytype.BuildConfig +import com.anytypeio.anytype.domain.debugging.Logger import com.anytypeio.anytype.presentation.util.downloader.UriFileProvider import java.io.File import javax.inject.Inject class DefaultUriFileProvider @Inject constructor( - private val context: Context + private val context: Context, + private val logger: Logger ) : UriFileProvider { - override fun getUriForFile(file: File): Uri = FileProvider.getUriForFile( - context, - BuildConfig.APPLICATION_ID + PROVIDER, - file - ) + override fun getUriForFile(file: File): Uri { + logger.logInfo("DefaultUriFileProvider, start getting uri for file $file") + val contentUri = FileProvider.getUriForFile( + context, + BuildConfig.APPLICATION_ID + PROVIDER, + file + ) + logger.logInfo("DefaultUriFileProvider, got uri $contentUri") + return contentUri + } } private const val PROVIDER = ".provider" \ No newline at end of file diff --git a/app/src/main/res/xml/provider_paths.xml b/app/src/main/res/xml/provider_paths.xml index 948ac11c51..23ca145757 100644 --- a/app/src/main/res/xml/provider_paths.xml +++ b/app/src/main/res/xml/provider_paths.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file From d315c129c3e6d29b5c22e92a2cfdb7a78ddda093 Mon Sep 17 00:00:00 2001 From: konstantiniiv Date: Sat, 21 Dec 2024 13:51:07 +0100 Subject: [PATCH 07/23] DROID-3185 ui --- .../main/res/drawable/bg_title_file_icon.xml | 7 +++ .../main/res/layout/item_block_title_file.xml | 50 +++++++++++++++++++ core-ui/src/main/res/values/dimens.xml | 1 + 3 files changed, 58 insertions(+) create mode 100644 core-ui/src/main/res/drawable/bg_title_file_icon.xml create mode 100644 core-ui/src/main/res/layout/item_block_title_file.xml diff --git a/core-ui/src/main/res/drawable/bg_title_file_icon.xml b/core-ui/src/main/res/drawable/bg_title_file_icon.xml new file mode 100644 index 0000000000..e95165e6d5 --- /dev/null +++ b/core-ui/src/main/res/drawable/bg_title_file_icon.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/core-ui/src/main/res/layout/item_block_title_file.xml b/core-ui/src/main/res/layout/item_block_title_file.xml new file mode 100644 index 0000000000..c5b76daec0 --- /dev/null +++ b/core-ui/src/main/res/layout/item_block_title_file.xml @@ -0,0 +1,50 @@ + + + + + + + + + + \ No newline at end of file diff --git a/core-ui/src/main/res/values/dimens.xml b/core-ui/src/main/res/values/dimens.xml index 5880304c71..f78c571f44 100644 --- a/core-ui/src/main/res/values/dimens.xml +++ b/core-ui/src/main/res/values/dimens.xml @@ -35,6 +35,7 @@ 46dp 48dp 54dp + 64dp 72dp 80dp 51dp From 327242ed43299540d2c72bb075228041e0a173ca Mon Sep 17 00:00:00 2001 From: konstantiniiv Date: Sat, 21 Dec 2024 13:58:58 +0100 Subject: [PATCH 08/23] DROID-3185 new holder --- .../core_ui/features/editor/BlockAdapter.kt | 18 +++++++++ .../features/editor/holders/other/Title.kt | 39 +++++++++++++++++-- .../editor/holders/upload/OpenFile.kt | 2 +- .../core_ui/widgets/ObjectIconWidget.kt | 3 +- .../editor/editor/ext/BlockViewExt.kt | 15 +++++++ .../editor/editor/model/BlockView.kt | 30 +++++++++++++- .../editor/editor/model/types/Types.kt | 1 + .../editor/selection/TableCellExt.kt | 1 + 8 files changed, 101 insertions(+), 8 deletions(-) diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/BlockAdapter.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/BlockAdapter.kt index fefc3fb8e1..fc86538d83 100644 --- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/BlockAdapter.kt +++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/BlockAdapter.kt @@ -56,6 +56,7 @@ import com.anytypeio.anytype.core_ui.databinding.ItemBlockRelationTagBinding import com.anytypeio.anytype.core_ui.databinding.ItemBlockTableBinding import com.anytypeio.anytype.core_ui.databinding.ItemBlockTextBinding import com.anytypeio.anytype.core_ui.databinding.ItemBlockTitleBinding +import com.anytypeio.anytype.core_ui.databinding.ItemBlockTitleFileBinding import com.anytypeio.anytype.core_ui.databinding.ItemBlockTitleProfileBinding import com.anytypeio.anytype.core_ui.databinding.ItemBlockTitleTodoBinding import com.anytypeio.anytype.core_ui.databinding.ItemBlockTocBinding @@ -151,6 +152,7 @@ import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_FILE import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_FILE_ERROR import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_FILE_PLACEHOLDER +import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_FILE_TITLE import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_FILE_UPLOAD import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_HEADER_ONE import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_HEADER_THREE @@ -363,6 +365,11 @@ class BlockAdapter( } } } + HOLDER_FILE_TITLE -> { + Title.File( + ItemBlockTitleFileBinding.inflate(inflater, parent, false) + ) + } HOLDER_TODO_TITLE -> { Title.Todo( ItemBlockTitleTodoBinding.inflate(inflater, parent, false) @@ -1013,6 +1020,12 @@ class BlockAdapter( item = blocks[position] as BlockView.Title.Todo ) } + is Title.File -> { + holder.processPayloads( + payloads = payloads.typeOf(), + item = blocks[position] as BlockView.Title.File + ) + } is Numbered -> { holder.processChangePayload( payloads = payloads.typeOf(), @@ -1372,6 +1385,11 @@ class BlockAdapter( holder.content.clipboardInterceptor = clipboardInterceptor } } + is Title.File -> { + holder.apply { + bind(item = blocks[position] as BlockView.Title.File,) + } + } is Code -> { holder.bind( item = blocks[position] as BlockView.Code, diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt index 405207d125..718cfd9ec3 100644 --- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt +++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt @@ -3,11 +3,10 @@ package com.anytypeio.anytype.core_ui.features.editor.holders.other import android.text.Spannable import android.view.View import android.view.inputmethod.InputMethodManager +import android.widget.FrameLayout.LayoutParams import android.widget.ImageView import android.widget.TextView -import androidx.compose.ui.Modifier import androidx.compose.ui.platform.ComposeView -import androidx.compose.ui.unit.dp import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.view.postDelayed import androidx.core.view.updateLayoutParams @@ -16,6 +15,7 @@ import com.anytypeio.anytype.core_ui.R import com.anytypeio.anytype.core_ui.common.SearchHighlightSpan import com.anytypeio.anytype.core_ui.common.SearchTargetHighlightSpan import com.anytypeio.anytype.core_ui.databinding.ItemBlockTitleBinding +import com.anytypeio.anytype.core_ui.databinding.ItemBlockTitleFileBinding import com.anytypeio.anytype.core_ui.databinding.ItemBlockTitleProfileBinding import com.anytypeio.anytype.core_ui.databinding.ItemBlockTitleTodoBinding import com.anytypeio.anytype.core_ui.extensions.setBlockBackgroundColor @@ -23,7 +23,7 @@ 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.holders.`interface`.TextHolder import com.anytypeio.anytype.core_ui.tools.DefaultSpannableFactory -import com.anytypeio.anytype.core_ui.widgets.RadialGradientComposeView +import com.anytypeio.anytype.core_ui.widgets.ObjectIconWidget import com.anytypeio.anytype.core_ui.widgets.text.TextInputWidget import com.anytypeio.anytype.core_utils.ext.dimen import com.anytypeio.anytype.core_utils.ext.gone @@ -555,4 +555,37 @@ sealed class Title(view: View) : BlockViewHolder(view), TextHolder { content.setBlockBackgroundColor(item.background) } } + + class File(val binding: ItemBlockTitleFileBinding) : Title(binding.root) { + + override val icon: ObjectIconWidget = binding.objectIconWidget + override val image: ImageView = binding.cover + override val selectionView: View = itemView + + var isLocked: Boolean = true + + override val root: View = itemView + override val content: TextInputWidget = binding.title + + init { + content.setSpannableFactory(DefaultSpannableFactory()) + icon.binding.ivImage.updateLayoutParams { + height = itemView.resources.getDimension(R.dimen.dp_80).toInt() + width = itemView.resources.getDimension(R.dimen.dp_64).toInt() + } + } + + fun bind( + item: BlockView.Title.File, + ) { + super.bind( + item = item, + onCoverClicked = {} + ) + icon.setIcon(item.icon) + } + + override fun applyTextColor(item: BlockView.Title) {} + override fun applyBackground(item: BlockView.Title) {} + } } \ No newline at end of file diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/upload/OpenFile.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/upload/OpenFile.kt index 45c01de913..3f82eab123 100644 --- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/upload/OpenFile.kt +++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/upload/OpenFile.kt @@ -16,7 +16,7 @@ class OpenFile( root.setOnClickListener { click( ListenerType.File.View( - target = item.id + target = item.targetId, ) ) } diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/ObjectIconWidget.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/ObjectIconWidget.kt index 2ade1ca5e8..fa82e469ae 100644 --- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/ObjectIconWidget.kt +++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/ObjectIconWidget.kt @@ -144,7 +144,6 @@ class ObjectIconWidget @JvmOverloads constructor( is ObjectIcon.None -> removeIcon() is ObjectIcon.File -> setFileImage( mime = icon.mime, - fileName = icon.fileName, extension = icon.extensions ) ObjectIcon.Deleted -> setDeletedIcon() @@ -220,7 +219,7 @@ class ObjectIconWidget @JvmOverloads constructor( } } - private fun setFileImage(mime: String?, fileName: String?, extension: String?) { + private fun setFileImage(mime: String?, extension: String?) { val icon = mime.getMimeIcon(extension) with(binding) { ivImage.visible() diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/ext/BlockViewExt.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/ext/BlockViewExt.kt index 4f8cb14f91..c54ddf6e03 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/ext/BlockViewExt.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/ext/BlockViewExt.kt @@ -155,6 +155,9 @@ fun List.singleStylingMode( is BlockView.Title.Todo -> view.copy( mode = BlockView.Mode.READ ) + is BlockView.Title.File -> view.copy( + mode = BlockView.Mode.READ + ) is BlockView.Title.Profile -> view.copy( mode = BlockView.Mode.READ ) @@ -312,6 +315,9 @@ fun List.enterSAM( is BlockView.Title.Todo -> view.copy( mode = BlockView.Mode.READ ) + is BlockView.Title.File -> view.copy( + mode = BlockView.Mode.READ + ) is BlockView.Title.Archive -> view.copy( mode = BlockView.Mode.READ ) @@ -494,6 +500,7 @@ fun List.updateCursorAndEditMode( ) is BlockView.Title.Basic -> view.copy(mode = BlockView.Mode.EDIT) is BlockView.Title.Todo -> view.copy(mode = BlockView.Mode.EDIT) + is BlockView.Title.File -> view.copy(mode = BlockView.Mode.EDIT) is BlockView.Title.Profile -> view.copy(mode = BlockView.Mode.EDIT) is BlockView.Title.Archive -> view.copy(mode = BlockView.Mode.EDIT) else -> view.also { @@ -516,6 +523,7 @@ fun List.toReadMode(): List = map { view -> is BlockView.Text.Callout -> view.copy(mode = BlockView.Mode.READ) is BlockView.Title.Basic -> view.copy(mode = BlockView.Mode.READ) is BlockView.Title.Todo -> view.copy(mode = BlockView.Mode.READ) + is BlockView.Title.File -> view.copy(mode = BlockView.Mode.READ) is BlockView.Title.Profile -> view.copy(mode = BlockView.Mode.READ) is BlockView.Title.Archive -> view.copy(mode = BlockView.Mode.READ) is BlockView.Description -> view.copy(mode = BlockView.Mode.READ) @@ -585,6 +593,7 @@ fun List.toEditMode(): List = map { view -> is BlockView.Title.Basic -> view.copy(mode = BlockView.Mode.EDIT) is BlockView.Title.Profile -> view.copy(mode = BlockView.Mode.EDIT) is BlockView.Title.Todo -> view.copy(mode = BlockView.Mode.EDIT) + is BlockView.Title.File -> view.copy(mode = BlockView.Mode.EDIT) is BlockView.Title.Archive -> view.copy(mode = BlockView.Mode.EDIT) else -> view.also { check(view !is BlockView.Permission) } } @@ -605,6 +614,7 @@ fun List.clearSearchHighlights(): List = map { view -> is BlockView.Title.Basic -> view.copy(searchFields = emptyList()) is BlockView.Title.Profile -> view.copy(searchFields = emptyList()) is BlockView.Title.Todo -> view.copy(searchFields = emptyList()) + is BlockView.Title.File -> view.copy(searchFields = emptyList()) is BlockView.Media.Bookmark -> view.copy(searchFields = emptyList()) is BlockView.Media.File -> view.copy(searchFields = emptyList()) is BlockView.LinkToObject.Default.Text -> view.copy(searchFields = emptyList()) @@ -674,6 +684,10 @@ fun List.highlight( val fields = listOf(DEFAULT_SEARCH_FIELD_KEY to view.text.orEmpty()) view.copy(searchFields = highlighter(fields)) } + is BlockView.Title.File -> { + val fields = listOf(DEFAULT_SEARCH_FIELD_KEY to view.text.orEmpty()) + view.copy(searchFields = highlighter(fields)) + } is BlockView.Title.Profile -> { val fields = listOf(DEFAULT_SEARCH_FIELD_KEY to view.text.orEmpty()) view.copy(searchFields = highlighter(fields)) @@ -781,6 +795,7 @@ fun BlockView.setHighlight( is BlockView.Title.Basic -> copy(searchFields = highlights) is BlockView.Title.Profile -> copy(searchFields = highlights) is BlockView.Title.Todo -> copy(searchFields = highlights) + is BlockView.Title.File -> copy(searchFields = highlights) is BlockView.Media.Bookmark -> copy(searchFields = highlights) is BlockView.Media.File -> copy(searchFields = highlights) is BlockView.LinkToObject.Default.Text -> copy(searchFields = highlights) diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/model/BlockView.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/model/BlockView.kt index 8d1720dab2..5592f60427 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/model/BlockView.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/model/BlockView.kt @@ -27,6 +27,7 @@ import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_FILE import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_FILE_ERROR import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_FILE_PLACEHOLDER +import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_FILE_TITLE import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_FILE_UPLOAD import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_HEADER_ONE import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_HEADER_THREE @@ -670,6 +671,31 @@ sealed class BlockView : ViewType { override fun getViewType() = HOLDER_TITLE } + /** + * UI-model for a file-layout title block. + * @property id block's id + * @property text text content (i.e. title text) + */ + data class File( + override val id: String, + override var isFocused: Boolean = false, + override var text: String, + override var coverColor: CoverColor? = null, + override var coverImage: Url? = null, + override var coverGradient: String? = null, + override val background: ThemeColor = ThemeColor.DEFAULT, + override val color: ThemeColor = ThemeColor.DEFAULT, + val emoji: String? = null, + override val image: String? = null, + override val mode: Mode = Mode.READ, + override var cursor: Int? = null, + override val searchFields: List = emptyList(), + override val hint: String? = null, + val icon: ObjectIcon + ) : Title(), Searchable { + override fun getViewType() = HOLDER_FILE_TITLE + } + /** * UI-model for a profile-layout title block. * @property id block's id @@ -1279,14 +1305,14 @@ sealed class BlockView : ViewType { data class Image( override val id: String, - override val targetId: Id? + override val targetId: Id ) : OpenFile(id) { override fun getViewType(): Int = HOLDER_OPEN_IMAGE } data class File( override val id: String, - override val targetId: Id? + override val targetId: Id ) : OpenFile(id) { override fun getViewType(): Int = HOLDER_OPEN_FILE } diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/model/types/Types.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/model/types/Types.kt index 9174ea0354..b898102ab5 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/model/types/Types.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/model/types/Types.kt @@ -5,6 +5,7 @@ object Types { const val HOLDER_TITLE = 1 const val HOLDER_PROFILE_TITLE = 35 const val HOLDER_ARCHIVE_TITLE = 36 + const val HOLDER_FILE_TITLE = 37 const val HOLDER_TODO_TITLE = 48 const val HOLDER_HEADER_ONE = 2 const val HOLDER_HEADER_TWO = 3 diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/selection/TableCellExt.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/selection/TableCellExt.kt index d120e80bc8..c36c645a3e 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/selection/TableCellExt.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/selection/TableCellExt.kt @@ -283,6 +283,7 @@ fun List.toggleTableMode( is BlockView.Title.Basic -> view.copy( mode = cellsMode ) + is BlockView.Title.File -> view is BlockView.Title.Profile -> view.copy( mode = cellsMode ) From a68c5b177b5707a20ecfe5cf177f20c466697960 Mon Sep 17 00:00:00 2001 From: konstantiniiv Date: Sat, 21 Dec 2024 13:59:13 +0100 Subject: [PATCH 09/23] DROID-3185 legacy --- .../presentation/editor/EditorViewModel.kt | 32 ------------------- 1 file changed, 32 deletions(-) diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModel.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModel.kt index 9a5620000f..e01562cfa4 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModel.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModel.kt @@ -366,8 +366,6 @@ class EditorViewModel( val actions = MutableStateFlow(ActionItemType.defaultSorting) - val icon = MutableStateFlow(ProfileIconView.Loading) - val isUndoEnabled = MutableStateFlow(false) val isRedoEnabled = MutableStateFlow(false) val isUndoRedoToolbarIsVisible = MutableStateFlow(false) @@ -442,7 +440,6 @@ class EditorViewModel( init { Timber.i("EditorViewModel, init") proceedWithObservingPermissions() - proceedWithObservingProfileIcon() startHandlingTextChanges() startProcessingFocusChanges() startProcessingControlPanelViewState() @@ -489,35 +486,6 @@ class EditorViewModel( } } - private fun proceedWithObservingProfileIcon() { - viewModelScope.launch { - spaceManager - .observe() - .flatMapLatest { config -> - storelessSubscriptionContainer.subscribe( - StoreSearchByIdsParams( - space = SpaceId(config.techSpace), - subscription = HOME_SCREEN_PROFILE_OBJECT_SUBSCRIPTION, - targets = listOf(config.profile), - keys = listOf( - Relations.ID, - Relations.NAME, - Relations.ICON_EMOJI, - Relations.ICON_IMAGE, - Relations.ICON_OPTION - ) - ) - ).map { result -> - val obj = result.firstOrNull() - obj?.profileIcon(urlBuilder) ?: ProfileIconView.Placeholder(null) - } - } - .catch { Timber.e(it, "Error while observing space icon") } - .flowOn(dispatchers.io) - .collect { icon.value = it } - } - } - override fun onPickedDocImageFromDevice(ctx: Id, path: String) { viewModelScope.launch { val obj = orchestrator.stores.details.getAsObject(ctx) From e68cc715379441053bfaa6f32884ba7bcfe4b811 Mon Sep 17 00:00:00 2001 From: konstantiniiv Date: Sat, 21 Dec 2024 14:01:21 +0100 Subject: [PATCH 10/23] DROID-3185 fix --- .../anytype/core_ui/features/editor/holders/other/Title.kt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt index 718cfd9ec3..84bcebb1c3 100644 --- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt +++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt @@ -561,14 +561,10 @@ sealed class Title(view: View) : BlockViewHolder(view), TextHolder { override val icon: ObjectIconWidget = binding.objectIconWidget override val image: ImageView = binding.cover override val selectionView: View = itemView - - var isLocked: Boolean = true - override val root: View = itemView override val content: TextInputWidget = binding.title init { - content.setSpannableFactory(DefaultSpannableFactory()) icon.binding.ivImage.updateLayoutParams { height = itemView.resources.getDimension(R.dimen.dp_80).toInt() width = itemView.resources.getDimension(R.dimen.dp_64).toInt() From 79e63df5abd7d376ed5b6a7bfb43813e4c223b09 Mon Sep 17 00:00:00 2001 From: konstantiniiv Date: Sat, 21 Dec 2024 14:29:45 +0100 Subject: [PATCH 11/23] DROID-3185 render new file open blocks --- .../presentation/editor/EditorViewModel.kt | 9 -- .../editor/render/DefaultBlockViewRenderer.kt | 98 +++++++++---------- .../presentation/mapper/MapperExtension.kt | 85 ++++++++++------ 3 files changed, 105 insertions(+), 87 deletions(-) diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModel.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModel.kt index e01562cfa4..0312c5b9cb 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModel.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModel.kt @@ -5,7 +5,6 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope import com.anytypeio.anytype.analytics.base.Analytics import com.anytypeio.anytype.analytics.base.EventsDictionary -import com.anytypeio.anytype.analytics.base.EventsDictionary.Routes.objDate import com.anytypeio.anytype.analytics.base.EventsDictionary.searchScreenShow import com.anytypeio.anytype.analytics.base.EventsPropertiesKey import com.anytypeio.anytype.analytics.base.sendEvent @@ -22,7 +21,6 @@ import com.anytypeio.anytype.core_models.InternalFlags import com.anytypeio.anytype.core_models.Key import com.anytypeio.anytype.core_models.Marketplace.COLLECTION_MARKETPLACE_ID import com.anytypeio.anytype.core_models.Marketplace.SET_MARKETPLACE_ID -import com.anytypeio.anytype.core_models.MarketplaceObjectTypeIds import com.anytypeio.anytype.core_models.ObjectType import com.anytypeio.anytype.core_models.ObjectTypeIds import com.anytypeio.anytype.core_models.ObjectTypeUniqueKeys @@ -83,7 +81,6 @@ import com.anytypeio.anytype.domain.event.interactor.SpaceSyncAndP2PStatusProvid import com.anytypeio.anytype.domain.icon.SetDocumentImageIcon import com.anytypeio.anytype.domain.icon.SetImageIcon import com.anytypeio.anytype.domain.launch.GetDefaultObjectType -import com.anytypeio.anytype.domain.library.StoreSearchByIdsParams import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer import com.anytypeio.anytype.domain.misc.DateProvider import com.anytypeio.anytype.domain.misc.UrlBuilder @@ -223,7 +220,6 @@ import com.anytypeio.anytype.presentation.extension.sendAnalyticsObjectShowEvent import com.anytypeio.anytype.presentation.extension.sendAnalyticsObjectTypeSelectOrChangeEvent import com.anytypeio.anytype.presentation.extension.sendAnalyticsOpenAsObject import com.anytypeio.anytype.presentation.extension.sendAnalyticsRelationEvent -import com.anytypeio.anytype.presentation.extension.sendAnalyticsSearchResultEvent import com.anytypeio.anytype.presentation.extension.sendAnalyticsSearchWordsEvent import com.anytypeio.anytype.presentation.extension.sendAnalyticsSelectTemplateEvent import com.anytypeio.anytype.presentation.extension.sendAnalyticsSelectionMenuEvent @@ -233,7 +229,6 @@ import com.anytypeio.anytype.presentation.extension.sendAnalyticsSlashMenuEvent import com.anytypeio.anytype.presentation.extension.sendAnalyticsStyleMenuEvent import com.anytypeio.anytype.presentation.extension.sendAnalyticsUpdateTextMarkupEvent import com.anytypeio.anytype.presentation.extension.sendHideKeyboardEvent -import com.anytypeio.anytype.presentation.home.HomeScreenViewModel.Companion.HOME_SCREEN_PROFILE_OBJECT_SUBSCRIPTION import com.anytypeio.anytype.presentation.home.OpenObjectNavigation import com.anytypeio.anytype.presentation.home.navigation import com.anytypeio.anytype.presentation.mapper.mark @@ -264,8 +259,6 @@ import com.anytypeio.anytype.presentation.objects.getObjectTypeViewsForSBPage import com.anytypeio.anytype.presentation.objects.getProperType import com.anytypeio.anytype.presentation.objects.isTemplatesAllowed import com.anytypeio.anytype.presentation.objects.toViews -import com.anytypeio.anytype.presentation.profile.ProfileIconView -import com.anytypeio.anytype.presentation.profile.profileIcon import com.anytypeio.anytype.presentation.relations.ObjectRelationView import com.anytypeio.anytype.presentation.relations.getNotIncludedRecommendedRelations import com.anytypeio.anytype.presentation.relations.getObjectRelations @@ -296,8 +289,6 @@ import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filterNotNull -import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/render/DefaultBlockViewRenderer.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/render/DefaultBlockViewRenderer.kt index 5fc8d63935..c99daf6952 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/render/DefaultBlockViewRenderer.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/render/DefaultBlockViewRenderer.kt @@ -623,54 +623,32 @@ class DefaultBlockViewRenderer @Inject constructor( isPreviousBlockMedia = link is BlockView.LinkToObject.Default.Card } is Content.File -> { - val detail = details.details.getOrDefault(root.id, Block.Fields.empty()) - val obj = ObjectWrapper.Basic(detail.map) - if (SupportedLayouts.fileLayouts.contains(obj.layout)) { - when (obj.layout) { - ObjectType.Layout.IMAGE -> { - result.add( - BlockView.OpenFile.Image( - id = block.id, - targetId = content.targetObjectId - ) - ) - } - else -> { - result.add( - BlockView.OpenFile.File( - id = block.id, - targetId = content.targetObjectId - ) - ) - } - } - } else { - mCounter = 0 - val blockDecorationScheme = buildNestedDecorationData( - block = block, - parentScheme = parentScheme, - currentDecoration = DecorationData( - style = if ((content.type == Content.File.Type.FILE || content.type == Content.File.Type.PDF) && content.state == Content.File.State.DONE) - DecorationData.Style.None - else - DecorationData.Style.Card, - background = block.parseThemeBackgroundColor() - ) + mCounter = 0 + val blockDecorationScheme = buildNestedDecorationData( + block = block, + parentScheme = parentScheme, + currentDecoration = DecorationData( + style = if ((content.type == Content.File.Type.FILE || content.type == Content.File.Type.PDF) && content.state == Content.File.State.DONE) + DecorationData.Style.None + else + DecorationData.Style.Card, + background = block.parseThemeBackgroundColor() ) - result.add( - file( - mode = mode, - content = content, - block = block, - indent = indent, - selection = selection, - isPreviousBlockMedia = isPreviousBlockMedia, - schema = blockDecorationScheme, - details = details - ) + ) + result.add( + file( + root = root, + mode = mode, + content = content, + block = block, + indent = indent, + selection = selection, + isPreviousBlockMedia = isPreviousBlockMedia, + schema = blockDecorationScheme, + details = details ) - isPreviousBlockMedia = true - } + ) + isPreviousBlockMedia = true } is Content.Layout -> { isPreviousBlockMedia = false @@ -1394,6 +1372,7 @@ class DefaultBlockViewRenderer @Inject constructor( } private fun file( + root: Block, mode: EditorMode, content: Content.File, block: Block, @@ -1404,6 +1383,7 @@ class DefaultBlockViewRenderer @Inject constructor( details: Block.Details ): BlockView = when (content.type) { Content.File.Type.IMAGE -> content.toPictureView( + root = root, blockId = block.id, urlBuilder = urlBuilder, indent = indent, @@ -1419,6 +1399,7 @@ class DefaultBlockViewRenderer @Inject constructor( details = details ) Content.File.Type.FILE -> content.toFileView( + root = root, blockId = block.id, urlBuilder = urlBuilder, indent = indent, @@ -1449,6 +1430,7 @@ class DefaultBlockViewRenderer @Inject constructor( details = details ) Content.File.Type.AUDIO -> content.toFileView( + root = root, blockId = block.id, urlBuilder = urlBuilder, indent = indent, @@ -1464,6 +1446,7 @@ class DefaultBlockViewRenderer @Inject constructor( details = details ) Content.File.Type.PDF -> content.toFileView( + root = root, blockId = block.id, urlBuilder = urlBuilder, indent = indent, @@ -1479,6 +1462,7 @@ class DefaultBlockViewRenderer @Inject constructor( details = details ) Content.File.Type.NONE -> content.toFileView( + root = root, blockId = block.id, urlBuilder = urlBuilder, indent = indent, @@ -1529,7 +1513,7 @@ class DefaultBlockViewRenderer @Inject constructor( val layoutCode = details.details[root.id]?.layout?.toInt() - val layout = ObjectType.Layout.values().find { + val layout = ObjectType.Layout.entries.find { it.code == layoutCode } ?: ObjectType.Layout.BASIC @@ -1590,18 +1574,32 @@ class DefaultBlockViewRenderer @Inject constructor( ) } ObjectType.Layout.FILE, - ObjectType.Layout.IMAGE, ObjectType.Layout.BOOKMARK, ObjectType.Layout.VIDEO, ObjectType.Layout.AUDIO, ObjectType.Layout.PDF -> { + val objFile = ObjectWrapper.Basic(details.details[root.id]?.map.orEmpty()) + BlockView.Title.File( + mode = blockMode, + id = block.id, + text = content.text, + isFocused = resolveIsFocused(focus, block), + cursor = cursor, + coverColor = coverContainer.coverColor, + coverImage = coverContainer.coverImage, + coverGradient = coverContainer.coverGradient, + background = block.parseThemeBackgroundColor(), + color = block.textColor(), + icon = objFile.objectIcon(builder = urlBuilder) + ) + } + ObjectType.Layout.IMAGE -> { BlockView.Title.Basic( mode = blockMode, id = block.id, text = content.text, - emoji = details.details[root.id]?.iconEmoji?.takeIf { it.isNotBlank() }, image = details.details[root.id]?.iconImage?.let { image -> - if (image.isNotBlank() && layout != ObjectType.Layout.BOOKMARK) + if (image.isNotBlank()) urlBuilder.thumbnail(image) else null diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/mapper/MapperExtension.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/mapper/MapperExtension.kt index bfa7b84a40..7e6cb0242d 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/mapper/MapperExtension.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/mapper/MapperExtension.kt @@ -31,6 +31,7 @@ import com.anytypeio.anytype.presentation.templates.TemplateObjectTypeView import timber.log.Timber fun Block.Content.File.toPictureView( + root: Block, blockId: String, urlBuilder: UrlBuilder, indent: Int, @@ -62,22 +63,29 @@ fun Block.Content.File.toPictureView( val url = urlBuilder.getUrlForFileContent(this) val targetId = this.targetObjectId val struct = details.details[targetId]?.map + val currentObject = ObjectWrapper.Basic(details.details[root.id]?.map.orEmpty()) if (url != null && targetId != null) { val targetObject = ObjectWrapper.File(struct.orEmpty()) - 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 - ) - + if (currentObject.layout == ObjectType.Layout.IMAGE) { + BlockView.OpenFile.Image( + id = blockId, + targetId = targetId + ) + } else { + 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( @@ -175,6 +183,7 @@ fun Block.Content.File.toVideoView( } fun Block.Content.File.toFileView( + root: Block, blockId: String, urlBuilder: UrlBuilder, indent: Int, @@ -203,6 +212,7 @@ fun Block.Content.File.toFileView( decorations = decorations ) Block.Content.File.State.DONE -> { + val currentObject = ObjectWrapper.Basic(details.details[root.id]?.map.orEmpty()) val url = urlBuilder.getUrlForFileContent(this) val targetId = this.targetObjectId val struct = details.details[targetId]?.map @@ -218,20 +228,39 @@ fun Block.Content.File.toFileView( ) } else { 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 - ) + when (currentObject.layout) { + ObjectType.Layout.IMAGE -> { + BlockView.OpenFile.Image( + id = blockId, + targetId = targetId + ) + } + ObjectType.Layout.FILE, + ObjectType.Layout.VIDEO, + ObjectType.Layout.AUDIO, + ObjectType.Layout.PDF -> { + BlockView.OpenFile.File( + id = blockId, + targetId = targetId + ) + } + else -> { + 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") From 38804b32f6e7bcc4b2e744ace653721077e84900 Mon Sep 17 00:00:00 2001 From: konstantiniiv Date: Mon, 23 Dec 2024 10:30:15 +0100 Subject: [PATCH 12/23] DROID-3185 tests --- .../editor/DefaultBlockViewRendererTest.kt | 59 ++--- .../editor/EditorViewModelTest.kt | 124 ++++------ .../editor/editor/EditorErrorMessageTest.kt | 43 ++-- .../editor/editor/EditorLockPageTest.kt | 29 +-- .../editor/EditorMultiSelectModeTest.kt | 14 +- .../editor/file_layout/FileLayoutTest.kt | 211 +++++++++++++++++ .../editor/styling/StyleToolbarExtKtTest.kt | 15 +- .../editor/table/TableBlockRendererTest.kt | 1 + .../mapper/MapperExtensionKtTest.kt | 216 +++++++++--------- 9 files changed, 444 insertions(+), 268 deletions(-) create mode 100644 presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/file_layout/FileLayoutTest.kt diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/DefaultBlockViewRendererTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/DefaultBlockViewRendererTest.kt index e2f4ae65eb..3351b95856 100644 --- a/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/DefaultBlockViewRendererTest.kt +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/DefaultBlockViewRendererTest.kt @@ -76,6 +76,7 @@ class DefaultBlockViewRendererTest { details: Block.Details, schema: NestedDecorationData = emptyList() ): List = blocks.render( + context = root.id, root = root, focus = focus, anchor = anchor, @@ -5756,10 +5757,10 @@ class DefaultBlockViewRendererTest { @Test fun `should render file open block in case of file, pdf, audio or video layouts`(){ val fileTypeExceptImage = listOf( - Block.Content.File.Type.FILE to ObjectType.Layout.FILE, - Block.Content.File.Type.PDF to ObjectType.Layout.PDF, - Block.Content.File.Type.VIDEO to ObjectType.Layout.VIDEO, - Block.Content.File.Type.AUDIO to ObjectType.Layout.AUDIO, + Block.Content.File.Type.FILE to Layout.FILE, + Block.Content.File.Type.PDF to Layout.PDF, + Block.Content.File.Type.VIDEO to Layout.VIDEO, + Block.Content.File.Type.AUDIO to Layout.AUDIO, ) fileTypeExceptImage.forEach { (fileType, layout) -> @@ -5769,22 +5770,23 @@ class DefaultBlockViewRendererTest { private fun testFileLayout(type: Block.Content.File.Type, layout: Layout) { - val file = StubFile( - backgroundColor = null, - state = Block.Content.File.State.DONE, - type = type - ) + val currentObjectId = MockDataFactory.randomUuid() + + val file = StubFile(type = type, targetObjectId = currentObjectId) val paragraph = StubParagraph() - val page = StubSmartBlock(children = listOf(paragraph.id, file.id)) + val page = StubSmartBlock(id = currentObjectId, children = listOf(paragraph.id, file.id)) - val details = mapOf(page.id to Block.Fields( - mapOf( - Relations.NAME to "file-name", - Relations.LAYOUT to layout.code.toDouble() + val details = mapOf( + page.id to Block.Fields( + mapOf( + Relations.ID to currentObjectId, + Relations.NAME to "file-name", + Relations.LAYOUT to layout.code.toDouble() + ) ) - )) + ) val blocks = listOf(page, paragraph, file) @@ -5822,7 +5824,7 @@ class DefaultBlockViewRendererTest { ), BlockView.OpenFile.File( id = file.id, - targetId = (file.content as Block.Content.File).targetObjectId + targetId = currentObjectId ) ) @@ -5832,22 +5834,27 @@ class DefaultBlockViewRendererTest { @Test fun `should render image open block in case of image layout`() { + val currentObjectId = MockDataFactory.randomUuid() + val file = StubFile( - backgroundColor = null, - state = Block.Content.File.State.DONE, - type = Block.Content.File.Type.IMAGE + type = Block.Content.File.Type.IMAGE, + targetObjectId = currentObjectId, + state = Block.Content.File.State.DONE ) val paragraph = StubParagraph() - val page = StubSmartBlock(children = listOf(paragraph.id, file.id)) + val page = StubSmartBlock(id = currentObjectId, children = listOf(paragraph.id, file.id)) - val details = mapOf(page.id to Block.Fields( - mapOf( - Relations.NAME to "file-name", - Relations.LAYOUT to Layout.IMAGE.code.toDouble() + val details = mapOf( + page.id to Block.Fields( + mapOf( + Relations.ID to currentObjectId, + Relations.NAME to "image-name", + Relations.LAYOUT to ObjectType.Layout.IMAGE.code.toDouble() + ) ) - )) + ) val blocks = listOf(page, paragraph, file) @@ -5885,7 +5892,7 @@ class DefaultBlockViewRendererTest { ), BlockView.OpenFile.Image( id = file.id, - targetId = (file.content as Block.Content.File).targetObjectId + targetId = currentObjectId ) ) diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/EditorViewModelTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/EditorViewModelTest.kt index c8af0443ea..18d7357747 100644 --- a/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/EditorViewModelTest.kt +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/EditorViewModelTest.kt @@ -1,6 +1,7 @@ package com.anytypeio.anytype.presentation.editor +import android.R import android.net.Uri import android.os.Build import androidx.arch.core.executor.testing.InstantTaskExecutorRule @@ -9,11 +10,15 @@ 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.ObjectType +import com.anytypeio.anytype.core_models.ObjectTypeIds import com.anytypeio.anytype.core_models.Payload import com.anytypeio.anytype.core_models.Position import com.anytypeio.anytype.core_models.Relation +import com.anytypeio.anytype.core_models.Relations import com.anytypeio.anytype.core_models.StubFile import com.anytypeio.anytype.core_models.StubNumbered +import com.anytypeio.anytype.core_models.StubObject import com.anytypeio.anytype.core_models.StubParagraph import com.anytypeio.anytype.core_models.ThemeColor import com.anytypeio.anytype.core_models.ext.content @@ -2555,68 +2560,24 @@ open class EditorViewModelTest { } @Test - fun `should start sharing a file`() { + fun `should start downloading file`() { val root = MockDataFactory.randomUuid() - val file = MockBlockFactory.makeFileBlock() + val targetObjectId = MockDataFactory.randomUuid() + val file = StubFile( + targetObjectId = targetObjectId, + type = Block.Content.File.Type.FILE + ) val title = MockBlockFactory.makeTitleBlock() - val page = listOf( - Block( - id = root, - fields = Block.Fields(emptyMap()), - content = Block.Content.Smart, - children = listOf(title.id, file.id) - ), - title, - file + val targetObject = StubObject( + id = targetObjectId, + name = "file1", + layout = ObjectType.Layout.FILE.code.toDouble(), + fileExt = ".pdf" ) - val flow: Flow> = flow { - delay(100) - emit( - listOf( - Event.Command.ShowObject( - root = root, - blocks = page, - context = root - ) - ) - ) - } - - stubObserveEvents(flow) - stubOpenPage() - givenViewModel(builder) - - givenSharedFile() - - vm.onStart(id = root, space = defaultSpace) - - coroutineTestRule.advanceTime(100) - - // TESTING - - vm.startSharingFile(id = file.id) - - runTest { - verify(documentFileShareDownloader, times(1)).async( - params = eq( - MiddlewareShareDownloader.Params( - name = file.content().name.orEmpty(), - objectId = file.content().targetObjectId.orEmpty(), - ) - ) - ) - } - } - - @Test - fun `should start downloading file`() { - - val root = MockDataFactory.randomUuid() - val file = MockBlockFactory.makeFileBlock() - val title = MockBlockFactory.makeTitleBlock() + val objectDetails = Block.Fields(targetObject.map) val page = listOf( Block( @@ -2636,12 +2597,19 @@ open class EditorViewModelTest { Event.Command.ShowObject( root = root, blocks = page, - context = root + context = root, + details = Block.Details(mapOf( + targetObjectId to objectDetails + )) ) ) ) } + fieldParser.stub { + on { getObjectName(targetObject) } doReturn targetObject.name!! + } + stubObserveEvents(flow) stubOpenPage() givenViewModel(builder) @@ -2660,10 +2628,8 @@ open class EditorViewModelTest { verify(downloadFile, times(1)).invoke( params = eq( DownloadFile.Params( - name = file.content().name.orEmpty(), - url = builder.file( - path = file.content().targetObjectId!! - ) + name = targetObject.name!!, + url = builder.file(path = targetObjectId) ) ) ) @@ -3423,17 +3389,11 @@ open class EditorViewModelTest { } @Test - fun `open select picture - when error in edit mode`() { + fun `open select picture - when error in edit mode`() = runTest { - val picture = Block( - id = MockDataFactory.randomUuid(), - fields = Block.Fields(emptyMap()), - content = Block.Content.File( - targetObjectId = MockDataFactory.randomString(), - type = Block.Content.File.Type.IMAGE, - state = Block.Content.File.State.ERROR - ), - children = emptyList() + val picture = StubFile( + state = Block.Content.File.State.ERROR, + type = Block.Content.File.Type.IMAGE ) val page = listOf( @@ -3457,10 +3417,10 @@ open class EditorViewModelTest { ) givenViewModel() - + advanceUntilIdle() vm.onStart(id = root, space = defaultSpace) - + advanceUntilIdle() val expected = listOf( BlockView.Title.Basic( @@ -3477,7 +3437,8 @@ open class EditorViewModelTest { background = ThemeColor.DEFAULT, style = BlockView.Decoration.Style.Card ) - ) + ), + name = picture.content.asFile().name ) ) @@ -3500,15 +3461,9 @@ open class EditorViewModelTest { @Test fun `open select video - when error in edit mode`() { - val video = Block( - id = MockDataFactory.randomUuid(), - fields = Block.Fields(emptyMap()), - content = Block.Content.File( - targetObjectId = MockDataFactory.randomString(), - type = Block.Content.File.Type.VIDEO, - state = Block.Content.File.State.ERROR - ), - children = emptyList() + val video = StubFile( + state = Block.Content.File.State.ERROR, + type = Block.Content.File.Type.VIDEO ) val page = listOf( @@ -3552,7 +3507,8 @@ open class EditorViewModelTest { background = ThemeColor.DEFAULT, style = BlockView.Decoration.Style.Card ) - ) + ), + name = video.content.asFile().name ) ) diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/EditorErrorMessageTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/EditorErrorMessageTest.kt index 7bbcd44983..6b5d95e8fd 100644 --- a/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/EditorErrorMessageTest.kt +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/EditorErrorMessageTest.kt @@ -2,6 +2,10 @@ package com.anytypeio.anytype.presentation.editor.editor import androidx.arch.core.executor.testing.InstantTaskExecutorRule import com.anytypeio.anytype.core_models.Block +import com.anytypeio.anytype.core_models.ObjectType +import com.anytypeio.anytype.core_models.Relations +import com.anytypeio.anytype.core_models.StubFile +import com.anytypeio.anytype.core_models.StubObject import com.anytypeio.anytype.domain.base.Either import com.anytypeio.anytype.presentation.util.DefaultCoroutineTestRule import com.anytypeio.anytype.test_utils.MockDataFactory @@ -38,15 +42,26 @@ class EditorErrorMessageTest : EditorPresentationTestSetup() { val consumed = mutableListOf() - val file = Block( - id = MockDataFactory.randomUuid(), - content = Block.Content.File( - targetObjectId = MockDataFactory.randomUuid(), - type = Block.Content.File.Type.FILE, - state = Block.Content.File.State.DONE - ), - fields = Block.Fields.empty(), - children = emptyList() + val fileObjectId = MockDataFactory.randomUuid() + + val fileBlock = StubFile( + type = Block.Content.File.Type.FILE, + state = Block.Content.File.State.DONE, + targetObjectId = fileObjectId + ) + + val details = Block.Details( + mapOf( + fileObjectId to Block.Fields( + mapOf( + Relations.ID to fileObjectId, + Relations.NAME to "file object", + Relations.SIZE_IN_BYTES to 10000.0, + Relations.FILE_MIME_TYPE to "pdf", + Relations.LAYOUT to ObjectType.Layout.FILE.code.toDouble() + ) + ) + ) ) val doc = listOf( @@ -54,17 +69,19 @@ class EditorErrorMessageTest : EditorPresentationTestSetup() { id = root, fields = Block.Fields(emptyMap()), content = Block.Content.Smart, - children = listOf(file.id) + children = listOf(fileBlock.id) ), - file + fileBlock ) - stubOpenDocument(doc) + stubOpenDocument(doc, details) stubInterceptEvents() stubDownloadFile() val vm = buildViewModel() + advanceUntilIdle() + vm.onStart(id = root, space = defaultSpace) advanceUntilIdle() @@ -73,7 +90,7 @@ class EditorErrorMessageTest : EditorPresentationTestSetup() { // Launching operation that triggers a toast - vm.startDownloadingFileFromBlock(blockId = file.id) + vm.startDownloadingFileFromBlock(blockId = fileBlock.id) advanceUntilIdle() diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/EditorLockPageTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/EditorLockPageTest.kt index 1513c379c2..3b5e0ff121 100644 --- a/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/EditorLockPageTest.kt +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/EditorLockPageTest.kt @@ -1,11 +1,11 @@ package com.anytypeio.anytype.presentation.editor.editor -import android.R import androidx.arch.core.executor.testing.InstantTaskExecutorRule import com.anytypeio.anytype.core_models.Block import com.anytypeio.anytype.core_models.ObjectType import com.anytypeio.anytype.core_models.Relations import com.anytypeio.anytype.core_models.StubBookmark +import com.anytypeio.anytype.core_models.StubFile import com.anytypeio.anytype.core_models.StubTitle import com.anytypeio.anytype.core_models.ThemeColor import com.anytypeio.anytype.core_models.ext.content @@ -28,8 +28,6 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.mockito.MockitoAnnotations -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.stub import org.mockito.kotlin.whenever class EditorLockPageTest : EditorPresentationTestSetup() { @@ -585,15 +583,11 @@ class EditorLockPageTest : EditorPresentationTestSetup() { val fileBlockId = MockDataFactory.randomUuid() val targetObjectId = MockDataFactory.randomUuid() - val file = Block( + val file = StubFile( id = fileBlockId, - fields = Block.Fields(emptyMap()), - content = Block.Content.File( - targetObjectId = targetObjectId, - type = Block.Content.File.Type.FILE, - state = Block.Content.File.State.DONE - ), - children = emptyList() + type = Block.Content.File.Type.FILE, + state = Block.Content.File.State.DONE, + targetObjectId = targetObjectId ) val page = listOf( @@ -697,15 +691,10 @@ class EditorLockPageTest : EditorPresentationTestSetup() { val fileName = "image.png" val fileSize = 1000.0 - val picture = Block( - id = fileBlockId, - fields = Block.Fields(emptyMap()), - content = Block.Content.File( - targetObjectId = targetObjectId, - type = Block.Content.File.Type.IMAGE, - state = Block.Content.File.State.DONE - ), - children = emptyList() + val picture = StubFile( + type = Block.Content.File.Type.IMAGE, + state = Block.Content.File.State.DONE, + targetObjectId = targetObjectId ) val page = listOf( diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/EditorMultiSelectModeTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/EditorMultiSelectModeTest.kt index 9a42e8b278..3dea8cec04 100644 --- a/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/EditorMultiSelectModeTest.kt +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/EditorMultiSelectModeTest.kt @@ -3,6 +3,7 @@ package com.anytypeio.anytype.presentation.editor.editor import android.os.Build import androidx.arch.core.executor.testing.InstantTaskExecutorRule import com.anytypeio.anytype.core_models.Block +import com.anytypeio.anytype.core_models.StubFile import com.anytypeio.anytype.core_models.StubHeader import com.anytypeio.anytype.core_models.StubLayoutColumns import com.anytypeio.anytype.core_models.StubLayoutRows @@ -1081,14 +1082,11 @@ class EditorMultiSelectModeTest : EditorPresentationTestSetup() { ) val backgroundC = null - val c = Block( - id = MockDataFactory.randomUuid(), - fields = Block.Fields.empty(), - children = emptyList(), - content = Block.Content.File( - state = Block.Content.File.State.EMPTY - ), - backgroundColor = backgroundC + + + val c = StubFile( + backgroundColor = backgroundC, + state = Block.Content.File.State.EMPTY ) val page = Block( diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/file_layout/FileLayoutTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/file_layout/FileLayoutTest.kt new file mode 100644 index 0000000000..08fa61469c --- /dev/null +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/file_layout/FileLayoutTest.kt @@ -0,0 +1,211 @@ +package com.anytypeio.anytype.presentation.editor.editor.file_layout + +import android.util.Log +import androidx.arch.core.executor.testing.InstantTaskExecutorRule +import com.anytypeio.anytype.core_models.Block +import com.anytypeio.anytype.core_models.ObjectType +import com.anytypeio.anytype.core_models.StubFile +import com.anytypeio.anytype.core_models.StubHeader +import com.anytypeio.anytype.core_models.StubObject +import com.anytypeio.anytype.core_models.StubSmartBlock +import com.anytypeio.anytype.core_models.StubTitle +import com.anytypeio.anytype.core_models.ext.content +import com.anytypeio.anytype.presentation.editor.editor.EditorPresentationTestSetup +import com.anytypeio.anytype.presentation.editor.editor.ViewState +import com.anytypeio.anytype.presentation.editor.editor.model.BlockView +import com.anytypeio.anytype.presentation.util.DefaultCoroutineTestRule +import com.anytypeio.anytype.presentation.util.TXT +import com.anytypeio.anytype.presentation.util.downloader.MiddlewareShareDownloader +import com.anytypeio.anytype.test_utils.MockDataFactory +import com.jraska.livedata.test +import kotlin.test.assertEquals +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.advanceUntilIdle +import kotlinx.coroutines.test.runTest +import net.bytebuddy.utility.RandomString +import net.lachlanmckee.timberjunit.TimberTestRule +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.mockito.MockitoAnnotations +import org.mockito.kotlin.eq +import org.mockito.kotlin.times +import org.mockito.kotlin.verify + +@OptIn(ExperimentalCoroutinesApi::class) +class FileLayoutTest : EditorPresentationTestSetup() { + + @get:Rule + val timberTestRule: TimberTestRule = TimberTestRule.builder() + .minPriority(Log.DEBUG) + .showThread(true) + .showTimestamp(false) + .onlyLogWhenTestFails(true) + .build() + + @get:Rule + val rule = InstantTaskExecutorRule() + + @OptIn(ExperimentalCoroutinesApi::class) + @get:Rule + val coroutineTestRule = DefaultCoroutineTestRule() + + val title = StubTitle() + val header = StubHeader(children = listOf(title.id)) + val fileBlock = StubFile( + id = "fileBlockId-${MockDataFactory.randomUuid()}", + targetObjectId = root, + state = Block.Content.File.State.DONE + ) + val page = StubSmartBlock(id = root, children = listOf(header.id, fileBlock.id)) + val document = listOf(page, header, title, fileBlock) + + @Before + fun setup() { + MockitoAnnotations.openMocks(this) + proceedWithDefaultBeforeTestStubbing() + } + + //region Editor view state + @Test + fun `should change file block to file open block`() = runTest { + + val fileObject = StubObject( + id = root, + space = defaultSpace, + name = "fileObjectName-${RandomString.make(5)}", + layout = ObjectType.Layout.FILE.code.toDouble(), + fileExt = "pdf" + ) + + val detailsList = Block.Details( + details = mapOf( + fileObject.id to Block.Fields(fileObject.map) + ) + ) + + stubOpenDocument( + document = document, + details = detailsList + ) + + val vm = buildViewModel() + advanceUntilIdle() + + vm.onStart(id = fileObject.id, space = defaultSpace) + val loadingState = vm.state.test() + assertEquals(ViewState.Loading, loadingState.value()) + + advanceUntilIdle() + + val firstTimeExpected = ViewState.Success( + listOf( + BlockView.Title.Basic( + isFocused = false, + id = title.id, + text = title.content().text + ), + BlockView.OpenFile.File( + id = fileBlock.id, + targetId = fileObject.id + ) + ) + ) + + val pageState = vm.state.test() + assertEquals(firstTimeExpected, pageState.value()) + } + + @Test + fun `should change file block to image open block`() = runTest { + + val fileObject = StubObject( + id = root, + space = defaultSpace, + name = "fileObjectName-${RandomString.make(5)}", + layout = ObjectType.Layout.IMAGE.code.toDouble(), + fileExt = "jpg" + ) + + val detailsList = Block.Details( + details = mapOf( + fileObject.id to Block.Fields(fileObject.map) + ) + ) + + stubOpenDocument( + document = document, + details = detailsList + ) + + val vm = buildViewModel() + advanceUntilIdle() + + vm.onStart(id = fileObject.id, space = defaultSpace) + val loadingState = vm.state.test() + assertEquals(ViewState.Loading, loadingState.value()) + + advanceUntilIdle() + + val firstTimeExpected = ViewState.Success( + listOf( + BlockView.Title.Basic( + isFocused = false, + id = title.id, + text = title.content().text + ), + BlockView.OpenFile.Image( + id = fileBlock.id, + targetId = fileObject.id + ) + ) + ) + + val pageState = vm.state.test() + assertEquals(firstTimeExpected, pageState.value()) + } + //endregion + + //region Open file click test + @Test + fun `should start sharing file on open file click`() = runTest { + + val fileObject = StubObject( + id = root, + space = defaultSpace, + name = "fileObjectName-${RandomString.make(5)}", + layout = ObjectType.Layout.FILE.code.toDouble(), + fileExt = "pdf" + ) + + val detailsList = Block.Details( + details = mapOf( + fileObject.id to Block.Fields(fileObject.map) + ) + ) + + stubOpenDocument( + document = document, + details = detailsList + ) + + val vm = buildViewModel() + advanceUntilIdle() + vm.onStart(id = fileObject.id, space = defaultSpace) + advanceUntilIdle() + + vm.startSharingFile(id = fileBlock.id) + advanceUntilIdle() + + val objName = fieldParser.getObjectName(fileObject) + + verify(documentFileShareDownloader, times(1)).async( + params = eq( + MiddlewareShareDownloader.Params( + name = objName, + objectId = fileObject.id, + ) + ) + ) + } +} \ No newline at end of file diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/styling/StyleToolbarExtKtTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/styling/StyleToolbarExtKtTest.kt index a399c4856a..d9c647cf4e 100644 --- a/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/styling/StyleToolbarExtKtTest.kt +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/styling/StyleToolbarExtKtTest.kt @@ -2,6 +2,7 @@ package com.anytypeio.anytype.presentation.editor.editor.styling import android.os.Build import com.anytypeio.anytype.core_models.Block +import com.anytypeio.anytype.core_models.StubFile import com.anytypeio.anytype.core_models.TextStyle import com.anytypeio.anytype.core_models.ThemeColor import com.anytypeio.anytype.test_utils.MockDataFactory @@ -140,12 +141,9 @@ class StyleToolbarExtKtTest { children = emptyList() ) - val given2 = Block( + val given2 = StubFile( id = child, - fields = Block.Fields(emptyMap()), - content = Block.Content.File(), - backgroundColor = backgroundTeal, - children = emptyList() + backgroundColor = backgroundTeal ) val given3 = Block( @@ -373,12 +371,9 @@ class StyleToolbarExtKtTest { children = emptyList() ) - val given3 = Block( + val given3 = StubFile( id = child, - fields = Block.Fields(emptyMap()), - content = Block.Content.File(), - backgroundColor = ThemeColor.LIME.code, - children = emptyList() + backgroundColor = ThemeColor.LIME.code ) val result = diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/table/TableBlockRendererTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/table/TableBlockRendererTest.kt index 2f7e61e375..ccbacf5e19 100644 --- a/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/table/TableBlockRendererTest.kt +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/table/TableBlockRendererTest.kt @@ -55,6 +55,7 @@ class TableBlockRendererTest { indent: Int, details: Block.Details ): List = blocks.render( + context = root.id, root = root, anchor = anchor, focus = focus, diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/mapper/MapperExtensionKtTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/mapper/MapperExtensionKtTest.kt index 196b680680..8531eb33a3 100644 --- a/presentation/src/test/java/com/anytypeio/anytype/presentation/mapper/MapperExtensionKtTest.kt +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/mapper/MapperExtensionKtTest.kt @@ -2,11 +2,20 @@ 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.ObjectType import com.anytypeio.anytype.core_models.Relations +import com.anytypeio.anytype.core_models.StubFile +import com.anytypeio.anytype.core_models.StubObject import com.anytypeio.anytype.domain.config.Gateway import com.anytypeio.anytype.domain.misc.UrlBuilder import com.anytypeio.anytype.presentation.editor.editor.Markup import com.anytypeio.anytype.core_models.ThemeColor +import com.anytypeio.anytype.domain.debugging.Logger +import com.anytypeio.anytype.domain.misc.DateProvider +import com.anytypeio.anytype.domain.objects.GetDateObjectByTimestamp +import com.anytypeio.anytype.domain.primitives.FieldParser +import com.anytypeio.anytype.domain.primitives.FieldParserImpl +import com.anytypeio.anytype.domain.resources.StringResourceProvider import com.anytypeio.anytype.presentation.editor.editor.model.BlockView import com.anytypeio.anytype.test_utils.MockDataFactory import org.junit.Before @@ -23,10 +32,26 @@ class MapperExtensionKtTest { private val urlBuilder: UrlBuilder get() = UrlBuilder(gateway) private val targetObjectId : Id = "647tyhfgehf7ru" + private val objectId : Id = MockDataFactory.randomUuid() + + lateinit var fieldParser: FieldParser + + @Mock + lateinit var dateProvider: DateProvider + + @Mock + lateinit var logger: Logger + + @Mock + lateinit var getDateObjectByTimestamp: GetDateObjectByTimestamp + + @Mock + lateinit var stringResourceProvider: StringResourceProvider @Before fun before() { MockitoAnnotations.openMocks(this) + fieldParser = FieldParserImpl(dateProvider, logger, getDateObjectByTimestamp, stringResourceProvider) } @Test @@ -44,25 +69,28 @@ class MapperExtensionKtTest { val details = Block.Details( mapOf( + objectId to Block.Fields( + StubObject( + layout = ObjectType.Layout.BASIC.code.toDouble() + ).map + ), targetObjectId to Block.Fields( mapOf( + Relations.ID to targetObjectId, Relations.NAME to name, Relations.SIZE_IN_BYTES to 10000.0, Relations.FILE_MIME_TYPE to mime, + Relations.LAYOUT to ObjectType.Layout.FILE.code.toDouble() ) ) ) ) - val block = Block.Content.File( - name = name, - size = 10000L, - mime = mime, - targetObjectId = targetObjectId, + val block = StubFile( state = state, - type = type - - ) + type = type, + targetObjectId = targetObjectId + ).content as Block.Content.File val expected = BlockView.Media.File( id = id, @@ -75,7 +103,7 @@ class MapperExtensionKtTest { indent = indent, decorations = emptyList() ) - val actual = block.toFileView(id, urlBuilder, indent, mode, false, ThemeColor.DEFAULT, false, emptyList(), details) + val actual = block.toFileView(objectId, id, urlBuilder, indent, mode, false, ThemeColor.DEFAULT, false, emptyList(), details, fieldParser) assertEquals(expected, actual) } @@ -91,15 +119,15 @@ class MapperExtensionKtTest { val type = Block.Content.File.Type.FILE val mode = BlockView.Mode.EDIT - val block = Block.Content.File( + val block = StubFile( state = state, type = type, targetObjectId = targetObjectId - ) + ).content as Block.Content.File val expected = BlockView.MediaPlaceholder.File(id = id, indent = indent, isPreviousBlockMedia = false) - val actual = block.toFileView(id, urlBuilder, indent, mode, false, ThemeColor.DEFAULT, false, emptyList()) + val actual = block.toFileView(objectId, id, urlBuilder, indent, mode, false, ThemeColor.DEFAULT, false, emptyList(), Block.Details(), fieldParser) assertEquals(expected, actual) } @@ -107,6 +135,8 @@ class MapperExtensionKtTest { @Test fun `should return error file block view`() { + val rootBlockId = MockDataFactory.randomUuid() + val id = MockDataFactory.randomUuid() val indent = MockDataFactory.randomInt() @@ -115,14 +145,14 @@ class MapperExtensionKtTest { val type = Block.Content.File.Type.FILE val mode = BlockView.Mode.EDIT - val block = Block.Content.File( + val block = StubFile( state = state, type = type, targetObjectId = targetObjectId - ) + ).content as Block.Content.File - val expected = BlockView.Error.File(id = id, indent = indent, decorations = emptyList()) - val actual = block.toFileView(id, urlBuilder, indent, mode, false, ThemeColor.DEFAULT, false, emptyList()) + val expected = BlockView.Error.File(id = id, indent = indent, decorations = emptyList(), name = block.name) + val actual = block.toFileView(objectId, id, urlBuilder, indent, mode, false, ThemeColor.DEFAULT, false, emptyList(), Block.Details(), fieldParser) assertEquals(expected, actual) } @@ -138,14 +168,14 @@ class MapperExtensionKtTest { val type = Block.Content.File.Type.FILE val mode = BlockView.Mode.EDIT - val block = Block.Content.File( + val block = StubFile( state = state, type = type, targetObjectId = targetObjectId - ) + ).content as Block.Content.File val expected = BlockView.Upload.File(id = id, indent = indent) - val actual = block.toFileView(id, urlBuilder, indent, mode, false, ThemeColor.DEFAULT, false, emptyList()) + val actual = block.toFileView(objectId, id, urlBuilder, indent, mode, false, ThemeColor.DEFAULT, false, emptyList(), Block.Details(), fieldParser) assertEquals(expected, actual) } @@ -165,17 +195,17 @@ class MapperExtensionKtTest { val type = Block.Content.File.Type.IMAGE val mode = BlockView.Mode.EDIT - val block = Block.Content.File( - targetObjectId = targetObjectId, + val block = StubFile( state = state, - type = type - - ) + type = type, + targetObjectId = targetObjectId + ).content as Block.Content.File val details = Block.Details( mapOf( targetObjectId to Block.Fields( mapOf( + Relations.ID to targetObjectId, Relations.NAME to name, Relations.SIZE_IN_BYTES to size, Relations.FILE_MIME_TYPE to mime, @@ -195,7 +225,7 @@ class MapperExtensionKtTest { decorations = emptyList() ) - val actual = block.toPictureView(id, urlBuilder, indent, mode, false, ThemeColor.DEFAULT, false, emptyList(), details) + val actual = block.toPictureView(objectId, id, urlBuilder, indent, mode, false, ThemeColor.DEFAULT, false, emptyList(), details, fieldParser) assertEquals(expected, actual) } @@ -211,18 +241,18 @@ class MapperExtensionKtTest { val type = Block.Content.File.Type.IMAGE val mode = BlockView.Mode.EDIT - val block = Block.Content.File( + val block = StubFile( state = state, type = type, targetObjectId = targetObjectId - ) + ).content as Block.Content.File val expected = BlockView.MediaPlaceholder.Picture( id = id, indent = indent, isPreviousBlockMedia = false ) - val actual = block.toPictureView(id, urlBuilder, indent, mode, false, ThemeColor.DEFAULT, false, emptyList()) + val actual = block.toPictureView(objectId, id, urlBuilder, indent, mode, false, ThemeColor.DEFAULT, false, emptyList(), Block.Details(), fieldParser) assertEquals(expected, actual) } @@ -238,19 +268,20 @@ class MapperExtensionKtTest { val type = Block.Content.File.Type.IMAGE val mode = BlockView.Mode.EDIT - val block = Block.Content.File( + val block = StubFile( state = state, type = type, targetObjectId = targetObjectId - ) + ).content as Block.Content.File val expected = BlockView.Error.Picture( id = id, indent = indent, - decorations = emptyList() + decorations = emptyList(), + name = block.name ) - val actual = block.toPictureView(id, urlBuilder, indent, mode, false, ThemeColor.DEFAULT, false, emptyList()) + val actual = block.toPictureView(objectId, id, urlBuilder, indent, mode, false, ThemeColor.DEFAULT, false, emptyList(), Block.Details(), fieldParser) assertEquals(expected, actual) } @@ -266,14 +297,14 @@ class MapperExtensionKtTest { val type = Block.Content.File.Type.IMAGE val mode = BlockView.Mode.EDIT - val block = Block.Content.File( + val block = StubFile( state = state, type = type, targetObjectId = targetObjectId - ) + ).content as Block.Content.File val expected = BlockView.Upload.Picture(id = id, indent = indent) - val actual = block.toPictureView(id, urlBuilder, indent, mode, false, ThemeColor.DEFAULT, false, emptyList()) + val actual = block.toPictureView(objectId, id, urlBuilder, indent, mode, false, ThemeColor.DEFAULT, false, emptyList(), Block.Details(), fieldParser) assertEquals(expected, actual) } @@ -293,16 +324,17 @@ class MapperExtensionKtTest { val type = Block.Content.File.Type.VIDEO val mode = BlockView.Mode.EDIT - val block = Block.Content.File( - targetObjectId = targetObjectId, + val block = StubFile( state = state, - type = type - ) + type = type, + targetObjectId = targetObjectId + ).content as Block.Content.File val details = Block.Details( mapOf( targetObjectId to Block.Fields( mapOf( + Relations.ID to targetObjectId, Relations.NAME to name, Relations.SIZE_IN_BYTES to 10000.0, Relations.FILE_MIME_TYPE to mime, @@ -322,7 +354,7 @@ class MapperExtensionKtTest { decorations = emptyList() ) - val actual = block.toVideoView(id, urlBuilder, indent, mode, false, ThemeColor.DEFAULT, false, emptyList(), details) + val actual = block.toVideoView(objectId, id, urlBuilder, indent, mode, false, ThemeColor.DEFAULT, false, emptyList(), details, fieldParser) assertEquals(expected, actual) } @@ -338,11 +370,11 @@ class MapperExtensionKtTest { val type = Block.Content.File.Type.VIDEO val mode = BlockView.Mode.EDIT - val block = Block.Content.File( - targetObjectId = null, + val block = StubFile( state = state, - type = type - ) + type = type, + targetObjectId = targetObjectId + ).content as Block.Content.File val expected = BlockView.Error.Video( id = id, @@ -350,7 +382,7 @@ class MapperExtensionKtTest { decorations = emptyList() ) - val actual = block.toVideoView(id, urlBuilder, indent, mode, false, ThemeColor.DEFAULT, false, emptyList()) + val actual = block.toVideoView(objectId, id, urlBuilder, indent, mode, false, ThemeColor.DEFAULT, false, emptyList(), Block.Details(), fieldParser) assertEquals(expected, actual) } @@ -366,11 +398,11 @@ class MapperExtensionKtTest { val type = Block.Content.File.Type.IMAGE val mode = BlockView.Mode.EDIT - val block = Block.Content.File( - targetObjectId = null, + val block = StubFile( state = state, - type = type - ) + type = type, + targetObjectId = "" + ).content as Block.Content.File val expected = BlockView.Error.Picture( id = id, @@ -378,7 +410,7 @@ class MapperExtensionKtTest { decorations = emptyList() ) - val actual = block.toPictureView(id, urlBuilder, indent, mode, false, ThemeColor.DEFAULT, false, emptyList()) + val actual = block.toPictureView(objectId, id, urlBuilder, indent, mode, false, ThemeColor.DEFAULT, false, emptyList(), Block.Details(), fieldParser) assertEquals(expected, actual) } @@ -394,11 +426,11 @@ class MapperExtensionKtTest { val type = Block.Content.File.Type.FILE val mode = BlockView.Mode.EDIT - val block = Block.Content.File( - targetObjectId = null, + val block = StubFile( state = state, - type = type - ) + type = type, + targetObjectId = "" + ).content as Block.Content.File val expected = BlockView.Error.File( id = id, @@ -406,7 +438,7 @@ class MapperExtensionKtTest { decorations = emptyList() ) - val actual = block.toFileView(id, urlBuilder, indent, mode, false, ThemeColor.DEFAULT, false, emptyList()) + val actual = block.toFileView(objectId, id, urlBuilder, indent, mode, false, ThemeColor.DEFAULT, false, emptyList(), Block.Details(), fieldParser) assertEquals(expected, actual) } @@ -422,11 +454,11 @@ class MapperExtensionKtTest { val type = Block.Content.File.Type.PDF val mode = BlockView.Mode.EDIT - val block = Block.Content.File( - targetObjectId = null, + val block = StubFile( state = state, - type = type - ) + type = type, + targetObjectId = "" + ).content as Block.Content.File val expected = BlockView.Error.File( id = id, @@ -434,7 +466,7 @@ class MapperExtensionKtTest { decorations = emptyList() ) - val actual = block.toFileView(id, urlBuilder, indent, mode, false, ThemeColor.DEFAULT, false, emptyList()) + val actual = block.toFileView(objectId, id, urlBuilder, indent, mode, false, ThemeColor.DEFAULT, false, emptyList(), Block.Details(), fieldParser) assertEquals(expected, actual) } @@ -450,14 +482,11 @@ class MapperExtensionKtTest { val type = Block.Content.File.Type.VIDEO val mode = BlockView.Mode.EDIT - val block = Block.Content.File( - name = null, - size = null, - mime = null, - targetObjectId = null, + val block = StubFile( state = state, - type = type - ) + type = type, + targetObjectId = "" + ).content as Block.Content.File val expected = BlockView.MediaPlaceholder.Video( id = id, @@ -465,7 +494,7 @@ class MapperExtensionKtTest { isPreviousBlockMedia = false ) - val actual = block.toVideoView(id, urlBuilder, indent, mode, false, ThemeColor.DEFAULT, false, emptyList()) + val actual = block.toVideoView(objectId, id, urlBuilder, indent, mode, false, ThemeColor.DEFAULT, false, emptyList(), Block.Details(), fieldParser) assertEquals(expected, actual) } @@ -481,21 +510,18 @@ class MapperExtensionKtTest { val type = Block.Content.File.Type.VIDEO val mode = BlockView.Mode.EDIT - val block = Block.Content.File( - name = null, - size = null, - mime = null, - targetObjectId = null, + val block = StubFile( state = state, - type = type - ) + type = type, + targetObjectId = "" + ).content as Block.Content.File val expected = BlockView.Upload.Video( id = id, indent = indent ) - val actual = block.toVideoView(id, urlBuilder, indent, mode, false, ThemeColor.DEFAULT, false, emptyList()) + val actual = block.toVideoView(objectId, id, urlBuilder, indent, mode, false, ThemeColor.DEFAULT, false, emptyList(), Block.Details(), fieldParser) assertEquals(expected, actual) } @@ -511,48 +537,24 @@ class MapperExtensionKtTest { val type = Block.Content.File.Type.VIDEO val mode = BlockView.Mode.EDIT - val block = Block.Content.File( - name = null, - size = null, - mime = null, - targetObjectId = null, + val block = StubFile( state = state, - type = type - ) + type = type, + targetObjectId = "" + ).content as Block.Content.File val expected = BlockView.Error.Video( id = id, indent = indent, - decorations = emptyList() + decorations = emptyList(), + name = block.name ) - val actual = block.toVideoView(id, urlBuilder, indent, mode, false, ThemeColor.DEFAULT, false, emptyList()) + val actual = block.toVideoView(objectId, id, urlBuilder, indent, mode, false, ThemeColor.DEFAULT, false, emptyList(), Block.Details(), fieldParser) assertEquals(expected, actual) } - @Test(expected = IllegalStateException::class) - fun `should throw exceptions when state not set`() { - - val id = MockDataFactory.randomUuid() - - val indent = MockDataFactory.randomInt() - - val type = Block.Content.File.Type.VIDEO - val mode = BlockView.Mode.EDIT - - val block = Block.Content.File( - name = null, - size = null, - mime = null, - targetObjectId = null, - state = null, - type = type - ) - - block.toVideoView(id, urlBuilder, indent, mode, false, ThemeColor.DEFAULT, false, emptyList()) - } - @Test fun `should not return mark when range from is equal text length`() { // SETUP From a979f1a4dfbb50558c83ac1e27f0dc4c72871acc Mon Sep 17 00:00:00 2001 From: konstantiniiv Date: Mon, 23 Dec 2024 10:30:39 +0100 Subject: [PATCH 13/23] DROID-3185 tests --- .../features/editor/SlashWidgetTesting.kt | 34 ++++++------------- .../anytype/domain/ext/BlockExtensionTest.kt | 15 ++------ .../anytype/presentation/MockBlockFactory.kt | 14 -------- 3 files changed, 13 insertions(+), 50 deletions(-) diff --git a/app/src/androidTest/java/com/anytypeio/anytype/features/editor/SlashWidgetTesting.kt b/app/src/androidTest/java/com/anytypeio/anytype/features/editor/SlashWidgetTesting.kt index 5a1e02b1c0..2f7a0a8ac5 100644 --- a/app/src/androidTest/java/com/anytypeio/anytype/features/editor/SlashWidgetTesting.kt +++ b/app/src/androidTest/java/com/anytypeio/anytype/features/editor/SlashWidgetTesting.kt @@ -19,6 +19,7 @@ import com.anytypeio.anytype.core_models.Position import com.anytypeio.anytype.core_models.Relation import com.anytypeio.anytype.core_models.Relations import com.anytypeio.anytype.core_models.StubBookmark +import com.anytypeio.anytype.core_models.StubFile import com.anytypeio.anytype.core_ui.features.editor.slash.holders.MainMenuHolder import com.anytypeio.anytype.core_ui.features.editor.slash.holders.MediaMenuHolder import com.anytypeio.anytype.domain.block.interactor.CreateBlock @@ -526,14 +527,9 @@ class SlashWidgetTesting : EditorTestSetup() { val paragraph = paragraph(text = "FooBar") val paragraph2 = paragraph(text = "Second") - val file = Block( - id = MockDataFactory.randomUuid(), - fields = Block.Fields.empty(), - children = emptyList(), - content = Block.Content.File( - type = Block.Content.File.Type.FILE, - state = Block.Content.File.State.EMPTY - ) + val file = StubFile( + type = Block.Content.File.Type.FILE, + state = Block.Content.File.State.EMPTY ) val page = Block( @@ -599,14 +595,9 @@ class SlashWidgetTesting : EditorTestSetup() { val paragraph = paragraph(text = "FooBar") val paragraph2 = paragraph(text = "Second") - val picture = Block( - id = MockDataFactory.randomUuid(), - fields = Block.Fields.empty(), - children = emptyList(), - content = Block.Content.File( - type = Block.Content.File.Type.IMAGE, - state = Block.Content.File.State.EMPTY - ) + val picture = StubFile( + type = Block.Content.File.Type.IMAGE, + state = Block.Content.File.State.EMPTY ) val page = Block( @@ -678,14 +669,9 @@ class SlashWidgetTesting : EditorTestSetup() { val paragraph2 = paragraph(text = "Second") - val video = Block( - id = MockDataFactory.randomUuid(), - fields = Block.Fields.empty(), - children = emptyList(), - content = Block.Content.File( - type = Block.Content.File.Type.VIDEO, - state = Block.Content.File.State.EMPTY - ) + val video = StubFile( + type = Block.Content.File.Type.VIDEO, + state = Block.Content.File.State.EMPTY ) val page = Block( diff --git a/domain/src/test/java/com/anytypeio/anytype/domain/ext/BlockExtensionTest.kt b/domain/src/test/java/com/anytypeio/anytype/domain/ext/BlockExtensionTest.kt index b925849b3c..888a879053 100644 --- a/domain/src/test/java/com/anytypeio/anytype/domain/ext/BlockExtensionTest.kt +++ b/domain/src/test/java/com/anytypeio/anytype/domain/ext/BlockExtensionTest.kt @@ -1,6 +1,7 @@ package com.anytypeio.anytype.domain.ext import com.anytypeio.anytype.core_models.Block +import com.anytypeio.anytype.core_models.StubFile import com.anytypeio.anytype.core_models.ext.asMap import com.anytypeio.anytype.core_models.ext.asRender import com.anytypeio.anytype.core_models.ext.getChildrenIdsList @@ -1096,19 +1097,9 @@ class BlockExtensionTest { val root = MockDataFactory.randomUuid() - val a = Block( - id = MockDataFactory.randomUuid(), - fields = Block.Fields.empty(), - children = emptyList(), - content = Block.Content.File() - ) + val a = StubFile() - val b = Block( - id = MockDataFactory.randomUuid(), - fields = Block.Fields.empty(), - children = emptyList(), - content = Block.Content.File() - ) + val b = StubFile() val document = listOf(a, b) diff --git a/test/core-models-stub/src/main/java/com/anytypeio/anytype/presentation/MockBlockFactory.kt b/test/core-models-stub/src/main/java/com/anytypeio/anytype/presentation/MockBlockFactory.kt index 80755bf310..3b051be671 100644 --- a/test/core-models-stub/src/main/java/com/anytypeio/anytype/presentation/MockBlockFactory.kt +++ b/test/core-models-stub/src/main/java/com/anytypeio/anytype/presentation/MockBlockFactory.kt @@ -84,20 +84,6 @@ object MockBlockFactory { ) ) - fun makeFileBlock(): Block = Block( - id = MockDataFactory.randomUuid(), - fields = Block.Fields(emptyMap()), - content = Block.Content.File( - targetObjectId = MockDataFactory.randomUuid(), - name = MockDataFactory.randomString(), - state = Block.Content.File.State.DONE, - mime = MockDataFactory.randomString(), - size = MockDataFactory.randomLong(), - type = Block.Content.File.Type.FILE - ), - children = emptyList() - ) - fun makeTitleBlock(): Block = Block( id = MockDataFactory.randomUuid(), fields = Block.Fields(emptyMap()), From 90b20174e748b77841813473fe3ebc09676961a2 Mon Sep 17 00:00:00 2001 From: konstantiniiv Date: Mon, 23 Dec 2024 10:31:28 +0100 Subject: [PATCH 14/23] DROID-3185 file block --- .../com/anytypeio/anytype/core_models/Block.kt | 14 ++++++++------ .../middleware/mappers/ToCoreModelMappers.kt | 3 ++- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/core-models/src/main/java/com/anytypeio/anytype/core_models/Block.kt b/core-models/src/main/java/com/anytypeio/anytype/core_models/Block.kt index c21a8ac31e..79c1ba1713 100644 --- a/core-models/src/main/java/com/anytypeio/anytype/core_models/Block.kt +++ b/core-models/src/main/java/com/anytypeio/anytype/core_models/Block.kt @@ -86,6 +86,7 @@ data class Block( fun asText() = this as Text fun asLink() = this as Link + fun asFile() = this as File /** * Smart block. @@ -227,12 +228,13 @@ data class Block( * @property state file state */ data class File( - val targetObjectId: Id? = null, - val name: String? = null, - val mime: String? = null, - val size: Long? = null, - val type: Type? = null, - val state: State? = null + val targetObjectId: Id, + val name: String, + val mime: String, + val size: Long, + val type: Type, + val state: State, + val addedAt: Long ) : Content() { enum class Type { NONE, FILE, IMAGE, VIDEO, AUDIO, PDF } enum class State { EMPTY, UPLOADING, DONE, ERROR } diff --git a/middleware/src/main/java/com/anytypeio/anytype/middleware/mappers/ToCoreModelMappers.kt b/middleware/src/main/java/com/anytypeio/anytype/middleware/mappers/ToCoreModelMappers.kt index 0a7306c26b..5f426fef8c 100644 --- a/middleware/src/main/java/com/anytypeio/anytype/middleware/mappers/ToCoreModelMappers.kt +++ b/middleware/src/main/java/com/anytypeio/anytype/middleware/mappers/ToCoreModelMappers.kt @@ -376,7 +376,8 @@ fun MBlock.toCoreModelsFile(): Block.Content.File { mime = content.mime, size = content.size, type = content.type.toCoreModels(), - state = content.state.toCoreModels() + state = content.state.toCoreModels(), + addedAt = content.addedAt ) } From 0ff884ab8e0cc0bca351dc367160b04f139d7596 Mon Sep 17 00:00:00 2001 From: konstantiniiv Date: Mon, 23 Dec 2024 10:31:34 +0100 Subject: [PATCH 15/23] DROID-3185 stubs --- .../java/com/anytypeio/anytype/core_models/Block.kt | 10 +++++++--- .../java/com/anytypeio/anytype/core_models/Object.kt | 6 ++++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/test/core-models-stub/src/main/java/com/anytypeio/anytype/core_models/Block.kt b/test/core-models-stub/src/main/java/com/anytypeio/anytype/core_models/Block.kt index 2b16bf3bfd..f9f50eec4b 100644 --- a/test/core-models-stub/src/main/java/com/anytypeio/anytype/core_models/Block.kt +++ b/test/core-models-stub/src/main/java/com/anytypeio/anytype/core_models/Block.kt @@ -94,9 +94,11 @@ fun StubFile( backgroundColor: String? = null, targetObjectId: Id = MockDataFactory.randomString(), name: String = MockDataFactory.randomString(), + mime: String = MockDataFactory.randomString(), size: Long = MockDataFactory.randomLong(), - type: Block.Content.File.Type? = null, - state: Block.Content.File.State? = null, + type: Block.Content.File.Type = Block.Content.File.Type.FILE, + state: Block.Content.File.State = Block.Content.File.State.DONE, + addedAt: Long = MockDataFactory.randomLong() ) : Block = Block( id = id, children = children, @@ -107,7 +109,9 @@ fun StubFile( name = name, targetObjectId = targetObjectId, type = type, - state = state + state = state, + mime = mime, + addedAt = addedAt ) ) diff --git a/test/core-models-stub/src/main/java/com/anytypeio/anytype/core_models/Object.kt b/test/core-models-stub/src/main/java/com/anytypeio/anytype/core_models/Object.kt index a89ac80856..a4feed51cc 100644 --- a/test/core-models-stub/src/main/java/com/anytypeio/anytype/core_models/Object.kt +++ b/test/core-models-stub/src/main/java/com/anytypeio/anytype/core_models/Object.kt @@ -22,7 +22,8 @@ fun StubObject( isHidden: Boolean? = null, links: List = emptyList(), targetObjectType: Id? = null, - identity: Id? = null + identity: Id? = null, + fileExt: String? = null, ): ObjectWrapper.Basic = ObjectWrapper.Basic( map = mapOf( Relations.ID to id, @@ -40,7 +41,8 @@ fun StubObject( Relations.LINKS to links, Relations.TARGET_OBJECT_TYPE to targetObjectType, Relations.UNIQUE_KEY to uniqueKey, - Relations.IDENTITY to identity + Relations.IDENTITY to identity, + Relations.FILE_EXT to fileExt ) ) From de1cc453d30b8e2515883e762833279824d8f07a Mon Sep 17 00:00:00 2001 From: konstantiniiv Date: Mon, 23 Dec 2024 11:22:54 +0100 Subject: [PATCH 16/23] DROID-3185 tests --- .../editor/render/BlockViewRenderer.kt | 2 + .../editor/render/DefaultBlockViewRenderer.kt | 153 +++++------------- .../presentation/mapper/MapperExtension.kt | 136 +++++++--------- .../editor/file_layout/FileLayoutTest.kt | 92 +++++++++-- 4 files changed, 179 insertions(+), 204 deletions(-) diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/render/BlockViewRenderer.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/render/BlockViewRenderer.kt index c636943ab5..3def4602fa 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/render/BlockViewRenderer.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/render/BlockViewRenderer.kt @@ -15,12 +15,14 @@ interface BlockViewRenderer { /** * Ext. function for recursively converting map to flattened view data structure. + * @param context object id * @param root root block, from which rendering starts * @param focus id of the current focus * @param anchor id of the current anchor (current rendering node) * @param indent current indent at this rendering node. */ suspend fun Map>.render( + context: Id, mode: EditorMode = EditorMode.Edit, root: Block, focus: Editor.Focus, diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/render/DefaultBlockViewRenderer.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/render/DefaultBlockViewRenderer.kt index c99daf6952..8943c5f494 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/render/DefaultBlockViewRenderer.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/render/DefaultBlockViewRenderer.kt @@ -9,7 +9,6 @@ import com.anytypeio.anytype.core_models.ObjectTypeIds.BOOKMARK import com.anytypeio.anytype.core_models.ObjectWrapper import com.anytypeio.anytype.core_models.RelationLink import com.anytypeio.anytype.core_models.Relations -import com.anytypeio.anytype.core_models.SupportedLayouts import com.anytypeio.anytype.core_models.ThemeColor import com.anytypeio.anytype.core_models.ext.parseThemeTextColor import com.anytypeio.anytype.core_models.ext.textColor @@ -58,6 +57,7 @@ class DefaultBlockViewRenderer @Inject constructor( ) : BlockViewRenderer, ToggleStateHolder by toggleStateHolder { override suspend fun Map>.render( + context: Id, mode: EditorMode, root: Block, focus: Focus, @@ -119,6 +119,7 @@ class DefaultBlockViewRenderer @Inject constructor( if (block.children.isNotEmpty()) { result.addAll( render( + context = context, mode = mode, root = root, focus = focus, @@ -164,6 +165,7 @@ class DefaultBlockViewRenderer @Inject constructor( if (block.children.isNotEmpty()) { result.addAll( render( + context = context, mode = mode, root = root, focus = focus, @@ -201,6 +203,7 @@ class DefaultBlockViewRenderer @Inject constructor( if (toggleStateHolder.isToggled(block.id)) { result.addAll( render( + context = context, mode = mode, root = root, focus = focus, @@ -241,6 +244,7 @@ class DefaultBlockViewRenderer @Inject constructor( if (block.children.isNotEmpty()) { result.addAll( render( + context = context, mode = mode, root = root, focus = focus, @@ -281,6 +285,7 @@ class DefaultBlockViewRenderer @Inject constructor( if (block.children.isNotEmpty()) { result.addAll( render( + context = context, mode = mode, root = root, focus = focus, @@ -321,6 +326,7 @@ class DefaultBlockViewRenderer @Inject constructor( if (block.children.isNotEmpty()) { result.addAll( render( + context = context, mode = mode, root = root, focus = focus, @@ -364,6 +370,7 @@ class DefaultBlockViewRenderer @Inject constructor( if (block.children.isNotEmpty()) { result.addAll( render( + context = context, mode = mode, root = root, focus = focus, @@ -400,6 +407,7 @@ class DefaultBlockViewRenderer @Inject constructor( if (block.children.isNotEmpty()) { result.addAll( render( + context = context, mode = mode, root = root, focus = focus, @@ -458,6 +466,7 @@ class DefaultBlockViewRenderer @Inject constructor( if (block.children.isNotEmpty()) { result.addAll( render( + context = context, mode = mode, root = root, focus = focus, @@ -497,6 +506,7 @@ class DefaultBlockViewRenderer @Inject constructor( if (block.children.isNotEmpty()) { result.addAll( render( + context = context, mode = mode, root = root, focus = focus, @@ -541,6 +551,7 @@ class DefaultBlockViewRenderer @Inject constructor( if (block.children.isNotEmpty()) { result.addAll( render( + context = context, mode = mode, root = root, focus = focus, @@ -635,19 +646,19 @@ class DefaultBlockViewRenderer @Inject constructor( background = block.parseThemeBackgroundColor() ) ) - result.add( - file( - root = root, - mode = mode, - content = content, - block = block, - indent = indent, - selection = selection, - isPreviousBlockMedia = isPreviousBlockMedia, - schema = blockDecorationScheme, - details = details - ) + val fileBlock = file( + context = context, + mode = mode, + content = content, + block = block, + indent = indent, + selection = selection, + isPreviousBlockMedia = isPreviousBlockMedia, + schema = blockDecorationScheme, + details = details, + fieldParser = fieldParser ) + result.add(fileBlock) isPreviousBlockMedia = true } is Content.Layout -> { @@ -662,6 +673,7 @@ class DefaultBlockViewRenderer @Inject constructor( } result.addAll( render( + context = context, mode = mode, root = root, focus = focus, @@ -1372,7 +1384,7 @@ class DefaultBlockViewRenderer @Inject constructor( } private fun file( - root: Block, + context: Id, mode: EditorMode, content: Content.File, block: Block, @@ -1380,104 +1392,21 @@ class DefaultBlockViewRenderer @Inject constructor( selection: Set, isPreviousBlockMedia: Boolean, schema: NestedDecorationData, - details: Block.Details - ): BlockView = when (content.type) { - Content.File.Type.IMAGE -> content.toPictureView( - root = root, - blockId = block.id, - urlBuilder = urlBuilder, - indent = indent, - mode = if (mode == EditorMode.Edit) BlockView.Mode.EDIT else BlockView.Mode.READ, - isSelected = checkIfSelected( - mode = mode, - block = block, - selection = selection - ), - background = block.parseThemeBackgroundColor(), - isPreviousBlockMedia = isPreviousBlockMedia, - decorations = schema.toBlockViewDecoration(block), - details = details - ) - Content.File.Type.FILE -> content.toFileView( - root = root, - blockId = block.id, - urlBuilder = urlBuilder, - indent = indent, - mode = if (mode == EditorMode.Edit) BlockView.Mode.EDIT else BlockView.Mode.READ, - isSelected = checkIfSelected( - mode = mode, - block = block, - selection = selection - ), - background = block.parseThemeBackgroundColor(), - isPrevBlockMedia = isPreviousBlockMedia, - decorations = schema.toBlockViewDecoration(block), - details = details - ) - Content.File.Type.VIDEO -> content.toVideoView( - blockId = block.id, - urlBuilder = urlBuilder, - indent = indent, - mode = if (mode == EditorMode.Edit) BlockView.Mode.EDIT else BlockView.Mode.READ, - isSelected = checkIfSelected( - mode = mode, - block = block, - selection = selection - ), - background = block.parseThemeBackgroundColor(), - isPrevBlockMedia = isPreviousBlockMedia, - decorations = schema.toBlockViewDecoration(block), - details = details - ) - Content.File.Type.AUDIO -> content.toFileView( - root = root, - blockId = block.id, - urlBuilder = urlBuilder, - indent = indent, - mode = if (mode == EditorMode.Edit) BlockView.Mode.EDIT else BlockView.Mode.READ, - isSelected = checkIfSelected( - mode = mode, - block = block, - selection = selection - ), - background = block.parseThemeBackgroundColor(), - isPrevBlockMedia = isPreviousBlockMedia, - decorations = schema.toBlockViewDecoration(block), - details = details - ) - Content.File.Type.PDF -> content.toFileView( - root = root, - blockId = block.id, - urlBuilder = urlBuilder, - indent = indent, - mode = if (mode == EditorMode.Edit) BlockView.Mode.EDIT else BlockView.Mode.READ, - isSelected = checkIfSelected( - mode = mode, - block = block, - selection = selection - ), - background = block.parseThemeBackgroundColor(), - isPrevBlockMedia = isPreviousBlockMedia, - decorations = schema.toBlockViewDecoration(block), - details = details - ) - Content.File.Type.NONE -> content.toFileView( - root = root, - blockId = block.id, - urlBuilder = urlBuilder, - indent = indent, - mode = if (mode == EditorMode.Edit) BlockView.Mode.EDIT else BlockView.Mode.READ, - isSelected = checkIfSelected( - mode = mode, - block = block, - selection = selection - ), - background = block.parseThemeBackgroundColor(), - isPrevBlockMedia = isPreviousBlockMedia, - decorations = schema.toBlockViewDecoration(block), - details = details - ) - else -> throw IllegalStateException("Unexpected file type: ${content.type}") + details: Block.Details, + fieldParser: FieldParser + ): BlockView { + + val blockViewMode = + if (mode == EditorMode.Edit) BlockView.Mode.EDIT else BlockView.Mode.READ + val isSelected = checkIfSelected(mode, block, selection) + val background = block.parseThemeBackgroundColor() + val decorations = schema.toBlockViewDecoration(block) + + return when (content.type) { + Content.File.Type.IMAGE -> content.toPictureView(context, block.id, urlBuilder, indent, blockViewMode, isSelected, background, isPreviousBlockMedia, decorations, details, fieldParser) + Content.File.Type.VIDEO -> content.toVideoView(context, block.id, urlBuilder, indent, blockViewMode, isSelected, background, isPreviousBlockMedia, decorations, details, fieldParser) + else -> content.toFileView(context, block.id, urlBuilder, indent, blockViewMode, isSelected, background, isPreviousBlockMedia, decorations, details, fieldParser) + } } private fun title( diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/mapper/MapperExtension.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/mapper/MapperExtension.kt index 7e6cb0242d..684aed85ae 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/mapper/MapperExtension.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/mapper/MapperExtension.kt @@ -9,6 +9,7 @@ import com.anytypeio.anytype.core_models.ObjectType import com.anytypeio.anytype.core_models.ObjectWrapper import com.anytypeio.anytype.core_models.RelationFormat import com.anytypeio.anytype.core_models.Relations +import com.anytypeio.anytype.core_models.SupportedLayouts import com.anytypeio.anytype.core_models.ThemeColor import com.anytypeio.anytype.domain.config.DebugSettings import com.anytypeio.anytype.domain.misc.UrlBuilder @@ -31,7 +32,7 @@ import com.anytypeio.anytype.presentation.templates.TemplateObjectTypeView import timber.log.Timber fun Block.Content.File.toPictureView( - root: Block, + context: Id, blockId: String, urlBuilder: UrlBuilder, indent: Int, @@ -40,7 +41,8 @@ fun Block.Content.File.toPictureView( background: ThemeColor, isPreviousBlockMedia: Boolean, decorations: List, - details: Block.Details = Block.Details() + details: Block.Details = Block.Details(), + fieldParser: FieldParser ): BlockView = when (state) { Block.Content.File.State.EMPTY -> BlockView.MediaPlaceholder.Picture( id = blockId, @@ -60,21 +62,21 @@ fun Block.Content.File.toPictureView( decorations = decorations ) Block.Content.File.State.DONE -> { + val url = urlBuilder.getUrlForFileContent(this) - val targetId = this.targetObjectId - val struct = details.details[targetId]?.map - val currentObject = ObjectWrapper.Basic(details.details[root.id]?.map.orEmpty()) - if (url != null && targetId != null) { - val targetObject = ObjectWrapper.File(struct.orEmpty()) + val currentObject = ObjectWrapper.Basic(details.details[context]?.map.orEmpty()) + val targetObject = ObjectWrapper.Basic(details.details[targetObjectId]?.map.orEmpty()) + + if (url != null && targetObject.isValid && targetObject.notDeletedNorArchived) { if (currentObject.layout == ObjectType.Layout.IMAGE) { BlockView.OpenFile.Image( id = blockId, - targetId = targetId + targetId = targetObjectId ) } else { BlockView.Media.Picture( id = blockId, - targetObjectId = targetId, + targetObjectId = targetObjectId, url = url, indent = indent, mode = mode, @@ -82,7 +84,7 @@ fun Block.Content.File.toPictureView( background = background, decorations = decorations, size = targetObject.sizeInBytes?.toLong(), - name = targetObject.name, + name = fieldParser.getObjectName(targetObject), mime = targetObject.fileMimeType ) } @@ -107,10 +109,10 @@ fun Block.Content.File.toPictureView( decorations = decorations, name = name ) - else -> throw IllegalStateException("Unexpected state: $state") } fun Block.Content.File.toVideoView( + context: Id, blockId: Id, urlBuilder: UrlBuilder, indent: Int, @@ -119,7 +121,8 @@ fun Block.Content.File.toVideoView( background: ThemeColor, isPrevBlockMedia: Boolean, decorations: List, - details: Block.Details = Block.Details() + details: Block.Details = Block.Details(), + fieldParser: FieldParser ): BlockView = when (state) { Block.Content.File.State.EMPTY -> BlockView.MediaPlaceholder.Video( id = blockId, @@ -139,25 +142,32 @@ fun Block.Content.File.toVideoView( 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 - ) + val currentObject = ObjectWrapper.Basic(details.details[context]?.map.orEmpty()) + val targetObject = ObjectWrapper.Basic(details.details[targetObjectId]?.map.orEmpty()) + if (url != null && targetObject.isValid && targetObject.notDeletedNorArchived) { + if (currentObject.layout == ObjectType.Layout.VIDEO) { + BlockView.OpenFile.File( + id = blockId, + targetId = targetObjectId + ) + } else { + BlockView.Media.Video( + id = blockId, + targetObjectId = targetObjectId, + url = url, + indent = indent, + mode = mode, + isSelected = isSelected, + background = background, + decorations = decorations, + size = targetObject.sizeInBytes?.toLong(), + name = fieldParser.getObjectName(targetObject), + mime = targetObject.fileMimeType + ) + } } else { Timber.w("Could not build video view for block $blockId") BlockView.Error.Video( @@ -179,11 +189,10 @@ fun Block.Content.File.toVideoView( decorations = decorations, name = name ) - else -> throw IllegalStateException("Unexpected state: $state") } fun Block.Content.File.toFileView( - root: Block, + context: Id, blockId: String, urlBuilder: UrlBuilder, indent: Int, @@ -192,7 +201,8 @@ fun Block.Content.File.toFileView( background: ThemeColor, isPrevBlockMedia: Boolean, decorations: List, - details: Block.Details = Block.Details() + details: Block.Details = Block.Details(), + fieldParser: FieldParser ): BlockView = when (state) { Block.Content.File.State.EMPTY -> BlockView.MediaPlaceholder.File( id = blockId, @@ -212,55 +222,32 @@ fun Block.Content.File.toFileView( decorations = decorations ) Block.Content.File.State.DONE -> { - val currentObject = ObjectWrapper.Basic(details.details[root.id]?.map.orEmpty()) + val url = urlBuilder.getUrlForFileContent(this) - val targetId = this.targetObjectId - val struct = details.details[targetId]?.map - if (url != null && targetId != null) { - if (struct.isNullOrEmpty()) { - BlockView.Upload.File( + val currentObject = ObjectWrapper.Basic(details.details[context]?.map.orEmpty()) + val targetObject = ObjectWrapper.Basic(details.details[targetObjectId]?.map.orEmpty()) + + if (url != null && targetObject.isValid && targetObject.notDeletedNorArchived) { + if (SupportedLayouts.fileLayouts.contains(currentObject.layout)) { + BlockView.OpenFile.File( id = blockId, + targetId = targetObjectId + ) + } else { + BlockView.Media.File( + id = blockId, + targetObjectId = targetObjectId, + url = url, indent = indent, mode = mode, isSelected = isSelected, background = background, - decorations = decorations + decorations = decorations, + size = targetObject.sizeInBytes?.toLong(), + name = fieldParser.getObjectName(targetObject), + mime = targetObject.fileMimeType, + fileExt = targetObject.fileExt ) - } else { - val targetObject = ObjectWrapper.File(struct) - when (currentObject.layout) { - ObjectType.Layout.IMAGE -> { - BlockView.OpenFile.Image( - id = blockId, - targetId = targetId - ) - } - ObjectType.Layout.FILE, - ObjectType.Layout.VIDEO, - ObjectType.Layout.AUDIO, - ObjectType.Layout.PDF -> { - BlockView.OpenFile.File( - id = blockId, - targetId = targetId - ) - } - else -> { - 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") @@ -283,7 +270,6 @@ fun Block.Content.File.toFileView( decorations = decorations, name = name ) - else -> throw IllegalStateException("Unexpected state: $state") } fun Block.Align.toView(): Alignment = when (this) { diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/file_layout/FileLayoutTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/file_layout/FileLayoutTest.kt index 08fa61469c..1736d8b565 100644 --- a/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/file_layout/FileLayoutTest.kt +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/file_layout/FileLayoutTest.kt @@ -1,5 +1,7 @@ package com.anytypeio.anytype.presentation.editor.editor.file_layout +import android.net.Uri +import android.os.Build import android.util.Log import androidx.arch.core.executor.testing.InstantTaskExecutorRule import com.anytypeio.anytype.core_models.Block @@ -10,9 +12,11 @@ import com.anytypeio.anytype.core_models.StubObject import com.anytypeio.anytype.core_models.StubSmartBlock import com.anytypeio.anytype.core_models.StubTitle import com.anytypeio.anytype.core_models.ext.content +import com.anytypeio.anytype.domain.base.Resultat import com.anytypeio.anytype.presentation.editor.editor.EditorPresentationTestSetup import com.anytypeio.anytype.presentation.editor.editor.ViewState import com.anytypeio.anytype.presentation.editor.editor.model.BlockView +import com.anytypeio.anytype.presentation.objects.ObjectIcon import com.anytypeio.anytype.presentation.util.DefaultCoroutineTestRule import com.anytypeio.anytype.presentation.util.TXT import com.anytypeio.anytype.presentation.util.downloader.MiddlewareShareDownloader @@ -27,12 +31,19 @@ import net.lachlanmckee.timberjunit.TimberTestRule import org.junit.Before import org.junit.Rule import org.junit.Test +import org.junit.runner.RunWith import org.mockito.MockitoAnnotations +import org.mockito.kotlin.doReturn import org.mockito.kotlin.eq +import org.mockito.kotlin.stub import org.mockito.kotlin.times import org.mockito.kotlin.verify +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config @OptIn(ExperimentalCoroutinesApi::class) +@Config(sdk = [Build.VERSION_CODES.P]) +@RunWith(RobolectricTestRunner::class) class FileLayoutTest : EditorPresentationTestSetup() { @get:Rule @@ -50,16 +61,6 @@ class FileLayoutTest : EditorPresentationTestSetup() { @get:Rule val coroutineTestRule = DefaultCoroutineTestRule() - val title = StubTitle() - val header = StubHeader(children = listOf(title.id)) - val fileBlock = StubFile( - id = "fileBlockId-${MockDataFactory.randomUuid()}", - targetObjectId = root, - state = Block.Content.File.State.DONE - ) - val page = StubSmartBlock(id = root, children = listOf(header.id, fileBlock.id)) - val document = listOf(page, header, title, fileBlock) - @Before fun setup() { MockitoAnnotations.openMocks(this) @@ -70,12 +71,25 @@ class FileLayoutTest : EditorPresentationTestSetup() { @Test fun `should change file block to file open block`() = runTest { + val title = StubTitle() + val header = StubHeader(children = listOf(title.id)) + val fileBlock = StubFile( + id = "fileBlockId-${MockDataFactory.randomUuid()}", + targetObjectId = root, + state = Block.Content.File.State.DONE, + type = Block.Content.File.Type.FILE + ) + val page = StubSmartBlock(id = root, children = listOf(header.id, fileBlock.id)) + val document = listOf(page, header, title, fileBlock) + + val fileExt = "pdf" + val fileObject = StubObject( id = root, space = defaultSpace, name = "fileObjectName-${RandomString.make(5)}", layout = ObjectType.Layout.FILE.code.toDouble(), - fileExt = "pdf" + fileExt = fileExt ) val detailsList = Block.Details( @@ -100,10 +114,16 @@ class FileLayoutTest : EditorPresentationTestSetup() { val firstTimeExpected = ViewState.Success( listOf( - BlockView.Title.Basic( + BlockView.Title.File( + mode = BlockView.Mode.EDIT, isFocused = false, id = title.id, - text = title.content().text + text = title.content().text, + icon = ObjectIcon.File( + mime = fileObject.fileMimeType, + fileName = fileObject.name, + extensions = fileExt + ) ), BlockView.OpenFile.File( id = fileBlock.id, @@ -119,12 +139,25 @@ class FileLayoutTest : EditorPresentationTestSetup() { @Test fun `should change file block to image open block`() = runTest { + val title = StubTitle() + val header = StubHeader(children = listOf(title.id)) + val fileBlock = StubFile( + id = "fileBlockId-${MockDataFactory.randomUuid()}", + targetObjectId = root, + state = Block.Content.File.State.DONE, + type = Block.Content.File.Type.IMAGE + ) + val page = StubSmartBlock(id = root, children = listOf(header.id, fileBlock.id)) + val document = listOf(page, header, title, fileBlock) + + val fileExt = "jpg" + val fileObject = StubObject( id = root, space = defaultSpace, - name = "fileObjectName-${RandomString.make(5)}", + name = "fileObjectImageName-${RandomString.make(5)}", layout = ObjectType.Layout.IMAGE.code.toDouble(), - fileExt = "jpg" + fileExt = fileExt ) val detailsList = Block.Details( @@ -170,6 +203,17 @@ class FileLayoutTest : EditorPresentationTestSetup() { @Test fun `should start sharing file on open file click`() = runTest { + val title = StubTitle() + val header = StubHeader(children = listOf(title.id)) + val fileBlock = StubFile( + id = "fileBlockId-${MockDataFactory.randomUuid()}", + targetObjectId = root, + state = Block.Content.File.State.DONE, + type = Block.Content.File.Type.FILE + ) + val page = StubSmartBlock(id = root, children = listOf(header.id, fileBlock.id)) + val document = listOf(page, header, title, fileBlock) + val fileObject = StubObject( id = root, space = defaultSpace, @@ -189,6 +233,22 @@ class FileLayoutTest : EditorPresentationTestSetup() { details = detailsList ) + val objName = fieldParser.getObjectName(fileObject) + + val downloadParams = MiddlewareShareDownloader.Params( + objectId = root, + name = objName + ) + + documentFileShareDownloader.stub { + onBlocking { async(downloadParams) } doReturn Resultat.success( + MiddlewareShareDownloader.Response( + Uri.EMPTY, + "" + ) + ) + } + val vm = buildViewModel() advanceUntilIdle() vm.onStart(id = fileObject.id, space = defaultSpace) @@ -197,8 +257,6 @@ class FileLayoutTest : EditorPresentationTestSetup() { vm.startSharingFile(id = fileBlock.id) advanceUntilIdle() - val objName = fieldParser.getObjectName(fileObject) - verify(documentFileShareDownloader, times(1)).async( params = eq( MiddlewareShareDownloader.Params( From 85e0dd5357c443936761dcfb5ffd63eee87e54ec Mon Sep 17 00:00:00 2001 From: konstantiniiv Date: Mon, 23 Dec 2024 11:23:04 +0100 Subject: [PATCH 17/23] DROID-3185 fixes --- .../anytype/core_ui/features/editor/holders/upload/OpenFile.kt | 2 +- .../anytype/presentation/history/VersionHistoryViewModel.kt | 2 ++ .../anytype/presentation/templates/TemplateBlankViewModel.kt | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/upload/OpenFile.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/upload/OpenFile.kt index 3f82eab123..e2e04d55fc 100644 --- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/upload/OpenFile.kt +++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/upload/OpenFile.kt @@ -16,7 +16,7 @@ class OpenFile( root.setOnClickListener { click( ListenerType.File.View( - target = item.targetId, + target = item.id, ) ) } diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/history/VersionHistoryViewModel.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/history/VersionHistoryViewModel.kt index 5f041d1f6f..c05a74a52c 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/history/VersionHistoryViewModel.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/history/VersionHistoryViewModel.kt @@ -529,6 +529,7 @@ class VersionHistoryViewModel( defaultPayloadConsumer(payload) val root = event.blocks.first { it.id == vmParams.objectId } val blocks = event.blocks.asMap().render( + context = obj.id, mode = Mode.Read, root = root, focus = Editor.Focus.empty(), @@ -566,6 +567,7 @@ class VersionHistoryViewModel( } else { val root = event.blocks.first { it.id == vmParams.objectId } val blocks = event.blocks.asMap().render( + context = obj.id, mode = Mode.Read, root = root, focus = Editor.Focus.empty(), diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/templates/TemplateBlankViewModel.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/templates/TemplateBlankViewModel.kt index 1ce3ee21c8..9c96408b70 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/templates/TemplateBlankViewModel.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/templates/TemplateBlankViewModel.kt @@ -94,6 +94,7 @@ class TemplateBlankViewModel( viewModelScope.launch { state.value = page.asMap().render( + context = DEFAULT_TEMPLATE_ID_BLANK, mode = Editor.Mode.Read, root = page.first(), focus = com.anytypeio.anytype.domain.editor.Editor.Focus.empty(), From 92449f8acdebd905f7934abb1281d18121b2960a Mon Sep 17 00:00:00 2001 From: konstantiniiv Date: Mon, 23 Dec 2024 11:25:12 +0100 Subject: [PATCH 18/23] DROID-3185 fix open file block --- .../anytype/presentation/editor/EditorViewModel.kt | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModel.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModel.kt index 0312c5b9cb..43aa45fb56 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModel.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModel.kt @@ -783,6 +783,7 @@ class EditorViewModel( val flags = mutableListOf() Timber.d("Rendering starting...") val doc = models.asMap().render( + context = context, mode = mode, root = root, focus = focus, @@ -4311,16 +4312,17 @@ class EditorViewModel( sendToast("Downloading file in background...") + val fileBlockView = (views.find { it.id == blockId } as? BlockView.Media.File) + val fileBlock = blocks.firstOrNull { it.id == blockId } val fileContent = fileBlock?.content as? Content.File - val url = urlBuilder.getUrlForFileBlock(fileBlock) - - if (fileContent != null && url != null) { + + if (fileBlockView != null && fileContent != null) { viewModelScope.launch { orchestrator.proxies.intents.send( Media.DownloadFile( - url = url, - name = fileContent.name.orEmpty(), + url = fileBlockView.url, + name = fileBlockView.name.orEmpty(), type = fileContent.type ) ) From dabad8f8337c7af9ed48cee0853f5789e84d2fe6 Mon Sep 17 00:00:00 2001 From: konstantiniiv Date: Mon, 23 Dec 2024 11:26:22 +0100 Subject: [PATCH 19/23] DROID-3185 move tests --- .../editor/file_layout/FileLayoutTest.kt | 269 ------------------ 1 file changed, 269 deletions(-) delete mode 100644 presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/file_layout/FileLayoutTest.kt diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/file_layout/FileLayoutTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/file_layout/FileLayoutTest.kt deleted file mode 100644 index 1736d8b565..0000000000 --- a/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/file_layout/FileLayoutTest.kt +++ /dev/null @@ -1,269 +0,0 @@ -package com.anytypeio.anytype.presentation.editor.editor.file_layout - -import android.net.Uri -import android.os.Build -import android.util.Log -import androidx.arch.core.executor.testing.InstantTaskExecutorRule -import com.anytypeio.anytype.core_models.Block -import com.anytypeio.anytype.core_models.ObjectType -import com.anytypeio.anytype.core_models.StubFile -import com.anytypeio.anytype.core_models.StubHeader -import com.anytypeio.anytype.core_models.StubObject -import com.anytypeio.anytype.core_models.StubSmartBlock -import com.anytypeio.anytype.core_models.StubTitle -import com.anytypeio.anytype.core_models.ext.content -import com.anytypeio.anytype.domain.base.Resultat -import com.anytypeio.anytype.presentation.editor.editor.EditorPresentationTestSetup -import com.anytypeio.anytype.presentation.editor.editor.ViewState -import com.anytypeio.anytype.presentation.editor.editor.model.BlockView -import com.anytypeio.anytype.presentation.objects.ObjectIcon -import com.anytypeio.anytype.presentation.util.DefaultCoroutineTestRule -import com.anytypeio.anytype.presentation.util.TXT -import com.anytypeio.anytype.presentation.util.downloader.MiddlewareShareDownloader -import com.anytypeio.anytype.test_utils.MockDataFactory -import com.jraska.livedata.test -import kotlin.test.assertEquals -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.advanceUntilIdle -import kotlinx.coroutines.test.runTest -import net.bytebuddy.utility.RandomString -import net.lachlanmckee.timberjunit.TimberTestRule -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.MockitoAnnotations -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.eq -import org.mockito.kotlin.stub -import org.mockito.kotlin.times -import org.mockito.kotlin.verify -import org.robolectric.RobolectricTestRunner -import org.robolectric.annotation.Config - -@OptIn(ExperimentalCoroutinesApi::class) -@Config(sdk = [Build.VERSION_CODES.P]) -@RunWith(RobolectricTestRunner::class) -class FileLayoutTest : EditorPresentationTestSetup() { - - @get:Rule - val timberTestRule: TimberTestRule = TimberTestRule.builder() - .minPriority(Log.DEBUG) - .showThread(true) - .showTimestamp(false) - .onlyLogWhenTestFails(true) - .build() - - @get:Rule - val rule = InstantTaskExecutorRule() - - @OptIn(ExperimentalCoroutinesApi::class) - @get:Rule - val coroutineTestRule = DefaultCoroutineTestRule() - - @Before - fun setup() { - MockitoAnnotations.openMocks(this) - proceedWithDefaultBeforeTestStubbing() - } - - //region Editor view state - @Test - fun `should change file block to file open block`() = runTest { - - val title = StubTitle() - val header = StubHeader(children = listOf(title.id)) - val fileBlock = StubFile( - id = "fileBlockId-${MockDataFactory.randomUuid()}", - targetObjectId = root, - state = Block.Content.File.State.DONE, - type = Block.Content.File.Type.FILE - ) - val page = StubSmartBlock(id = root, children = listOf(header.id, fileBlock.id)) - val document = listOf(page, header, title, fileBlock) - - val fileExt = "pdf" - - val fileObject = StubObject( - id = root, - space = defaultSpace, - name = "fileObjectName-${RandomString.make(5)}", - layout = ObjectType.Layout.FILE.code.toDouble(), - fileExt = fileExt - ) - - val detailsList = Block.Details( - details = mapOf( - fileObject.id to Block.Fields(fileObject.map) - ) - ) - - stubOpenDocument( - document = document, - details = detailsList - ) - - val vm = buildViewModel() - advanceUntilIdle() - - vm.onStart(id = fileObject.id, space = defaultSpace) - val loadingState = vm.state.test() - assertEquals(ViewState.Loading, loadingState.value()) - - advanceUntilIdle() - - val firstTimeExpected = ViewState.Success( - listOf( - BlockView.Title.File( - mode = BlockView.Mode.EDIT, - isFocused = false, - id = title.id, - text = title.content().text, - icon = ObjectIcon.File( - mime = fileObject.fileMimeType, - fileName = fileObject.name, - extensions = fileExt - ) - ), - BlockView.OpenFile.File( - id = fileBlock.id, - targetId = fileObject.id - ) - ) - ) - - val pageState = vm.state.test() - assertEquals(firstTimeExpected, pageState.value()) - } - - @Test - fun `should change file block to image open block`() = runTest { - - val title = StubTitle() - val header = StubHeader(children = listOf(title.id)) - val fileBlock = StubFile( - id = "fileBlockId-${MockDataFactory.randomUuid()}", - targetObjectId = root, - state = Block.Content.File.State.DONE, - type = Block.Content.File.Type.IMAGE - ) - val page = StubSmartBlock(id = root, children = listOf(header.id, fileBlock.id)) - val document = listOf(page, header, title, fileBlock) - - val fileExt = "jpg" - - val fileObject = StubObject( - id = root, - space = defaultSpace, - name = "fileObjectImageName-${RandomString.make(5)}", - layout = ObjectType.Layout.IMAGE.code.toDouble(), - fileExt = fileExt - ) - - val detailsList = Block.Details( - details = mapOf( - fileObject.id to Block.Fields(fileObject.map) - ) - ) - - stubOpenDocument( - document = document, - details = detailsList - ) - - val vm = buildViewModel() - advanceUntilIdle() - - vm.onStart(id = fileObject.id, space = defaultSpace) - val loadingState = vm.state.test() - assertEquals(ViewState.Loading, loadingState.value()) - - advanceUntilIdle() - - val firstTimeExpected = ViewState.Success( - listOf( - BlockView.Title.Basic( - isFocused = false, - id = title.id, - text = title.content().text - ), - BlockView.OpenFile.Image( - id = fileBlock.id, - targetId = fileObject.id - ) - ) - ) - - val pageState = vm.state.test() - assertEquals(firstTimeExpected, pageState.value()) - } - //endregion - - //region Open file click test - @Test - fun `should start sharing file on open file click`() = runTest { - - val title = StubTitle() - val header = StubHeader(children = listOf(title.id)) - val fileBlock = StubFile( - id = "fileBlockId-${MockDataFactory.randomUuid()}", - targetObjectId = root, - state = Block.Content.File.State.DONE, - type = Block.Content.File.Type.FILE - ) - val page = StubSmartBlock(id = root, children = listOf(header.id, fileBlock.id)) - val document = listOf(page, header, title, fileBlock) - - val fileObject = StubObject( - id = root, - space = defaultSpace, - name = "fileObjectName-${RandomString.make(5)}", - layout = ObjectType.Layout.FILE.code.toDouble(), - fileExt = "pdf" - ) - - val detailsList = Block.Details( - details = mapOf( - fileObject.id to Block.Fields(fileObject.map) - ) - ) - - stubOpenDocument( - document = document, - details = detailsList - ) - - val objName = fieldParser.getObjectName(fileObject) - - val downloadParams = MiddlewareShareDownloader.Params( - objectId = root, - name = objName - ) - - documentFileShareDownloader.stub { - onBlocking { async(downloadParams) } doReturn Resultat.success( - MiddlewareShareDownloader.Response( - Uri.EMPTY, - "" - ) - ) - } - - val vm = buildViewModel() - advanceUntilIdle() - vm.onStart(id = fileObject.id, space = defaultSpace) - advanceUntilIdle() - - vm.startSharingFile(id = fileBlock.id) - advanceUntilIdle() - - verify(documentFileShareDownloader, times(1)).async( - params = eq( - MiddlewareShareDownloader.Params( - name = objName, - objectId = fileObject.id, - ) - ) - ) - } -} \ No newline at end of file From a86cca06109faaa35f6163ea843ce63b21b126d8 Mon Sep 17 00:00:00 2001 From: konstantiniiv Date: Mon, 23 Dec 2024 13:35:05 +0100 Subject: [PATCH 20/23] DROID-3185 ext files --- .../presentation/editor/EditorViewModel.kt | 78 ++++++------------- .../presentation/editor/editor/Command.kt | 3 +- .../presentation/extension/FileUrlExt.kt | 53 +++++++++++++ 3 files changed, 79 insertions(+), 55 deletions(-) diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModel.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModel.kt index 43aa45fb56..fb4442f10a 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModel.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModel.kt @@ -254,6 +254,8 @@ import com.anytypeio.anytype.presentation.editor.model.OnEditorDatePickerEvent.O import com.anytypeio.anytype.presentation.editor.model.OnEditorDatePickerEvent.OnDateSelected import com.anytypeio.anytype.presentation.editor.model.OnEditorDatePickerEvent.OnTodayClick import com.anytypeio.anytype.presentation.editor.model.OnEditorDatePickerEvent.OnTomorrowClick +import com.anytypeio.anytype.presentation.extension.getFileDetailsForBlock +import com.anytypeio.anytype.presentation.extension.getUrlForFileContent import com.anytypeio.anytype.presentation.objects.getCreateObjectParams import com.anytypeio.anytype.presentation.objects.getObjectTypeViewsForSBPage import com.anytypeio.anytype.presentation.objects.getProperType @@ -4245,54 +4247,21 @@ class EditorViewModel( } 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 = blockId, - uri = url - ) + dispatch( + Command.OpenFileByDefaultApp( + id = blockId ) - } else { - Timber.e("Block is not File or with wrong state, can't proceed with open") - sendToast("Something went wrong. Couldn't open file.") - } + ) } fun startSharingFile(id: String, onDownloaded: (Uri) -> Unit = {}) { - Timber.d("startSharingFile, fileBlockId: [$id]") + Timber.d("startSharingFile, fileBlockId: [$id]") sendToast("Preparing file to share...") - val block = blocks.firstOrNull { it.id == id } - if (block == null) { - Timber.e("No block found with id $id") - return - } - - val content = block.content - if (content !is Content.File || content.state != Content.File.State.DONE) { - Timber.e("Block content is not a file or is not in the DONE state; cannot proceed.") - return - } - - val targetObjectId = content.targetObjectId - if (targetObjectId == null) { - Timber.e("Target object ID is null; cannot proceed with file sharing.") - return - } - - val fileObject = orchestrator.stores.details.getAsObject(target = targetObjectId) - if (fileObject == null) { - Timber.e("Object with id $targetObjectId not found.") - return - } - - val fileName = fieldParser.getObjectName(fileObject) + val fileDetails = blocks.getFileDetailsForBlock(id, orchestrator, fieldParser) ?: return + val (content, targetObjectId, fileName) = fileDetails - Timber.d("startDownloadingFile, fileObjectId: [$targetObjectId], fileName: [$fileName]") + Timber.d("startSharingFile, fileObjectId: [$targetObjectId], fileName: [$fileName]") viewModelScope.launch { orchestrator.proxies.intents.send( @@ -4306,29 +4275,32 @@ class EditorViewModel( } } - fun startDownloadingFileFromBlock(blockId: Id) { - - Timber.d("startDownloadingFile, for block:[$blockId]") - + fun startDownloadingFileFromBlock(id: Id) { + Timber.d("startDownloadingFile, for block:[$id]") sendToast("Downloading file in background...") - val fileBlockView = (views.find { it.id == blockId } as? BlockView.Media.File) + val fileDetails = blocks.getFileDetailsForBlock(id, orchestrator, fieldParser) ?: return + val (content, targetObjectId, fileName) = fileDetails + + val url = urlBuilder.getUrlForFileContent( + fileContent = content, + isOriginalImage = true + ) - val fileBlock = blocks.firstOrNull { it.id == blockId } - val fileContent = fileBlock?.content as? Content.File + Timber.d("startDownloadingFileFromBlock, fileObjectId: [$targetObjectId], fileName: [$fileName], url: [$url]") - if (fileBlockView != null && fileContent != null) { + if (url != null) { viewModelScope.launch { orchestrator.proxies.intents.send( Media.DownloadFile( - url = fileBlockView.url, - name = fileBlockView.name.orEmpty(), - type = fileContent.type + url = url, + name = fileName, + type = content.type ) ) } } else { - Timber.e("Block is not File or with wrong state, can't proceed with download") + Timber.e("Couldn't proceed with downloading file, because url is null") sendToast("Something went wrong. Couldn't download file.") } } diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/Command.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/Command.kt index a3dee935fc..b1c51f9672 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/Command.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/Command.kt @@ -33,8 +33,7 @@ sealed class Command { * @property [id] id of the file block */ data class OpenFileByDefaultApp( - val id: String, - val uri: String + val id: String ) : Command() data class OpenObjectSnackbar( diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/extension/FileUrlExt.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/extension/FileUrlExt.kt index e26e9df5a3..bff85bb254 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/extension/FileUrlExt.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/extension/FileUrlExt.kt @@ -1,10 +1,14 @@ package com.anytypeio.anytype.presentation.extension import com.anytypeio.anytype.core_models.Block +import com.anytypeio.anytype.core_models.Block.Content import com.anytypeio.anytype.core_models.Id import com.anytypeio.anytype.core_models.ObjectType import com.anytypeio.anytype.core_models.Url import com.anytypeio.anytype.domain.misc.UrlBuilder +import com.anytypeio.anytype.domain.primitives.FieldParser +import com.anytypeio.anytype.presentation.editor.editor.Orchestrator +import timber.log.Timber fun UrlBuilder.getUrlForFileBlock( fileBlock: Block?, @@ -66,3 +70,52 @@ fun UrlBuilder.getUrlBasedOnFileLayout( else -> null } } + + +data class FileDetails( + val content: Block.Content.File, + val targetObjectId: String, + val fileName: String, +) + +/** + * Attempts to retrieve and validate the file details for a given block [blockId]. + * Returns [FileDetails] if successful, or `null` if something went wrong. + */ +fun List.getFileDetailsForBlock( + blockId: String, + orchestrator: Orchestrator, + fieldParser: FieldParser +): FileDetails? { + + val block = firstOrNull { it.id == blockId } ?: run { + Timber.e("No block found with id $blockId") + return null + } + + val content = block.content + if (content !is Content.File || content.state != Content.File.State.DONE) { + Timber.e("Block content is not a file or is not in the DONE state; cannot proceed.") + return null + } + + val targetObjectId = content.targetObjectId + if (targetObjectId.isEmpty()) { + Timber.e("Target object ID is empty; cannot proceed with file sharing.") + return null + } + + val fileObject = orchestrator.stores.details.getAsObject(target = targetObjectId) + if (fileObject == null) { + Timber.e("Object with id $targetObjectId not found.") + return null + } + + val fileName = fieldParser.getObjectName(fileObject) + + return FileDetails( + content = content, + targetObjectId = targetObjectId, + fileName = fileName, + ) +} From 8994c3d3730b1dc91a6628774d0de6732f1edf5a Mon Sep 17 00:00:00 2001 From: konstantiniiv Date: Mon, 23 Dec 2024 13:42:46 +0100 Subject: [PATCH 21/23] DROID-3185 fixes --- .../anytype/presentation/editor/EditorViewModel.kt | 6 +++--- .../anytype/presentation/editor/EditorViewModelTest.kt | 2 +- .../presentation/editor/editor/EditorErrorMessageTest.kt | 2 +- .../presentation/editor/editor/EditorLockPageTest.kt | 3 +-- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModel.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModel.kt index fb4442f10a..4859b0e2fc 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModel.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModel.kt @@ -3789,8 +3789,8 @@ class EditorViewModel( } is ListenerType.File.View -> { when (mode) { - EditorMode.Edit -> onFileClicked(clicked.target) - EditorMode.Locked, EditorMode.Read -> onFileClicked(clicked.target) + EditorMode.Edit -> onFileBlockClicked(clicked.target) + EditorMode.Locked, EditorMode.Read -> onFileBlockClicked(clicked.target) EditorMode.Select -> onBlockMultiSelectClicked(clicked.target) else -> Unit } @@ -4246,7 +4246,7 @@ class EditorViewModel( } } - private fun onFileClicked(blockId: String) { + private fun onFileBlockClicked(blockId: String) { dispatch( Command.OpenFileByDefaultApp( id = blockId diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/EditorViewModelTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/EditorViewModelTest.kt index 18d7357747..b4901821c7 100644 --- a/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/EditorViewModelTest.kt +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/EditorViewModelTest.kt @@ -2622,7 +2622,7 @@ open class EditorViewModelTest { // TESTING - vm.startDownloadingFileFromBlock(blockId = file.id) + vm.startDownloadingFileFromBlock(id = file.id) runBlockingTest { verify(downloadFile, times(1)).invoke( diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/EditorErrorMessageTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/EditorErrorMessageTest.kt index 6b5d95e8fd..480e053843 100644 --- a/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/EditorErrorMessageTest.kt +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/EditorErrorMessageTest.kt @@ -90,7 +90,7 @@ class EditorErrorMessageTest : EditorPresentationTestSetup() { // Launching operation that triggers a toast - vm.startDownloadingFileFromBlock(blockId = fileBlock.id) + vm.startDownloadingFileFromBlock(id = fileBlock.id) advanceUntilIdle() diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/EditorLockPageTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/EditorLockPageTest.kt index 3b5e0ff121..784dda8567 100644 --- a/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/EditorLockPageTest.kt +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/editor/EditorLockPageTest.kt @@ -674,8 +674,7 @@ class EditorLockPageTest : EditorPresentationTestSetup() { testObserver.assertValue { value -> value is EventWrapper && value.peekContent() == Command.OpenFileByDefaultApp( - id = fileBlockId, - uri = builder.file(targetObjectId) + id = fileBlockId ) } } From 9f0edeaaea207eaee10245bb263a7af2e514ba3f Mon Sep 17 00:00:00 2001 From: konstantiniiv Date: Mon, 23 Dec 2024 13:49:55 +0100 Subject: [PATCH 22/23] DROID-3185 fix --- .../presentation/editor/EditorViewModel.kt | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModel.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModel.kt index 4859b0e2fc..a0a324d2b4 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModel.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/EditorViewModel.kt @@ -4307,14 +4307,15 @@ class EditorViewModel( private fun proceedWithDownloadCurrentObjectAsFile() { - val details = orchestrator.stores.details.current() - val objectDetails = details.details[context]?.map ?: return - if (objectDetails.isEmpty()) return - val obj = ObjectWrapper.Basic(objectDetails) + val fileObject = orchestrator.stores.details.getAsObject(target = context) + if (fileObject == null) { + Timber.e("Object with id $context not found.") + return + } - Timber.d("startDownloadingFileAsObject, for object:[$obj]") + Timber.d("startDownloadingFileAsObject, for object:[$context]") - val layout = obj.layout + val layout = fileObject.layout if (layout == null || layout !in SupportedLayouts.fileLayouts) { Timber.e("Object with layout:$layout is not Media, can't proceed with download") @@ -4325,7 +4326,7 @@ class EditorViewModel( sendToast("Downloading file in background...") val url = urlBuilder.getUrlBasedOnFileLayout( - obj = obj.id, + obj = fileObject.id, layout = layout ) @@ -4334,7 +4335,7 @@ class EditorViewModel( orchestrator.proxies.intents.send( Media.DownloadFile( url = url, - name = fieldParser.getObjectName(obj), + name = fieldParser.getObjectName(fileObject), type = null ) ) From 88fbd06d30fb6df80b6615a20ada6603c357ca47 Mon Sep 17 00:00:00 2001 From: konstantiniiv Date: Mon, 23 Dec 2024 15:39:53 +0100 Subject: [PATCH 23/23] DROID-3185 pr fix --- .../core_ui/features/editor/BlockAdapter.kt | 12 +++++------ .../features/editor/holders/other/Title.kt | 8 +++++-- .../editor/holders/upload/OpenFile.kt | 4 ++-- .../editor/editor/model/BlockView.kt | 21 ++++++++----------- .../editor/editor/model/types/Types.kt | 4 ++-- .../editor/selection/TableCellExt.kt | 4 ++-- .../presentation/mapper/MapperExtension.kt | 6 +++--- .../editor/DefaultBlockViewRendererTest.kt | 4 ++-- 8 files changed, 32 insertions(+), 31 deletions(-) diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/BlockAdapter.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/BlockAdapter.kt index fc86538d83..68cfeba72b 100644 --- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/BlockAdapter.kt +++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/BlockAdapter.kt @@ -168,8 +168,8 @@ import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_OBJECT_LINK_DEFAULT import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_OBJECT_LINK_DELETED import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_OBJECT_LINK_LOADING -import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_OPEN_FILE -import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_OPEN_IMAGE +import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_BUTTON_OPEN_FILE +import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_BUTTON_OPEN_IMAGE import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_PARAGRAPH import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_PICTURE import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_PICTURE_ERROR @@ -542,10 +542,10 @@ class BlockAdapter( ItemBlockMediaErrorBinding.inflate(inflater, parent, false) ) } - HOLDER_OPEN_FILE -> { + HOLDER_BUTTON_OPEN_FILE -> { OpenFile(ItemBlockOpenFileBinding.inflate(inflater, parent, false)) } - HOLDER_OPEN_IMAGE -> { + HOLDER_BUTTON_OPEN_IMAGE -> { OpenImage(ItemBlockOpenFileBinding.inflate(inflater, parent, false)) } HOLDER_VIDEO -> { @@ -1653,13 +1653,13 @@ class BlockAdapter( } is OpenFile -> { holder.bind( - item = blocks[position] as BlockView.OpenFile.File, + item = blocks[position] as BlockView.ButtonOpenFile.FileButton, click = onClickListener ) } is OpenImage -> { holder.bind( - item = blocks[position] as BlockView.OpenFile.Image, + item = blocks[position] as BlockView.ButtonOpenFile.ImageButton, click = onClickListener ) } diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt index 84bcebb1c3..442aa63fb7 100644 --- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt +++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/other/Title.kt @@ -581,7 +581,11 @@ sealed class Title(view: View) : BlockViewHolder(view), TextHolder { icon.setIcon(item.icon) } - override fun applyTextColor(item: BlockView.Title) {} - override fun applyBackground(item: BlockView.Title) {} + override fun applyTextColor(item: BlockView.Title) { + //do nothing + } + override fun applyBackground(item: BlockView.Title) { + //do nothing + } } } \ No newline at end of file diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/upload/OpenFile.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/upload/OpenFile.kt index e2e04d55fc..0f48c101e5 100644 --- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/upload/OpenFile.kt +++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/editor/holders/upload/OpenFile.kt @@ -12,7 +12,7 @@ class OpenFile( private val root: View = itemView - fun bind(item: BlockView.OpenFile.File, click: (ListenerType) -> Unit) { + fun bind(item: BlockView.ButtonOpenFile.FileButton, click: (ListenerType) -> Unit) { root.setOnClickListener { click( ListenerType.File.View( @@ -29,7 +29,7 @@ class OpenImage( private val root: View = itemView - fun bind(item: BlockView.OpenFile.Image, click: (ListenerType) -> Unit) { + fun bind(item: BlockView.ButtonOpenFile.ImageButton, click: (ListenerType) -> Unit) { root.setOnClickListener { click( ListenerType.Picture.View( diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/model/BlockView.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/model/BlockView.kt index 5592f60427..533f32e42f 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/model/BlockView.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/model/BlockView.kt @@ -44,11 +44,9 @@ import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_OBJECT_LINK_DELETED import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_OBJECT_LINK_LOADING import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_OBJECT_TYPE -import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_OBJECT_TYPE_COLLECTION import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_OBJECT_TYPE_DELETED -import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_OBJECT_TYPE_SET -import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_OPEN_FILE -import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_OPEN_IMAGE +import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_BUTTON_OPEN_FILE +import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_BUTTON_OPEN_IMAGE import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_PARAGRAPH import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_PICTURE import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER_PICTURE_ERROR @@ -78,7 +76,6 @@ import com.anytypeio.anytype.presentation.editor.editor.model.types.Types.HOLDER import com.anytypeio.anytype.presentation.objects.ObjectIcon import com.anytypeio.anytype.presentation.objects.appearance.choose.ObjectAppearanceChooseSettingsView import com.anytypeio.anytype.presentation.relations.ObjectRelationView -import com.anytypeio.anytype.presentation.spaces.SpaceIconView /** * UI-models for different types of blocks. @@ -1296,25 +1293,25 @@ sealed class BlockView : ViewType { override fun getViewType(): Int = HOLDER_FEATURED_RELATION } - sealed class OpenFile( + sealed class ButtonOpenFile( override val id: String, val isSelected: Boolean = false ) : BlockView() { abstract val targetId: Id? - data class Image( + data class ImageButton( override val id: String, override val targetId: Id - ) : OpenFile(id) { - override fun getViewType(): Int = HOLDER_OPEN_IMAGE + ) : ButtonOpenFile(id) { + override fun getViewType(): Int = HOLDER_BUTTON_OPEN_IMAGE } - data class File( + data class FileButton( override val id: String, override val targetId: Id - ) : OpenFile(id) { - override fun getViewType(): Int = HOLDER_OPEN_FILE + ) : ButtonOpenFile(id) { + override fun getViewType(): Int = HOLDER_BUTTON_OPEN_FILE } } diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/model/types/Types.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/model/types/Types.kt index b898102ab5..c1fb63c24a 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/model/types/Types.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/editor/model/types/Types.kt @@ -46,8 +46,8 @@ object Types { const val HOLDER_FILE_PLACEHOLDER = 32 const val HOLDER_FILE_UPLOAD = 33 const val HOLDER_FILE_ERROR = 34 - const val HOLDER_OPEN_FILE = 134 - const val HOLDER_OPEN_IMAGE = 135 + const val HOLDER_BUTTON_OPEN_FILE = 134 + const val HOLDER_BUTTON_OPEN_IMAGE = 135 const val HOLDER_DIVIDER_LINE = 16 const val HOLDER_DIVIDER_DOTS = 38 diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/selection/TableCellExt.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/selection/TableCellExt.kt index c36c645a3e..403a17e199 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/selection/TableCellExt.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/editor/selection/TableCellExt.kt @@ -353,8 +353,8 @@ fun List.toggleTableMode( is BlockView.DataView.EmptyData -> view.copy(isSelected = false) is BlockView.DataView.EmptySource -> view.copy(isSelected = false) is BlockView.DataView.Deleted -> view.copy(isSelected = false) - is BlockView.OpenFile.Image -> view - is BlockView.OpenFile.File -> view + is BlockView.ButtonOpenFile.ImageButton -> view + is BlockView.ButtonOpenFile.FileButton -> view } } } diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/mapper/MapperExtension.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/mapper/MapperExtension.kt index 684aed85ae..f658bb3af0 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/mapper/MapperExtension.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/mapper/MapperExtension.kt @@ -69,7 +69,7 @@ fun Block.Content.File.toPictureView( if (url != null && targetObject.isValid && targetObject.notDeletedNorArchived) { if (currentObject.layout == ObjectType.Layout.IMAGE) { - BlockView.OpenFile.Image( + BlockView.ButtonOpenFile.ImageButton( id = blockId, targetId = targetObjectId ) @@ -149,7 +149,7 @@ fun Block.Content.File.toVideoView( if (url != null && targetObject.isValid && targetObject.notDeletedNorArchived) { if (currentObject.layout == ObjectType.Layout.VIDEO) { - BlockView.OpenFile.File( + BlockView.ButtonOpenFile.FileButton( id = blockId, targetId = targetObjectId ) @@ -229,7 +229,7 @@ fun Block.Content.File.toFileView( if (url != null && targetObject.isValid && targetObject.notDeletedNorArchived) { if (SupportedLayouts.fileLayouts.contains(currentObject.layout)) { - BlockView.OpenFile.File( + BlockView.ButtonOpenFile.FileButton( id = blockId, targetId = targetObjectId ) diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/DefaultBlockViewRendererTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/DefaultBlockViewRendererTest.kt index 3351b95856..318df39ecc 100644 --- a/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/DefaultBlockViewRendererTest.kt +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/editor/DefaultBlockViewRendererTest.kt @@ -5822,7 +5822,7 @@ class DefaultBlockViewRendererTest { ) ), ), - BlockView.OpenFile.File( + BlockView.ButtonOpenFile.FileButton( id = file.id, targetId = currentObjectId ) @@ -5890,7 +5890,7 @@ class DefaultBlockViewRendererTest { ) ), ), - BlockView.OpenFile.Image( + BlockView.ButtonOpenFile.ImageButton( id = file.id, targetId = currentObjectId )