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:
parent
9dd320f2f6
commit
13014ee490
19 changed files with 118 additions and 33 deletions
|
@ -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(
|
||||
|
|
|
@ -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 -> {
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
||||
/**
|
||||
|
|
|
@ -31,7 +31,8 @@ sealed class Response {
|
|||
|
||||
sealed class Media : Response() {
|
||||
class Upload(
|
||||
val objectId: Id
|
||||
val objectId: Id,
|
||||
val details: Struct
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 -> {
|
||||
|
|
|
@ -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()
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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 = ", "
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue