Compare commits

...

18 Commits

Author SHA1 Message Date
uburoiubu
83d7847a8e
DROID-2156 fixes 2024-01-30 22:02:10 +01:00
uburoiubu
70dca79974
DROID-2156 fixes 2024-01-30 21:58:00 +01:00
uburoiubu
ce984bdd13
DROID-2156 fixes 2024-01-30 21:50:35 +01:00
uburoiubu
a83a81182c
DROID-2156 fixes 2024-01-30 21:48:30 +01:00
uburoiubu
8f5a850fd3
DROID-2156 fixes 2024-01-30 21:36:34 +01:00
uburoiubu
30661412a5
DROID-2156 fixes 2024-01-30 21:01:43 +01:00
uburoiubu
76cdcacb16
DROID-2156 fixes 2024-01-30 20:17:18 +01:00
uburoiubu
ecb51a431e
DROID-2156 fix 2024-01-30 18:53:52 +01:00
uburoiubu
c1369553f1
DROID-2156 fixes 2024-01-30 18:37:55 +01:00
uburoiubu
26343a21a7
DROID-2156 fixes 2024-01-30 17:02:42 +01:00
uburoiubu
297eb36dc1
DROID-2156 basics 2024-01-30 16:38:16 +01:00
uburoiubu
68a77d586b
DROID-2156 added intent filters 2024-01-30 14:45:52 +01:00
Konstantin Ivanov
ece65b089f
DROID-2135 Files as objects | Pr update (#810) 2024-01-30 13:21:19 +01:00
konstantiniiv
ded7702c92 DROID-2135 Merge branch 'main' into droid-2135-files-as-objects-main 2024-01-29 17:27:28 +01:00
konstantiniiv
c68327cde9 DROID-2135 Merge branch 'main' into droid-2135-files-as-objects-main
# Conflicts:
#	gradle/libs.versions.toml
#	localization/src/main/res/values/strings.xml
2024-01-24 17:36:55 +01:00
Konstantin Ivanov
b5d8f55b65
DROID-2135 Files as objects | file blocks (#779) 2024-01-18 18:49:00 +01:00
Konstantin Ivanov
d6d894d93a
DROID-2135 Design | Media blocks (#774) 2024-01-16 18:47:13 +01:00
Konstantin Ivanov
0a52e65f2e
DROID-2135 Objects | Support files as objects (#765) 2024-01-14 13:12:39 +01:00
103 changed files with 1669 additions and 1109 deletions

View File

@ -57,6 +57,21 @@
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND_MULTIPLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="application/*" />
</intent-filter>
</activity>
<activity
android:name=".ui.settings.system.SettingsActivity"

View File

@ -9,6 +9,7 @@ import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
import com.anytypeio.anytype.domain.block.repo.BlockRepository
import com.anytypeio.anytype.domain.config.ConfigStorage
import com.anytypeio.anytype.domain.config.UserSettingsRepository
import com.anytypeio.anytype.domain.device.FileSharer
import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.workspace.SpaceManager
@ -55,4 +56,5 @@ interface AddToAnytypeDependencies : ComponentDependencies {
fun urlBuilder(): UrlBuilder
fun awaitAccountStartedManager(): AwaitAccountStartManager
fun analytics(): Analytics
fun fileSharer(): FileSharer
}

View File

@ -4,10 +4,13 @@ import android.content.Context
import com.anytypeio.anytype.data.auth.other.DataDownloader
import com.anytypeio.anytype.data.auth.other.Device
import com.anytypeio.anytype.device.DefaultLocalProvider
import com.anytypeio.anytype.device.SharedFileUploader
import com.anytypeio.anytype.device.base.AndroidDevice
import com.anytypeio.anytype.device.download.AndroidDeviceDownloader
import com.anytypeio.anytype.domain.device.FileSharer
import com.anytypeio.anytype.domain.download.Downloader
import com.anytypeio.anytype.domain.misc.LocaleProvider
import com.anytypeio.anytype.providers.DefaultUriFileProvider
import dagger.Module
import dagger.Provides
import javax.inject.Singleton
@ -40,4 +43,9 @@ object DeviceModule {
@Provides
@Singleton
fun provideLocaleProvider(context: Context): LocaleProvider = DefaultLocalProvider(context)
@JvmStatic
@Provides
@Singleton
fun provideFileSharer(context: Context): FileSharer = SharedFileUploader(context)
}

View File

@ -3,8 +3,11 @@ package com.anytypeio.anytype.ui.main
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Parcelable
import android.provider.OpenableColumns
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.os.bundleOf
@ -18,6 +21,7 @@ import com.anytypeio.anytype.R
import com.anytypeio.anytype.app.DefaultAppActionManager
import com.anytypeio.anytype.core_models.ThemeMode
import com.anytypeio.anytype.core_models.Wallpaper
import com.anytypeio.anytype.core_utils.ext.Mimetype
import com.anytypeio.anytype.core_utils.ext.toast
import com.anytypeio.anytype.core_utils.tools.FeatureToggles
import com.anytypeio.anytype.di.common.componentManager
@ -31,12 +35,15 @@ import com.anytypeio.anytype.presentation.main.MainViewModel
import com.anytypeio.anytype.presentation.main.MainViewModel.Command
import com.anytypeio.anytype.presentation.main.MainViewModelFactory
import com.anytypeio.anytype.presentation.navigation.AppNavigation
import com.anytypeio.anytype.presentation.util.getExternalFilesDirTemp
import com.anytypeio.anytype.presentation.wallpaper.WallpaperColor
import com.anytypeio.anytype.ui.editor.CreateObjectFragment
import com.anytypeio.anytype.ui.sharing.SharingFragment
import com.anytypeio.anytype.ui_settings.appearance.ThemeApplicator
import com.github.javiersantos.appupdater.AppUpdater
import com.github.javiersantos.appupdater.enums.UpdateFrom
import java.io.File
import java.io.FileOutputStream
import javax.inject.Inject
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
@ -107,8 +114,20 @@ class MainActivity : AppCompatActivity(R.layout.activity_main), AppNavigation.Pr
)
)
}
is Command.AddToAnytype -> {
SharingFragment.new(command.data).show(
is Command.Sharing.Text -> {
SharingFragment.text(command.data).show(
supportFragmentManager,
SHARE_DIALOG_LABEL
)
}
is Command.Sharing.Image -> {
SharingFragment.image(command.path).show(
supportFragmentManager,
SHARE_DIALOG_LABEL
)
}
is Command.Sharing.Images -> {
SharingFragment.images(command.uris).show(
supportFragmentManager,
SHARE_DIALOG_LABEL
)
@ -121,8 +140,11 @@ class MainActivity : AppCompatActivity(R.layout.activity_main), AppNavigation.Pr
}
}
}
if (savedInstanceState == null && intent.action == Intent.ACTION_SEND) {
proceedWithShareIntent(intent)
if (savedInstanceState == null) {
val action = intent.action
if (action == Intent.ACTION_SEND || action == Intent.ACTION_SEND_MULTIPLE) {
proceedWithShareIntent(intent)
}
}
}
@ -172,6 +194,9 @@ class MainActivity : AppCompatActivity(R.layout.activity_main), AppNavigation.Pr
Intent.ACTION_SEND -> {
proceedWithShareIntent(intent)
}
Intent.ACTION_SEND_MULTIPLE -> {
proceedWithShareIntent(intent)
}
}
}
if (BuildConfig.DEBUG) {
@ -180,8 +205,170 @@ class MainActivity : AppCompatActivity(R.layout.activity_main), AppNavigation.Pr
}
private fun proceedWithShareIntent(intent: Intent) {
intent.getStringExtra(Intent.EXTRA_TEXT)?.let {
vm.onIntentShare(it)
Timber.d("Got intent: $intent")
when {
intent.type == Mimetype.MIME_TEXT_PLAIN.value -> {
vm.onIntentTextShare(intent.getStringExtra(Intent.EXTRA_TEXT).orEmpty())
}
intent.type?.startsWith("image/") == true -> {
if (intent.action == Intent.ACTION_SEND_MULTIPLE) {
val extras = intent
.getParcelableArrayListExtra<Parcelable>(Intent.EXTRA_STREAM)
?: arrayListOf()
val uris = extras.mapNotNull { extra ->
if (extra is Uri)
extra.toString()
else
null
}
Timber.d("Parsed multiple uris: ${uris}")
vm.onIntentMultipleImageShare(uris)
} else {
proceedWithReceivingImage(intent)
}
}
intent.type?.startsWith("application/") == true -> {
if (intent.action == Intent.ACTION_SEND_MULTIPLE) {
// TODO
} else {
proceedWithReceivingFile(intent)
}
Mimetype.MIME_FILE_ALL
}
intent.type == Mimetype.MIME_FILE_ALL.value -> {
if (intent.action == Intent.ACTION_SEND_MULTIPLE) {
val extras = intent
.getParcelableArrayListExtra<Parcelable>(Intent.EXTRA_STREAM)
?: arrayListOf()
val uris = extras.mapNotNull { extra ->
if (extra is Uri)
extra.toString()
else
null
}
Timber.d("Parsed multiple uris: ${uris}")
vm.onIntentMultipleImageShare(uris)
}
}
else -> Timber.e("Unexpected scenario: ${intent.type}")
}
}
private fun proceedWithReceivingFile(intent: Intent) {
val extra = intent.getParcelableExtra<Parcelable>(Intent.EXTRA_STREAM)
if (extra is Uri) {
val name = if (extra.scheme == "content") {
contentResolver.query(
extra,
null,
null,
null,
null
).use { cursor ->
if (cursor != null && cursor.moveToFirst()) {
val idx = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
if (idx != -1) {
cursor.getString(idx)
} else {
"Untitled"
}
} else {
"Untitled"
}
}
} else {
Timber.w("Not content scheme")
extra.path!!.substring(extra.path!!.lastIndexOf("/"))
}
val inputStream = contentResolver.openInputStream(extra)
var path = ""
inputStream?.use { input ->
val newFile = File(cacheDir?.path + "/" + name);
FileOutputStream(newFile).use { output ->
val buffer = ByteArray(1024)
var read: Int = input.read(buffer)
while (read != -1) {
output.write(buffer, 0, read)
read = input.read(buffer)
}
}
path = newFile.path
}
val parsed = runCatching { path }
Timber.d("Parsed APPLICATION uri: ${parsed.getOrNull()} for path: ${extra.path} with scheme: ${extra.scheme}")
parsed.fold(
onSuccess = {
vm.onIntentImageShare(it)
},
onFailure = {
Timber.e(it, "Error while parsing path")
}
)
}
}
private fun proceedWithReceivingImage(intent: Intent) {
val extra = intent.getParcelableExtra<Parcelable>(Intent.EXTRA_STREAM)
if (extra is Uri) {
Timber.d("Uri path: ${extra.path}")
val name = if (extra.scheme == "content") {
contentResolver.query(
extra,
null,
null,
null,
null
).use { cursor ->
val result = if (cursor != null && cursor.moveToFirst()) {
val idx = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
if (idx != -1) {
cursor.getString(idx)
} else {
"Untitled"
}
} else {
"Untitled"
}
cursor?.close()
result
}
} else {
extra.path!!.substring(extra.path!!.lastIndexOf("/"))
}
Timber.d("Extracted name: $name")
val inputStream = contentResolver.openInputStream(extra)
val cacheDir = context.getExternalFilesDirTemp()
if (cacheDir != null && !cacheDir.exists()) {
Timber.d("Created temp dir")
cacheDir.mkdirs()
}
var path = ""
inputStream?.use { input ->
val newFile = File(cacheDir?.path + "/" + name);
FileOutputStream(newFile).use { output ->
val buffer = ByteArray(1024)
var read: Int = input.read(buffer)
while (read != -1) {
output.write(buffer, 0, read)
read = input.read(buffer)
}
}
path = newFile.path
}
val parsed = runCatching { path }
Timber.d("Parse IMAGE uri: ${parsed.getOrNull()} for path: ${extra.path} with scheme: ${extra.scheme}")
parsed.fold(
onSuccess = {
vm.onIntentImageShare(it)
},
onFailure = {
Timber.e(it, "Error while parsing path")
}
)
} else {
Timber.w("URI not found")
}
}

View File

@ -80,14 +80,16 @@ fun AddToAnytypeScreen(
onSelectSpaceClicked: (SpaceView) -> Unit
) {
var isSaveAsMenuExpanded by remember { mutableStateOf(false) }
val items = if (data is SharingData.Url)
listOf(SAVE_AS_NOTE, SAVE_AS_BOOKMARK)
else
listOf(SAVE_AS_NOTE)
val items = when (data) {
is SharingData.Url -> listOf(SAVE_AS_NOTE, SAVE_AS_BOOKMARK)
is SharingData.Image -> listOf(SAVE_AS_IMAGE)
else -> listOf(SAVE_AS_NOTE)
}
var selectedIndex by remember {
mutableStateOf(
when(data) {
is SharingData.Url -> SAVE_AS_BOOKMARK
is SharingData.Image -> SAVE_AS_IMAGE
else -> SAVE_AS_NOTE
}
)
@ -111,10 +113,11 @@ fun AddToAnytypeScreen(
)
Text(
text = if (selectedIndex == SAVE_AS_BOOKMARK)
stringResource(id = R.string.sharing_menu_save_as_bookmark_option)
else
stringResource(id = R.string.sharing_menu_save_as_note_option),
text = when (selectedIndex) {
SAVE_AS_BOOKMARK -> stringResource(id = R.string.sharing_menu_save_as_bookmark_option)
SAVE_AS_IMAGE -> stringResource(id = R.string.sharing_menu_save_as_image_option)
else -> stringResource(id = R.string.sharing_menu_save_as_note_option)
},
modifier = Modifier
.align(Alignment.BottomStart)
.padding(bottom = 14.dp, start = 20.dp),
@ -390,6 +393,7 @@ private fun SmallSpaceIcon(
const val SAVE_AS_NOTE = 0
const val SAVE_AS_BOOKMARK = 1
const val SAVE_AS_IMAGE = 2
typealias SaveAsOption = Int
sealed class SharingData {
@ -402,4 +406,13 @@ sealed class SharingData {
override val data: String
get() = raw
}
data class Image(val path: String) : SharingData() {
override val data: String
get() = path
}
data class Images(val uris: List<String>): SharingData() {
override val data: String
get() = uris.toString()
}
}

View File

@ -15,6 +15,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.fragment.findNavController
import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_utils.ext.arg
import com.anytypeio.anytype.core_utils.ext.argStringList
import com.anytypeio.anytype.core_utils.ext.toast
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetComposeFragment
import com.anytypeio.anytype.di.common.componentManager
@ -22,16 +23,28 @@ import com.anytypeio.anytype.presentation.home.OpenObjectNavigation
import com.anytypeio.anytype.presentation.sharing.AddToAnytypeViewModel
import com.anytypeio.anytype.ui.editor.EditorFragment
import com.anytypeio.anytype.ui.settings.typography
import java.lang.IllegalStateException
import javax.inject.Inject
class SharingFragment : BaseBottomSheetComposeFragment() {
private val sharedData : SharingData get() {
val result = arg<String>(SHARING_DATE_KEY)
return if (URLUtil.isValidUrl(result)) {
SharingData.Url(result)
} else {
SharingData.Raw(result)
val args = requireArguments()
return if (args.containsKey(SHARING_TEXT_KEY)) {
val result = arg<String>(SHARING_TEXT_KEY)
if (URLUtil.isValidUrl(result)) {
SharingData.Url(result)
} else {
SharingData.Raw(result)
}
} else if (args.containsKey(SHARING_IMAGE_KEY)) {
val result = arg<String>(SHARING_IMAGE_KEY)
SharingData.Image(path = result)
} else if (args.containsKey(SHARING_MULTIPLE_IMAGES_KEY)) {
val result = argStringList(SHARING_MULTIPLE_IMAGES_KEY)
SharingData.Images(uris = result)
}else {
throw IllegalStateException("Unexpected result")
}
}
@ -53,10 +66,14 @@ class SharingFragment : BaseBottomSheetComposeFragment() {
AddToAnytypeScreen(
data = sharedData,
onDoneClicked = { option ->
when(option) {
SAVE_AS_BOOKMARK -> vm.onCreateBookmark(url = sharedData.data)
SAVE_AS_NOTE -> vm.onCreateNote(sharedData.data)
}
// when(option) {
// SAVE_AS_BOOKMARK -> vm.onCreateBookmark(url = sharedData.data)
// SAVE_AS_NOTE -> vm.onCreateNote(sharedData.data)
// SAVE_AS_IMAGE -> vm.onUploadImage(sharedData.data)
// }
vm.onShareImages(
uris = (sharedData as SharingData.Images).uris
)
},
onCancelClicked = {
vm.onCancelClicked().also {
@ -120,9 +137,20 @@ class SharingFragment : BaseBottomSheetComposeFragment() {
}
companion object {
private const val SHARING_DATE_KEY = "arg.sharing.data-key"
fun new(data: String) : SharingFragment = SharingFragment().apply {
arguments = bundleOf(SHARING_DATE_KEY to data)
private const val SHARING_TEXT_KEY = "arg.sharing.text-key"
private const val SHARING_IMAGE_KEY = "arg.sharing.image-key"
private const val SHARING_MULTIPLE_IMAGES_KEY = "arg.sharing.multiple-images-key"
fun text(data: String) : SharingFragment = SharingFragment().apply {
arguments = bundleOf(SHARING_TEXT_KEY to data)
}
fun image(image: String) : SharingFragment = SharingFragment().apply {
arguments = bundleOf(SHARING_IMAGE_KEY to image)
}
fun images(uris: List<String>) : SharingFragment = SharingFragment().apply {
arguments = bundleOf(SHARING_MULTIPLE_IMAGES_KEY to ArrayList(uris))
}
}
}

View File

@ -218,7 +218,7 @@ data class Block(
/**
* File block.
* @property hash file hash
* @property targetObjectId id of the target object
* @property name filename
* @property mime mime type
* @property size file size (in bytes)
@ -226,7 +226,7 @@ data class Block(
* @property state file state
*/
data class File(
val hash: String? = null,
val targetObjectId: Id? = null,
val name: String? = null,
val mime: String? = null,
val size: Long? = null,
@ -397,7 +397,8 @@ data class Block(
data class File(
val type: Content.File.Type,
val state: Content.File.State
val state: Content.File.State,
val targetObjectId: Id? = null
) : Prototype()
data class Link(

View File

@ -21,13 +21,14 @@ sealed class Command {
) : Command()
class UploadFile(
val space: SpaceId? = null,
val path: String,
val type: Block.Content.File.Type?
)
class DownloadFile(
val path: String,
val hash: Hash
val objectId: Id
)
/**

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -46,6 +46,10 @@ fun <T : Parcelable> Fragment.argList(key: String): ArrayList<T> {
return checkNotNull(value)
}
fun Fragment.argStringList(key: String): ArrayList<String> {
return requireArguments().getStringArrayList(key) ?: ArrayList()
}
fun <T> CoroutineScope.subscribe(flow: Flow<T>, body: suspend (T) -> Unit): Job =
flow.cancellable().onEach { body(it) }.launchIn(this)

View File

@ -1,9 +1,11 @@
package com.anytypeio.anytype.core_utils.ext
enum class Mimetype(val value: String) {
MIME_TEXT_PLAIN("text/plain"),
MIME_VIDEO_ALL("video/*"),
MIME_IMAGE_ALL("image/*"),
MIME_FILE_ALL("*/*"),
MIME_IMAGE_AND_VIDEO("image/*,video/*"),
MIME_YAML("application/zip")
MIME_YAML("application/zip"),
MIME_APPLICATION_ALL("application/*")
}

View File

@ -256,7 +256,7 @@ class BlockDataRepository(
override suspend fun uploadFile(
command: Command.UploadFile
): Hash = remote.uploadFile(command)
): Id = remote.uploadFile(command)
override suspend fun downloadFile(
command: Command.DownloadFile

View File

@ -80,7 +80,7 @@ interface BlockRemote {
suspend fun paste(command: Command.Paste): Response.Clipboard.Paste
suspend fun copy(command: Command.Copy): Response.Clipboard.Copy
suspend fun uploadFile(command: Command.UploadFile): String
suspend fun uploadFile(command: Command.UploadFile): Id
suspend fun downloadFile(command: Command.DownloadFile): String
suspend fun setRelationKey(command: Command.SetRelationKey): Payload

View File

@ -0,0 +1,70 @@
package com.anytypeio.anytype.device
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Parcelable
import android.provider.OpenableColumns
import com.anytypeio.anytype.domain.device.FileSharer
import java.io.File
import java.io.FileOutputStream
import javax.inject.Inject
import timber.log.Timber
class SharedFileUploader @Inject constructor(
private val context: Context
) : FileSharer {
override fun getPath(uri: String): String {
Timber.d("Getting path for: ${uri}")
val parsed = Uri.parse(uri)
checkNotNull(parsed)
return parsePathFromFile(parsed)
}
private fun parsePathFromFile(extra: Uri) : String {
val name = if (extra.scheme == "content") {
context.contentResolver.query(
extra,
null,
null,
null,
null
).use { cursor ->
if (cursor != null && cursor.moveToFirst()) {
val idx = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
if (idx != -1) {
cursor.getString(idx)
} else {
"Untitled"
}
} else {
"Untitled"
}
}
} else {
extra.path!!.substring(extra.path!!.lastIndexOf("/"))
}
val inputStream = context.contentResolver.openInputStream(extra)
val cacheDir = context.getExternalFilesDir(null)
if (cacheDir != null && !cacheDir.exists()) {
cacheDir.mkdirs()
}
var path = ""
inputStream?.use { input ->
val newFile = File(cacheDir?.path + "/" + name);
FileOutputStream(newFile).use { output ->
val buffer = ByteArray(1024)
var read: Int = input.read(buffer)
while (read != -1) {
output.write(buffer, 0, read)
read = input.read(buffer)
}
}
path = newFile.path
}
return path
}
}

View File

@ -33,7 +33,7 @@ import com.anytypeio.anytype.domain.page.Undo
interface BlockRepository {
suspend fun uploadFile(command: Command.UploadFile): Hash
suspend fun uploadFile(command: Command.UploadFile): Id
suspend fun downloadFile(command: Command.DownloadFile): String
suspend fun move(command: Command.Move): Payload

View File

@ -0,0 +1,5 @@
package com.anytypeio.anytype.domain.device
interface FileSharer {
fun getPath(uri: String) : String?
}

View File

@ -0,0 +1,26 @@
package com.anytypeio.anytype.domain.media
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.Command
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.primitives.SpaceId
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
import com.anytypeio.anytype.domain.base.ResultInteractor
import com.anytypeio.anytype.domain.block.repo.BlockRepository
import javax.inject.Inject
class UploadFile @Inject constructor(
private val repo: BlockRepository,
private val dispatchers: AppCoroutineDispatchers
) : ResultInteractor<UploadFile.Params, Id>(dispatchers.io) {
override suspend fun doWork(params: Params) : Id = repo.uploadFile(
command = Command.UploadFile(
path = params.path,
type = Block.Content.File.Type.FILE,
space = params.space
)
)
data class Params(val path: String, val space: SpaceId)
}

View File

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

View File

@ -40,6 +40,19 @@ class CreatePrefilledNote @Inject constructor(
target = NO_VALUE
)
)
params.attachments.forEach { attachment ->
repo.create(
command = Command.Create(
context = obj.id,
prototype = Block.Prototype.Link(
target = attachment,
cardStyle = Block.Content.Link.CardStyle.CARD
),
position = Position.NONE,
target = NO_VALUE
)
)
}
return obj.id
}
@ -50,6 +63,7 @@ class CreatePrefilledNote @Inject constructor(
val space: Id,
val text: String,
val details: Struct,
val customType: TypeKey? = null
val customType: TypeKey? = null,
val attachments: List<Id> = emptyList()
)
}

View File

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

View File

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

View File

@ -193,7 +193,7 @@
<string name="hint_upload_image">Upload a picture</string>
<string name="hint_upload_file">Upload a file</string>
<string name="hint_upload_audio">Upload an audio</string>
<string name="hint_add_a_web_bookmark">Add a web bookmark</string>
<string name="hint_add_a_web_bookmark">Add bookmark</string>
<string name="hint_bullet">Bulleted list item</string>
<string name="hint_numbered_list">Numbered list item</string>
<string name="hint_toggle">Toggle block</string>
@ -808,6 +808,7 @@
<string name="anytype_analytics">Anytype Analytics</string>
<string name="anytype_analytics_msg">Understanding how people use Anytype helps us improve the product. This version of Anytype includes the analytics code that protects your privacy.\nIt doesn\'t record the actual document\'s content but still allows us to understand how you use Anytype.\nStay subscribed to our mailing list, as we will soon announce a new release that enables you to opt-out.</string>
<string name="retry">Retry</string>
<string name="try_again">Try again</string>
<string name="unsplash">Unsplash</string>
<string name="cancel_deletion">Cancel deletion</string>
<string name="logout_and_clear_local_data">Logout and clear local data</string>
@ -1191,6 +1192,7 @@
<string name="sharing_menu_save_as_section_name">Save as</string>
<string name="sharing_menu_save_as_note_option">Note</string>
<string name="sharing_menu_save_as_bookmark_option">Bookmark</string>
<string name="sharing_menu_save_as_image_option">Image</string>
<string name="sharing_menu_data">Data</string>
<string name="sharing_menu_add_to_anytype_header_title">Add to Anytype</string>
<string name="sharing_menu_toast_object_added">New object is added to the space \'%1$s\'</string>
@ -1249,4 +1251,5 @@
<string name="any_object_creation_menu_move_right">Move right</string>
<string name="clipboard_panel_create_object_from_clipboard">Create object from clipboard</string>
<string name="block_media_error">Something went wrong, please try again</string>
</resources>

View File

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

View File

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

View File

@ -34,6 +34,8 @@ import com.anytypeio.anytype.core_utils.tools.ThreadInfo
import com.anytypeio.anytype.middleware.BuildConfig
import com.anytypeio.anytype.middleware.auth.toAccountSetup
import com.anytypeio.anytype.middleware.const.Constants
import com.anytypeio.anytype.middleware.mappers.MBFile
import com.anytypeio.anytype.middleware.mappers.MBFileType
import com.anytypeio.anytype.middleware.mappers.MDVFilter
import com.anytypeio.anytype.middleware.mappers.MNetworkMode
import com.anytypeio.anytype.middleware.mappers.MRelationFormat
@ -821,18 +823,19 @@ class Middleware @Inject constructor(
val type = command.type.toMiddlewareModel()
val request = Rpc.File.Upload.Request(
localPath = command.path,
type = type
type = MBFileType.None,
spaceId = command.space?.id.orEmpty()
)
if (BuildConfig.DEBUG) logRequest(request)
val response = service.fileUpload(request)
if (BuildConfig.DEBUG) logResponse(response)
return Response.Media.Upload(response.hash)
return Response.Media.Upload(response.objectId)
}
@Throws(Exception::class)
fun fileDownload(command: Command.DownloadFile): Rpc.File.Download.Response {
val request = Rpc.File.Download.Request(
hash = command.hash,
objectId = command.objectId,
path = command.path
)
if (BuildConfig.DEBUG) logRequest(request)

View File

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

View File

@ -46,7 +46,8 @@ class MiddlewareFactory {
is Block.Prototype.File -> {
val file = MBFile(
state = prototype.state.toMiddlewareModel(),
type = prototype.type.toMiddlewareModel()
type = prototype.type.toMiddlewareModel(),
targetObjectId = prototype.targetObjectId.orEmpty()
)
MBlock(file_ = file)
}

View File

@ -330,7 +330,7 @@ fun MBlock.toCoreModelsDivider(): Block.Content.Divider {
fun MBlock.toCoreModelsFile(): Block.Content.File {
val content = checkNotNull(file_)
return Block.Content.File(
hash = content.hash,
targetObjectId = content.targetObjectId,
name = content.name,
mime = content.mime,
size = content.size,

View File

@ -113,7 +113,7 @@ fun Block.Content.Bookmark.State.toMiddlewareModel(): MBookmarkState = when (thi
}
fun Block.Content.File.toMiddlewareModel(): MBFile = MBFile(
hash = hash.orEmpty(),
targetObjectId = targetObjectId.orEmpty(),
name = name.orEmpty(),
mime = mime.orEmpty(),
size = size ?: 0,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -164,13 +164,41 @@ class MainViewModel(
}
}
fun onIntentShare(data: String) {
fun onIntentTextShare(data: String) {
viewModelScope.launch {
checkAuthorizationStatus(Unit).process(
failure = { e -> Timber.e(e, "Error while checking auth status") },
success = { status ->
if (status == AuthStatus.AUTHORIZED) {
commands.emit(Command.AddToAnytype(data))
commands.emit(Command.Sharing.Text(data))
}
}
)
}
}
fun onIntentImageShare(data: String) {
Timber.d("onIntentImageShare: $data")
viewModelScope.launch {
checkAuthorizationStatus(Unit).process(
failure = { e -> Timber.e(e, "Error while checking auth status") },
success = { status ->
if (status == AuthStatus.AUTHORIZED) {
commands.emit(Command.Sharing.Image(data))
}
}
)
}
}
fun onIntentMultipleImageShare(uris: List<String>) {
Timber.d("onIntentMultipleImageShare: $uris")
viewModelScope.launch {
checkAuthorizationStatus(Unit).process(
failure = { e -> Timber.e(e, "Error while checking auth status") },
success = { status ->
if (status == AuthStatus.AUTHORIZED) {
commands.emit(Command.Sharing.Images(uris))
}
}
)
@ -182,6 +210,10 @@ class MainViewModel(
object LogoutDueToAccountDeletion : Command()
class OpenCreateNewType(val type: Id) : Command()
data class Error(val msg: String) : Command()
data class AddToAnytype(val data: String): Command()
sealed class Sharing : Command() {
data class Text(val data: String) : Sharing()
data class Image(val path: String): Sharing()
data class Images(val uris: List<String>): Sharing()
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -11,14 +11,19 @@ import com.anytypeio.anytype.analytics.base.EventsDictionary.CLICK_ONBOARDING_TO
import com.anytypeio.anytype.analytics.base.EventsPropertiesKey
import com.anytypeio.anytype.analytics.event.EventAnalytics
import com.anytypeio.anytype.analytics.props.Props
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.MarketplaceObjectTypeIds
import com.anytypeio.anytype.core_models.NO_VALUE
import com.anytypeio.anytype.core_models.ObjectOrigin
import com.anytypeio.anytype.core_models.ObjectWrapper
import com.anytypeio.anytype.core_models.Relations
import com.anytypeio.anytype.core_models.primitives.SpaceId
import com.anytypeio.anytype.core_utils.ext.msg
import com.anytypeio.anytype.domain.account.AwaitAccountStartManager
import com.anytypeio.anytype.domain.base.fold
import com.anytypeio.anytype.domain.base.onSuccess
import com.anytypeio.anytype.domain.device.FileSharer
import com.anytypeio.anytype.domain.media.UploadFile
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.objects.CreateBookmarkObject
import com.anytypeio.anytype.domain.objects.CreatePrefilledNote
@ -31,6 +36,8 @@ import com.anytypeio.anytype.presentation.spaces.SpaceGradientProvider
import com.anytypeio.anytype.presentation.spaces.SpaceIconView
import com.anytypeio.anytype.presentation.spaces.spaceIcon
import javax.inject.Inject
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
@ -48,7 +55,9 @@ class AddToAnytypeViewModel(
private val getSpaceViews: GetSpaceViews,
private val urlBuilder: UrlBuilder,
private val awaitAccountStartManager: AwaitAccountStartManager,
private val analytics: Analytics
private val analytics: Analytics,
private val uploadFile: UploadFile,
private val fileSharer: FileSharer
) : BaseViewModel() {
private val selectedSpaceId = MutableStateFlow(NO_VALUE)
@ -108,6 +117,62 @@ class AddToAnytypeViewModel(
}
}
fun onShareImages(uris: List<String>) {
viewModelScope.launch(Dispatchers.IO) {
delay(3000)
val targetSpaceView = spaceViews.value.firstOrNull { view ->
view.isSelected
}
val targetSpaceId = targetSpaceView?.obj?.targetSpaceId!!
val paths = uris.mapNotNull { uri ->
fileSharer.getPath(uri)
}
val files = mutableListOf<Id>()
paths.forEach { path ->
uploadFile.async(
UploadFile.Params(
path = path,
space = SpaceId(targetSpaceId)
)
).onSuccess { obj ->
files.add(obj)
}
}
val startTime = System.currentTimeMillis()
createPrefilledNote.async(
CreatePrefilledNote.Params(
text = "Here are your files",
space = targetSpaceId,
details = mapOf(
Relations.ORIGIN to ObjectOrigin.SHARING_EXTENSION.code.toDouble()
),
attachments = files
)
).fold(
onSuccess = { result ->
sendAnalyticsObjectCreateEvent(
analytics = analytics,
objType = MarketplaceObjectTypeIds.NOTE,
route = EventsDictionary.Routes.sharingExtension,
startTime = startTime
)
if (targetSpaceId == spaceManager.get()) {
navigation.emit(OpenObjectNavigation.OpenEditor(result))
} else {
with(commands) {
emit(Command.ObjectAddToSpaceToast(targetSpaceView.obj.name))
emit(Command.Dismiss)
}
}
},
onFailure = {
Timber.d(it, "Error while creating note")
sendToast("Error while creating note: ${it.msg()}")
}
)
}
}
fun onCreateBookmark(url: String) {
viewModelScope.launch {
val targetSpaceView = spaceViews.value.firstOrNull { view ->
@ -192,6 +257,32 @@ class AddToAnytypeViewModel(
}
}
fun onUploadImage(path: String) {
Timber.d("Attempt to share image: $path")
viewModelScope.launch {
val targetSpaceView = spaceViews.value.firstOrNull { view ->
view.isSelected
}
val targetSpaceId = targetSpaceView?.obj?.targetSpaceId!!
val params = UploadFile.Params(
path = path,
space = SpaceId(targetSpaceId)
)
Timber.d("Uploading file with params: $params")
uploadFile.async(params).fold(
onSuccess = { obj: Id ->
Timber.d("Successfully upload file")
if (targetSpaceId == spaceManager.get()) {
navigation.emit(OpenObjectNavigation.OpenEditor(obj))
}
},
onFailure = {
Timber.e(it, "Error while uploading file")
}
)
}
}
fun onSelectSpaceClicked(view: SpaceView) {
Timber.d("onSelectSpaceClicked: ${view.obj.targetSpaceId}")
viewModelScope.launch {
@ -225,7 +316,9 @@ class AddToAnytypeViewModel(
private val getSpaceViews: GetSpaceViews,
private val urlBuilder: UrlBuilder,
private val awaitAccountStartManager: AwaitAccountStartManager,
private val analytics: Analytics
private val analytics: Analytics,
private val uploadFile: UploadFile,
private val fileSharer: FileSharer
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
@ -236,7 +329,9 @@ class AddToAnytypeViewModel(
getSpaceViews = getSpaceViews,
urlBuilder = urlBuilder,
awaitAccountStartManager = awaitAccountStartManager,
analytics = analytics
analytics = analytics,
uploadFile = uploadFile,
fileSharer = fileSharer
) as T
}
}

View File

@ -308,7 +308,7 @@ private fun Context.deleteTemporaryFolder() {
/**
* Return /storage/emulated/0/Android/data/package/files/$TEMPORARY_DIRECTORY_NAME directory
*/
private fun Context.getExternalFilesDirTemp(): File? = getExternalFilesDir(TEMPORARY_DIRECTORY_NAME)
fun Context.getExternalFilesDirTemp(): File? = getExternalFilesDir(TEMPORARY_DIRECTORY_NAME)
/**
* Return /storage/emulated/0/Android/data/io.anytype.app/files/networkModeConfig directory

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2572,6 +2572,32 @@ message Rpc {
}
}
}
message SpaceOffload {
message Request {
string spaceId = 1;
}
message Response {
Error error = 1;
int32 filesOffloaded = 2;
uint64 bytesOffloaded = 3;
message Error {
Code code = 1;
string description = 2;
enum Code {
NULL = 0;
UNKNOWN_ERROR = 1;
BAD_INPUT = 2;
// ...
NODE_NOT_STARTED = 103;
}
}
}
}
message ListOffload {
message Request {
repeated string onlyIds = 1; // empty means all
@ -2605,12 +2631,13 @@ message Rpc {
anytype.model.Block.Content.File.Type type = 3;
bool disableEncryption = 4; // deprecated, has no affect, GO-1926
anytype.model.Block.Content.File.Style style = 5;
google.protobuf.Struct details = 7; // additional details for file object
}
message Response {
Error error = 1;
string hash = 2;
string objectId = 2;
google.protobuf.Struct details = 3;
message Error {
Code code = 1;
@ -2626,7 +2653,7 @@ message Rpc {
}
message Download {
message Request {
string hash = 1;
string objectId = 1;
string path = 2; // path to save file. Temp directory is used if empty
}
@ -2942,7 +2969,7 @@ message Rpc {
message Response {
Error error = 1;
string hash = 2;
string objectId = 2;
message Error {
Code code = 1;

View File

@ -370,6 +370,7 @@ message Event {
Name name = 6;
Size size = 7;
Style style = 8;
TargetObjectId targetObjectId = 9;
message Name {
string value = 1;
@ -403,6 +404,9 @@ message Event {
int64 value = 1;
}
message TargetObjectId {
string value = 1;
}
}

Some files were not shown because too many files have changed in this diff Show More