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

DROID-2072 Sharing extension | Enhancement | Display filename in the content field (#840)

This commit is contained in:
Evgenii Kozlov 2024-02-08 11:02:29 +01:00 committed by GitHub
parent 9dd320f2f6
commit 13014ee490
Signed by: github
GPG key ID: B5690EEEBB952194
19 changed files with 118 additions and 33 deletions

View file

@ -55,7 +55,8 @@ fun AddToAnytypeScreenUrlPreview() {
onCancelClicked = {},
onDoneClicked = {},
spaces = emptyList(),
onSelectSpaceClicked = {}
onSelectSpaceClicked = {},
content = "https://en.wikipedia.org/wiki/Walter_Benjamin"
)
}
@ -67,12 +68,14 @@ fun AddToAnytypeScreenNotePreview() {
onCancelClicked = {},
onDoneClicked = {},
spaces = emptyList(),
onSelectSpaceClicked = {}
onSelectSpaceClicked = {},
content = ""
)
}
@Composable
fun AddToAnytypeScreen(
content: String,
spaces: List<SpaceView>,
data: SharingData,
onCancelClicked: () -> Unit,
@ -103,7 +106,7 @@ fun AddToAnytypeScreen(
Column(modifier = Modifier.fillMaxWidth()) {
Header()
DataSection(data)
DataSection(content)
Box(
modifier = Modifier
.fillMaxWidth()
@ -199,7 +202,7 @@ fun AddToAnytypeScreen(
}
@Composable
private fun DataSection(data: SharingData) {
private fun DataSection(content: String) {
Box(
modifier = Modifier
.fillMaxWidth()
@ -222,7 +225,7 @@ private fun DataSection(data: SharingData) {
)
)
Text(
text = data.data,
text = content,
style = BodyRegular,
color = colorResource(id = R.color.text_primary),
modifier = Modifier.padding(

View file

@ -7,6 +7,7 @@ import android.view.ViewGroup
import android.webkit.URLUtil
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.core.os.bundleOf
@ -25,6 +26,7 @@ import com.anytypeio.anytype.ui.editor.EditorFragment
import com.anytypeio.anytype.ui.settings.typography
import java.lang.IllegalStateException
import javax.inject.Inject
import kotlinx.coroutines.flow.map
class SharingFragment : BaseBottomSheetComposeFragment() {
@ -71,6 +73,12 @@ class SharingFragment : BaseBottomSheetComposeFragment() {
typography = typography
) {
AddToAnytypeScreen(
content = vm.state.map { state ->
when (state) {
is AddToAnytypeViewModel.ViewState.Default -> state.content
AddToAnytypeViewModel.ViewState.Init -> ""
}
}.collectAsState(initial = "").value,
data = sharedData,
onDoneClicked = { option ->
when(option) {
@ -136,6 +144,30 @@ class SharingFragment : BaseBottomSheetComposeFragment() {
}
}
override fun onStart() {
super.onStart()
when(val data = sharedData) {
is SharingData.File -> {
vm.onSharedMediaData(listOf(data.uri))
}
is SharingData.Files -> {
vm.onSharedMediaData(data.uris)
}
is SharingData.Image -> {
vm.onSharedMediaData(listOf(data.uri))
}
is SharingData.Images -> {
vm.onSharedMediaData(data.uris)
}
is SharingData.Text -> {
vm.onSharedTextData(data.raw)
}
is SharingData.Url -> {
vm.onSharedTextData(data.url)
}
}
}
private fun proceedWithCommand(command: AddToAnytypeViewModel.Command) {
when (command) {
AddToAnytypeViewModel.Command.Dismiss -> {

View file

@ -306,12 +306,12 @@ sealed class Command {
/**
* Command for setting document's image icon
* @property hash image hash
* @property id image hash
* @property context id of the context for this operation
*/
data class SetDocumentImageIcon(
val context: Id,
val hash: Hash
val id: Hash
)
/**

View file

@ -31,7 +31,8 @@ sealed class Response {
sealed class Media : Response() {
class Upload(
val objectId: Id
val objectId: Id,
val details: Struct
)
}

View file

@ -256,7 +256,7 @@ class BlockDataRepository(
override suspend fun uploadFile(
command: Command.UploadFile
): Id = remote.uploadFile(command)
): ObjectWrapper.File = 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): Id
suspend fun uploadFile(command: Command.UploadFile): ObjectWrapper.File
suspend fun downloadFile(command: Command.DownloadFile): String
suspend fun setRelationKey(command: Command.SetRelationKey): Payload

View file

@ -28,6 +28,28 @@ class SharedFileUploader @Inject constructor(
TODO("Not yet implemented")
}
override suspend fun getDisplayName(uri: String): String? = withContext(dispatchers.io) {
val parsed = Uri.parse(uri)
context.contentResolver.query(
parsed,
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 {
context.resources.getString(R.string.untitled)
}
} else {
context.resources.getString(R.string.untitled)
}
}
}
private fun parsePathFromUri(extra: Uri) : String {
val name = if (extra.scheme == CONTENT_URI_SCHEME) {
context.contentResolver.query(

View file

@ -54,7 +54,7 @@ class GetLastOpenedObject(
object Empty : Response()
/**
* The last opened object could not be found. It might habe been deleted.
* The last opened object could not be found. It might have been deleted.
*/
data class NotFound(val id: Id) : Response()

View file

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

View file

@ -15,7 +15,7 @@ class SetDocCoverImage(
override suspend fun run(params: Params) = safe {
when (params) {
is Params.FromPath -> {
val hash = repo.uploadFile(
val file = repo.uploadFile(
command = Command.UploadFile(
path = params.path,
type = Block.Content.File.Type.IMAGE,
@ -24,7 +24,7 @@ class SetDocCoverImage(
)
repo.setDocumentCoverImage(
ctx = params.context,
hash = hash
hash = file.id
)
}
is Params.FromHash -> {

View file

@ -2,5 +2,6 @@ package com.anytypeio.anytype.domain.device
interface FileSharer {
suspend fun getPath(uri: String) : String?
suspend fun getDisplayName(uri: String) : String?
suspend fun clear()
}

View file

@ -10,7 +10,7 @@ class SetDocumentImageIcon(
) : SetImageIcon<Id>() {
override suspend fun run(params: Params<Id>) = safe {
val hash = repo.uploadFile(
val file = repo.uploadFile(
command = Command.UploadFile(
path = params.path,
type = Block.Content.File.Type.IMAGE
@ -18,10 +18,10 @@ class SetDocumentImageIcon(
)
val payload = repo.setDocumentImageIcon(
command = Command.SetDocumentImageIcon(
hash = hash,
id = file.id,
context = params.target
)
)
Pair(payload, hash)
Pair(payload, file.id)
}
}

View file

@ -11,7 +11,7 @@ class SetTextBlockImage(
override suspend fun run(
params: Params<TextBlockTarget>
) = safe {
val hash = repo.uploadFile(
val file = repo.uploadFile(
command = Command.UploadFile(
path = params.path,
type = Block.Content.File.Type.IMAGE
@ -19,11 +19,11 @@ class SetTextBlockImage(
)
val payload = repo.setTextIcon(
command = Command.SetTextIcon(
icon = Command.SetTextIcon.Icon.Image(hash),
icon = Command.SetTextIcon.Icon.Image(file.id),
context = params.target.context,
blockId = params.target.blockId
)
)
Pair(payload, hash)
Pair(payload, file.id)
}
}

View file

@ -3,6 +3,7 @@ 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.ObjectWrapper
import com.anytypeio.anytype.core_models.primitives.SpaceId
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
import com.anytypeio.anytype.domain.base.ResultInteractor
@ -12,9 +13,9 @@ import javax.inject.Inject
class UploadFile @Inject constructor(
private val repo: BlockRepository,
private val dispatchers: AppCoroutineDispatchers
) : ResultInteractor<UploadFile.Params, Id>(dispatchers.io) {
) : ResultInteractor<UploadFile.Params, ObjectWrapper.File>(dispatchers.io) {
override suspend fun doWork(params: Params) : Id = repo.uploadFile(
override suspend fun doWork(params: Params) : ObjectWrapper.File = repo.uploadFile(
command = Command.UploadFile(
path = params.path,
type = params.type,

View file

@ -13,14 +13,14 @@ class AddFileToObject(
) : BaseUseCase<Payload, AddFileToObject.Params>() {
override suspend fun run(params: Params): Either<Throwable, Payload> = safe {
val hash = repo.uploadFile(
val file = repo.uploadFile(
command = Command.UploadFile(
path = params.path,
type = null
)
)
val obj = params.obj
val remaining = obj[params.relation].addIds(listOf(hash))
val remaining = obj[params.relation].addIds(listOf(file.id))
repo.setObjectDetail(
ctx = params.ctx,
key = params.relation,

View file

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

View file

@ -34,8 +34,6 @@ 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
@ -819,7 +817,7 @@ class Middleware @Inject constructor(
}
@Throws(Exception::class)
fun fileUpload(command: Command.UploadFile): Response.Media.Upload {
fun fileUpload(command: Command.UploadFile): ObjectWrapper.File {
val type = command.type.toMiddlewareModel()
val request = Rpc.File.Upload.Request(
localPath = command.path,
@ -829,7 +827,7 @@ class Middleware @Inject constructor(
if (BuildConfig.DEBUG) logRequest(request)
val response = service.fileUpload(request)
if (BuildConfig.DEBUG) logResponse(response)
return Response.Media.Upload(response.objectId)
return ObjectWrapper.File(response.details.orEmpty())
}
@Throws(Exception::class)
@ -1449,7 +1447,7 @@ class Middleware @Inject constructor(
val imageDetail = Rpc.Object.SetDetails.Detail(
key = Relations.ICON_IMAGE,
value_ = command.hash
value_ = command.id
)
val emojiDetail = Rpc.Object.SetDetails.Detail(
key = Relations.ICON_EMOJI,

View file

@ -285,14 +285,14 @@ class MiddlewareTest {
val command = Command.SetDocumentImageIcon(
context = MockDataFactory.randomUuid(),
hash = MockDataFactory.randomUuid()
id = MockDataFactory.randomUuid()
)
val response = Rpc.Object.SetDetails.Response(event = ResponseEvent())
val imageIconKey = "iconImage"
val imageIconValue = command.hash
val imageIconValue = command.id
val imageIconDetail = Rpc.Object.SetDetails.Detail(imageIconKey, imageIconValue)

View file

@ -69,6 +69,8 @@ class AddToAnytypeViewModel(
val spaceViews = MutableStateFlow<List<SpaceView>>(emptyList())
val commands = MutableSharedFlow<Command>()
val state = MutableStateFlow<ViewState>(ViewState.Init)
init {
viewModelScope.launch {
analytics.registerEvent(
@ -137,7 +139,7 @@ class AddToAnytypeViewModel(
type = Block.Content.File.Type.NONE
)
).onSuccess { obj ->
files.add(obj)
files.add(obj.id)
}
}
if (files.size == 1) {
@ -296,6 +298,22 @@ class AddToAnytypeViewModel(
}
}
fun onSharedMediaData(uris: List<String>) {
viewModelScope.launch {
state.value = ViewState.Default(
uris.mapNotNull { uri ->
fileSharer.getDisplayName(uri)
}.joinToString(separator = FILE_NAME_SEPARATOR)
)
}
}
fun onSharedTextData(text: String) {
viewModelScope.launch {
state.value = ViewState.Default(text)
}
}
class Factory @Inject constructor(
private val createBookmarkObject: CreateBookmarkObject,
private val createPrefilledNote: CreatePrefilledNote,
@ -335,4 +353,13 @@ class AddToAnytypeViewModel(
val spaceName: String?
) : Command()
}
sealed class ViewState {
object Init : ViewState()
data class Default(val content: String) : ViewState()
}
companion object {
const val FILE_NAME_SEPARATOR = ", "
}
}