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

DROID-3185 File as Object | New layout ui, part 1 (#1947)

This commit is contained in:
Konstantin Ivanov 2024-12-23 15:53:37 +01:00 committed by konstantiniiv
parent 4f60643653
commit d979ff0e25
44 changed files with 876 additions and 607 deletions

View file

@ -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(

View file

@ -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
}

View file

@ -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

View file

@ -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

View file

@ -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"

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<paths>
<cache-path name="." path="/" />
<files-path name="." path="/" />
<files-path name="file_path" path="/" />
</paths>

View file

@ -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 }

View file

@ -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)
}
}

View file

@ -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
@ -55,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
@ -113,6 +115,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
@ -148,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
@ -163,6 +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_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
@ -358,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)
@ -530,6 +542,12 @@ class BlockAdapter(
ItemBlockMediaErrorBinding.inflate(inflater, parent, false)
)
}
HOLDER_BUTTON_OPEN_FILE -> {
OpenFile(ItemBlockOpenFileBinding.inflate(inflater, parent, false))
}
HOLDER_BUTTON_OPEN_IMAGE -> {
OpenImage(ItemBlockOpenFileBinding.inflate(inflater, parent, false))
}
HOLDER_VIDEO -> {
Video(
ItemBlockVideoBinding.inflate(inflater, parent, false)
@ -1002,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(),
@ -1361,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,
@ -1622,6 +1651,18 @@ class BlockAdapter(
clicked = onClickListener
)
}
is OpenFile -> {
holder.bind(
item = blocks[position] as BlockView.ButtonOpenFile.FileButton,
click = onClickListener
)
}
is OpenImage -> {
holder.bind(
item = blocks[position] as BlockView.ButtonOpenFile.ImageButton,
click = onClickListener
)
}
}
if (holder is Text<*>) {

View file

@ -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
override val root: View = itemView
override val content: TextInputWidget = binding.title
init {
icon.binding.ivImage.updateLayoutParams<LayoutParams> {
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) {
//do nothing
}
override fun applyBackground(item: BlockView.Title) {
//do nothing
}
}
}

View file

@ -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.ButtonOpenFile.FileButton, 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.ButtonOpenFile.ImageButton, click: (ListenerType) -> Unit) {
root.setOnClickListener {
click(
ListenerType.Picture.View(
target = item.id
)
)
}
}
}

View file

@ -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()

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<stroke
android:width="1dp"
android:color="@color/shape_primary" />
<corners android:radius="8dp" />
</shape>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid
android:color="@color/background_highlighted_medium"/>
<corners android:radius="8dp" />
</shape>

View file

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/fileName"
android:background="@drawable/bg_button_open_file"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginBottom="@dimen/dp_32"
android:layout_gravity="bottom"
android:gravity="center"
android:paddingStart="@dimen/dp_12"
android:paddingBottom="7dp"
android:paddingTop="7dp"
android:paddingEnd="@dimen/dp_12"
android:textColor="@color/text_primary"
style="@style/TextView.UXStyle.Body.Callout.Regular"
android:text="@string/open_file"/>
</FrameLayout>

View file

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/cover"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<com.anytypeio.anytype.core_ui.widgets.ObjectIconWidget
android:id="@+id/objectIconWidget"
android:layout_width="64dp"
android:layout_height="80dp"
android:layout_gravity="bottom"
android:layout_marginStart="28dp"
android:layout_marginTop="80dp"
app:imageSize="80dp"
tools:src="@drawable/ic_mime_pdf"
android:background="@drawable/bg_title_file_icon"
android:transitionName="@string/logo_transition"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.anytypeio.anytype.core_ui.widgets.text.TextInputWidget
android:id="@+id/title"
style="@style/BlockTitleContentStyle"
android:layout_width="0dp"
android:layout_marginStart="20dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="20dp"
android:hint="@string/untitled"
android:paddingTop="0dp"
app:ignoreDragAndDrop="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/objectIconWidget"
app:onlyPasteAsPlaneText="true"
app:layout_goneMarginTop="@dimen/dp_80"
tools:text="Check new Android version multiline"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -35,6 +35,7 @@
<dimen name="dp_46">46dp</dimen>
<dimen name="dp_48">48dp</dimen>
<dimen name="dp_54">54dp</dimen>
<dimen name="dp_64">64dp</dimen>
<dimen name="dp_72">72dp</dimen>
<dimen name="dp_80">80dp</dimen>
<dimen name="dp_51">51dp</dimen>

View file

@ -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 {

View file

@ -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)

View file

@ -525,6 +525,7 @@
<string name="open_source">Open source</string>
<string name="reload_object_content">Reload object content</string>
<string name="open_link">Open link</string>
<string name="open_file">Open file</string>
<string name="copy_email">Copy email</string>
<string name="copy_phone_number">Copy phone number</string>
<string name="send_email">Send email</string>

View file

@ -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
)
}

View file

@ -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
@ -259,13 +254,13 @@ 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
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 +291,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
@ -366,8 +359,6 @@ class EditorViewModel(
val actions = MutableStateFlow(ActionItemType.defaultSorting)
val icon = MutableStateFlow<ProfileIconView>(ProfileIconView.Loading)
val isUndoEnabled = MutableStateFlow(false)
val isRedoEnabled = MutableStateFlow(false)
val isUndoRedoToolbarIsVisible = MutableStateFlow(false)
@ -442,7 +433,6 @@ class EditorViewModel(
init {
Timber.i("EditorViewModel, init")
proceedWithObservingPermissions()
proceedWithObservingProfileIcon()
startHandlingTextChanges()
startProcessingFocusChanges()
startProcessingControlPanelViewState()
@ -489,35 +479,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)
@ -824,6 +785,7 @@ class EditorViewModel(
val flags = mutableListOf<BlockViewRenderer.RenderFlag>()
Timber.d("Rendering starting...")
val doc = models.asMap().render(
context = context,
mode = mode,
root = root,
focus = focus,
@ -3827,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
}
@ -4284,85 +4246,76 @@ 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
)
private fun onFileBlockClicked(blockId: String) {
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("startDownloadingFile, id:[$id]")
Timber.d("startSharingFile, fileBlockId: [$id]")
sendToast("Preparing file to share...")
val block = blocks.firstOrNull { it.id == id }
val content = block?.content
val fileDetails = blocks.getFileDetailsForBlock(id, orchestrator, fieldParser) ?: return
val (content, targetObjectId, fileName) = fileDetails
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
)
Timber.d("startSharingFile, 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!")
)
}
}
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 fileBlock = blocks.firstOrNull { it.id == blockId }
val fileContent = fileBlock?.content as? Content.File
val url = urlBuilder.getUrlForFileBlock(fileBlock)
if (fileContent != null && url != null) {
val fileDetails = blocks.getFileDetailsForBlock(id, orchestrator, fieldParser) ?: return
val (content, targetObjectId, fileName) = fileDetails
val url = urlBuilder.getUrlForFileContent(
fileContent = content,
isOriginalImage = true
)
Timber.d("startDownloadingFileFromBlock, fileObjectId: [$targetObjectId], fileName: [$fileName], url: [$url]")
if (url != null) {
viewModelScope.launch {
orchestrator.proxies.intents.send(
Media.DownloadFile(
url = url,
name = fileContent.name.orEmpty(),
type = fileContent.type
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.")
}
}
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")
@ -4373,7 +4326,7 @@ class EditorViewModel(
sendToast("Downloading file in background...")
val url = urlBuilder.getUrlBasedOnFileLayout(
obj = obj.id,
obj = fileObject.id,
layout = layout
)
@ -4382,7 +4335,7 @@ class EditorViewModel(
orchestrator.proxies.intents.send(
Media.DownloadFile(
url = url,
name = fieldParser.getObjectName(obj),
name = fieldParser.getObjectName(fileObject),
type = null
)
)

View file

@ -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(

View file

@ -155,6 +155,9 @@ fun List<BlockView>.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<BlockView>.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<BlockView>.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<BlockView>.toReadMode(): List<BlockView> = 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<BlockView>.toEditMode(): List<BlockView> = 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<BlockView>.clearSearchHighlights(): List<BlockView> = 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<BlockView>.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)

View file

@ -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
@ -43,9 +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_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
@ -75,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.
@ -668,6 +668,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<Searchable.Field> = 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
@ -1268,6 +1293,28 @@ sealed class BlockView : ViewType {
override fun getViewType(): Int = HOLDER_FEATURED_RELATION
}
sealed class ButtonOpenFile(
override val id: String,
val isSelected: Boolean = false
) : BlockView() {
abstract val targetId: Id?
data class ImageButton(
override val id: String,
override val targetId: Id
) : ButtonOpenFile(id) {
override fun getViewType(): Int = HOLDER_BUTTON_OPEN_IMAGE
}
data class FileButton(
override val id: String,
override val targetId: Id
) : ButtonOpenFile(id) {
override fun getViewType(): Int = HOLDER_BUTTON_OPEN_FILE
}
}
sealed class Relation : BlockView(), Selectable, Indentable, Decoratable {
abstract val background: ThemeColor

View file

@ -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
@ -45,6 +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_BUTTON_OPEN_FILE = 134
const val HOLDER_BUTTON_OPEN_IMAGE = 135
const val HOLDER_DIVIDER_LINE = 16
const val HOLDER_DIVIDER_DOTS = 38

View file

@ -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<Id, List<Block>>.render(
context: Id,
mode: EditorMode = EditorMode.Edit,
root: Block,
focus: Editor.Focus,

View file

@ -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<Id, List<Block>>.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,
@ -623,11 +634,6 @@ 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)) {
return@forEach
}
mCounter = 0
val blockDecorationScheme = buildNestedDecorationData(
block = block,
@ -640,18 +646,19 @@ class DefaultBlockViewRenderer @Inject constructor(
background = block.parseThemeBackgroundColor()
)
)
result.add(
file(
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 -> {
@ -666,6 +673,7 @@ class DefaultBlockViewRenderer @Inject constructor(
}
result.addAll(
render(
context = context,
mode = mode,
root = root,
focus = focus,
@ -1376,6 +1384,7 @@ class DefaultBlockViewRenderer @Inject constructor(
}
private fun file(
context: Id,
mode: EditorMode,
content: Content.File,
block: Block,
@ -1383,99 +1392,21 @@ class DefaultBlockViewRenderer @Inject constructor(
selection: Set<Id>,
isPreviousBlockMedia: Boolean,
schema: NestedDecorationData,
details: Block.Details
): BlockView = when (content.type) {
Content.File.Type.IMAGE -> content.toPictureView(
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(
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(
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(
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(
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(
@ -1511,7 +1442,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
@ -1572,18 +1503,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

View file

@ -283,6 +283,7 @@ fun List<BlockView>.toggleTableMode(
is BlockView.Title.Basic -> view.copy(
mode = cellsMode
)
is BlockView.Title.File -> view
is BlockView.Title.Profile -> view.copy(
mode = cellsMode
)
@ -352,6 +353,8 @@ fun List<BlockView>.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.ButtonOpenFile.ImageButton -> view
is BlockView.ButtonOpenFile.FileButton -> view
}
}
}

View file

@ -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<Block>.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,
)
}

View file

@ -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(),

View file

@ -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,6 +32,7 @@ import com.anytypeio.anytype.presentation.templates.TemplateObjectTypeView
import timber.log.Timber
fun Block.Content.File.toPictureView(
context: Id,
blockId: String,
urlBuilder: UrlBuilder,
indent: Int,
@ -39,7 +41,8 @@ fun Block.Content.File.toPictureView(
background: ThemeColor,
isPreviousBlockMedia: Boolean,
decorations: List<BlockView.Decoration>,
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,
@ -59,25 +62,32 @@ 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
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
)
val url = urlBuilder.getUrlForFileContent(this)
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.ButtonOpenFile.ImageButton(
id = blockId,
targetId = targetObjectId
)
} else {
BlockView.Media.Picture(
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 picture view for block $blockId")
BlockView.Error.Picture(
@ -99,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,
@ -111,7 +121,8 @@ fun Block.Content.File.toVideoView(
background: ThemeColor,
isPrevBlockMedia: Boolean,
decorations: List<BlockView.Decoration>,
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,
@ -131,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 url = urlBuilder.getUrlForFileContent(this)
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.ButtonOpenFile.FileButton(
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(
@ -171,10 +189,10 @@ fun Block.Content.File.toVideoView(
decorations = decorations,
name = name
)
else -> throw IllegalStateException("Unexpected state: $state")
}
fun Block.Content.File.toFileView(
context: Id,
blockId: String,
urlBuilder: UrlBuilder,
indent: Int,
@ -183,7 +201,8 @@ fun Block.Content.File.toFileView(
background: ThemeColor,
isPrevBlockMedia: Boolean,
decorations: List<BlockView.Decoration>,
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,
@ -203,24 +222,21 @@ fun Block.Content.File.toFileView(
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) {
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.ButtonOpenFile.FileButton(
id = blockId,
indent = indent,
mode = mode,
isSelected = isSelected,
background = background,
decorations = decorations
targetId = targetObjectId
)
} else {
val targetObject = ObjectWrapper.File(struct)
BlockView.Media.File(
id = blockId,
targetObjectId = targetId,
targetObjectId = targetObjectId,
url = url,
indent = indent,
mode = mode,
@ -228,7 +244,7 @@ fun Block.Content.File.toFileView(
background = background,
decorations = decorations,
size = targetObject.sizeInBytes?.toLong(),
name = targetObject.name,
name = fieldParser.getObjectName(targetObject),
mime = targetObject.fileMimeType,
fileExt = targetObject.fileExt
)
@ -254,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) {

View file

@ -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(),

View file

@ -76,6 +76,7 @@ class DefaultBlockViewRendererTest {
details: Block.Details,
schema: NestedDecorationData = emptyList()
): List<BlockView> = blocks.render(
context = root.id,
root = root,
focus = focus,
anchor = anchor,
@ -5754,24 +5755,38 @@ class DefaultBlockViewRendererTest {
//endregion
@Test
fun `should not render file block in case of file layout`() {
val file = StubFile(
backgroundColor = null,
state = Block.Content.File.State.DONE,
type = Block.Content.File.Type.FILE
fun `should render file open block in case of file, pdf, audio or video layouts`(){
val fileTypeExceptImage = listOf(
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) ->
testFileLayout(type = fileType, layout = layout)
}
}
private fun testFileLayout(type: Block.Content.File.Type, layout: Layout) {
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.FILE.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)
@ -5806,6 +5821,78 @@ class DefaultBlockViewRendererTest {
background = paragraph.parseThemeBackgroundColor()
)
),
),
BlockView.ButtonOpenFile.FileButton(
id = file.id,
targetId = currentObjectId
)
)
assertEquals(expected = expected, actual = result)
}
@Test
fun `should render image open block in case of image layout`() {
val currentObjectId = MockDataFactory.randomUuid()
val file = StubFile(
type = Block.Content.File.Type.IMAGE,
targetObjectId = currentObjectId,
state = Block.Content.File.State.DONE
)
val paragraph = StubParagraph()
val page = StubSmartBlock(id = currentObjectId, children = listOf(paragraph.id, file.id))
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)
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<Block.Content.Text>().text,
decorations = listOf(
BlockView.Decoration(
style = BlockView.Decoration.Style.None,
background = paragraph.parseThemeBackgroundColor()
)
),
),
BlockView.ButtonOpenFile.ImageButton(
id = file.id,
targetId = currentObjectId
)
)

View file

@ -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
@ -2554,70 +2559,26 @@ open class EditorViewModelTest {
}
}
@Test
fun `should start sharing a file`() {
val root = MockDataFactory.randomUuid()
val file = MockBlockFactory.makeFileBlock()
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 flow: Flow<List<Event.Command>> = 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<Block.Content.File>().name.orEmpty(),
objectId = file.content<Block.Content.File>().targetObjectId.orEmpty(),
)
)
)
}
}
@Test
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 targetObject = StubObject(
id = targetObjectId,
name = "file1",
layout = ObjectType.Layout.FILE.code.toDouble(),
fileExt = ".pdf"
)
val objectDetails = Block.Fields(targetObject.map)
val page = listOf(
Block(
id = root,
@ -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)
@ -2654,16 +2622,14 @@ open class EditorViewModelTest {
// TESTING
vm.startDownloadingFileFromBlock(blockId = file.id)
vm.startDownloadingFileFromBlock(id = file.id)
runBlockingTest {
verify(downloadFile, times(1)).invoke(
params = eq(
DownloadFile.Params(
name = file.content<Block.Content.File>().name.orEmpty(),
url = builder.file(
path = file.content<Block.Content.File>().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
)
)

View file

@ -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<String>()
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(id = fileBlock.id)
advanceUntilIdle()

View file

@ -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(
@ -680,8 +674,7 @@ class EditorLockPageTest : EditorPresentationTestSetup() {
testObserver.assertValue { value ->
value is EventWrapper && value.peekContent() == Command.OpenFileByDefaultApp(
id = fileBlockId,
uri = builder.file(targetObjectId)
id = fileBlockId
)
}
}
@ -697,15 +690,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(

View file

@ -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(

View file

@ -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 =

View file

@ -55,6 +55,7 @@ class TableBlockRendererTest {
indent: Int,
details: Block.Details
): List<BlockView> = blocks.render(
context = root.id,
root = root,
anchor = anchor,
focus = focus,

View file

@ -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

View file

@ -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
)
)

View file

@ -22,7 +22,8 @@ fun StubObject(
isHidden: Boolean? = null,
links: List<Id> = 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
)
)

View file

@ -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()),