mirror of
https://github.com/anyproto/anytype-kotlin.git
synced 2025-06-08 05:47:05 +09:00
DROID-3444 Chats | Enhancement | Bookmark flow updates (#2399)
This commit is contained in:
parent
191b675fc9
commit
1d8501f7a0
20 changed files with 282 additions and 15 deletions
|
@ -0,0 +1,17 @@
|
|||
package com.anytypeio.anytype.core_models
|
||||
|
||||
data class LinkPreview(
|
||||
val url: String,
|
||||
val title: String = "",
|
||||
val description: String = "",
|
||||
val imageUrl: String = "",
|
||||
val faviconUrl: String = "",
|
||||
val type: Type = Type.Unknown
|
||||
) {
|
||||
enum class Type {
|
||||
Unknown,
|
||||
Page,
|
||||
Image,
|
||||
Text;
|
||||
}
|
||||
}
|
|
@ -14,6 +14,7 @@ import com.anytypeio.anytype.core_models.DeviceNetworkType
|
|||
import com.anytypeio.anytype.core_models.Event
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.Key
|
||||
import com.anytypeio.anytype.core_models.LinkPreview
|
||||
import com.anytypeio.anytype.core_models.ManifestInfo
|
||||
import com.anytypeio.anytype.core_models.NodeUsageInfo
|
||||
import com.anytypeio.anytype.core_models.ObjectType
|
||||
|
@ -1122,4 +1123,9 @@ class BlockDataRepository(
|
|||
override suspend fun setDataViewProperties(command: Command.SetDataViewProperties): Payload {
|
||||
return remote.setDataViewProperties(command)
|
||||
}
|
||||
|
||||
override suspend fun getLinkPreview(url: Url): LinkPreview = remote.getLinkPreview(url)
|
||||
|
||||
override suspend fun createObjectFromUrl(space: SpaceId, url: Url): ObjectWrapper.Basic =
|
||||
remote.createObjectFromUrl(space = space, url = url)
|
||||
}
|
|
@ -14,6 +14,7 @@ import com.anytypeio.anytype.core_models.DeviceNetworkType
|
|||
import com.anytypeio.anytype.core_models.Event
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.Key
|
||||
import com.anytypeio.anytype.core_models.LinkPreview
|
||||
import com.anytypeio.anytype.core_models.ManifestInfo
|
||||
import com.anytypeio.anytype.core_models.NodeUsageInfo
|
||||
import com.anytypeio.anytype.core_models.ObjectType
|
||||
|
@ -476,4 +477,8 @@ interface BlockRemote {
|
|||
suspend fun objectTypeSetRecommendedFields(command: Command.ObjectTypeSetRecommendedFields)
|
||||
|
||||
suspend fun setDataViewProperties(command: Command.SetDataViewProperties): Payload
|
||||
|
||||
suspend fun getLinkPreview(url: Url): LinkPreview
|
||||
|
||||
suspend fun createObjectFromUrl(space: SpaceId, url: Url): ObjectWrapper.Basic
|
||||
}
|
|
@ -14,6 +14,7 @@ import com.anytypeio.anytype.core_models.DeviceNetworkType
|
|||
import com.anytypeio.anytype.core_models.Event
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.Key
|
||||
import com.anytypeio.anytype.core_models.LinkPreview
|
||||
import com.anytypeio.anytype.core_models.ManifestInfo
|
||||
import com.anytypeio.anytype.core_models.NodeUsageInfo
|
||||
import com.anytypeio.anytype.core_models.ObjectType
|
||||
|
@ -519,4 +520,8 @@ interface BlockRepository {
|
|||
suspend fun objectTypeSetRecommendedFields(command: Command.ObjectTypeSetRecommendedFields)
|
||||
|
||||
suspend fun setDataViewProperties(command: Command.SetDataViewProperties): Payload
|
||||
|
||||
suspend fun getLinkPreview(url: Url): LinkPreview
|
||||
|
||||
suspend fun createObjectFromUrl(space: SpaceId, url: Url): ObjectWrapper.Basic
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package com.anytypeio.anytype.domain.misc
|
||||
|
||||
import com.anytypeio.anytype.core_models.LinkPreview
|
||||
import com.anytypeio.anytype.core_models.Url
|
||||
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 GetLinkPreview @Inject constructor(
|
||||
private val repo: BlockRepository,
|
||||
dispatchers: AppCoroutineDispatchers
|
||||
) : ResultInteractor<Url, LinkPreview>(dispatchers.io) {
|
||||
override suspend fun doWork(params: Url): LinkPreview {
|
||||
return repo.getLinkPreview(url = params)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package com.anytypeio.anytype.domain.objects
|
||||
|
||||
import com.anytypeio.anytype.core_models.ObjectWrapper
|
||||
import com.anytypeio.anytype.core_models.Url
|
||||
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 CreateObjectFromUrl @Inject constructor(
|
||||
private val repository: BlockRepository,
|
||||
dispatchers: AppCoroutineDispatchers
|
||||
) : ResultInteractor<CreateObjectFromUrl.Params, ObjectWrapper.Basic>(dispatchers.io) {
|
||||
|
||||
override suspend fun doWork(params: Params): ObjectWrapper.Basic {
|
||||
return repository.createObjectFromUrl(
|
||||
space = params.space,
|
||||
url = params.url
|
||||
)
|
||||
}
|
||||
|
||||
data class Params(
|
||||
val space: SpaceId,
|
||||
val url: Url
|
||||
)
|
||||
}
|
|
@ -3,6 +3,7 @@ package com.anytypeio.anytype.feature_chats.presentation
|
|||
import com.anytypeio.anytype.core_models.Block
|
||||
import com.anytypeio.anytype.core_models.Hash
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.LinkPreview
|
||||
import com.anytypeio.anytype.core_models.ObjectWrapper
|
||||
import com.anytypeio.anytype.core_models.Url
|
||||
import com.anytypeio.anytype.domain.chats.ChatContainer
|
||||
|
@ -129,6 +130,10 @@ sealed interface ChatView {
|
|||
val wrapper: GlobalSearchItemView
|
||||
): ChatBoxAttachment()
|
||||
|
||||
data class Bookmark(
|
||||
val preview: LinkPreview
|
||||
) : ChatBoxAttachment()
|
||||
|
||||
sealed class State {
|
||||
data object Idle : State()
|
||||
data object Uploading : State()
|
||||
|
|
|
@ -4,8 +4,10 @@ import androidx.lifecycle.viewModelScope
|
|||
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.LinkPreview
|
||||
import com.anytypeio.anytype.core_models.ObjectType
|
||||
import com.anytypeio.anytype.core_models.ObjectWrapper
|
||||
import com.anytypeio.anytype.core_models.Url
|
||||
import com.anytypeio.anytype.core_models.chats.Chat
|
||||
import com.anytypeio.anytype.core_models.ext.EMPTY_STRING_VALUE
|
||||
import com.anytypeio.anytype.core_models.primitives.Space
|
||||
|
@ -23,10 +25,12 @@ import com.anytypeio.anytype.domain.chats.DeleteChatMessage
|
|||
import com.anytypeio.anytype.domain.chats.EditChatMessage
|
||||
import com.anytypeio.anytype.domain.chats.ToggleChatMessageReaction
|
||||
import com.anytypeio.anytype.domain.media.UploadFile
|
||||
import com.anytypeio.anytype.domain.misc.GetLinkPreview
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.domain.multiplayer.ActiveSpaceMemberSubscriptionContainer
|
||||
import com.anytypeio.anytype.domain.multiplayer.ActiveSpaceMemberSubscriptionContainer.Store
|
||||
import com.anytypeio.anytype.domain.multiplayer.SpaceViewSubscriptionContainer
|
||||
import com.anytypeio.anytype.domain.objects.CreateObjectFromUrl
|
||||
import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
|
||||
import com.anytypeio.anytype.domain.objects.getTypeOfObject
|
||||
import com.anytypeio.anytype.feature_chats.BuildConfig
|
||||
|
@ -76,6 +80,8 @@ class ChatViewModel @Inject constructor(
|
|||
private val storeOfObjectTypes: StoreOfObjectTypes,
|
||||
private val copyFileToCacheDirectory: CopyFileToCacheDirectory,
|
||||
private val exitToVaultDelegate: ExitToVaultDelegate,
|
||||
private val getLinkPreview: GetLinkPreview,
|
||||
private val createObjectFromUrl: CreateObjectFromUrl
|
||||
) : BaseViewModel(), ExitToVaultDelegate by exitToVaultDelegate {
|
||||
|
||||
private val visibleRangeUpdates = MutableSharedFlow<Pair<Id, Id>>(
|
||||
|
@ -489,6 +495,27 @@ class ChatViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
}
|
||||
is ChatView.Message.ChatBoxAttachment.Bookmark -> {
|
||||
createObjectFromUrl.async(
|
||||
params = CreateObjectFromUrl.Params(
|
||||
url = attachment.preview.url,
|
||||
space = vmParams.space
|
||||
)
|
||||
).onSuccess { obj ->
|
||||
if (obj.isValid) {
|
||||
add(
|
||||
Chat.Message.Attachment(
|
||||
target = obj.id,
|
||||
type = Chat.Message.Attachment.Type.Link
|
||||
)
|
||||
)
|
||||
} else {
|
||||
Timber.w("DROID-2966 Created object from URL is not valid")
|
||||
}
|
||||
}.onFailure {
|
||||
Timber.e(it, "DROID-2966 Error while creating object from url")
|
||||
}
|
||||
}
|
||||
is ChatView.Message.ChatBoxAttachment.File -> {
|
||||
val path = withContext(dispatchers.io) {
|
||||
copyFileToCacheDirectory.copy(attachment.uri)
|
||||
|
@ -961,6 +988,25 @@ class ChatViewModel @Inject constructor(
|
|||
visibleRangeUpdates.tryEmit(from to to)
|
||||
}
|
||||
|
||||
fun onUrlPasted(url: Url) {
|
||||
viewModelScope.launch {
|
||||
getLinkPreview.async(
|
||||
params = url
|
||||
).onSuccess { preview ->
|
||||
chatBoxAttachments.value = buildList {
|
||||
addAll(chatBoxAttachments.value)
|
||||
add(
|
||||
ChatView.Message.ChatBoxAttachment.Bookmark(
|
||||
preview = preview
|
||||
)
|
||||
)
|
||||
}
|
||||
}.onFailure {
|
||||
Timber.e(it, "Failed to get link preview")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for testing. Will be deleted.
|
||||
*/
|
||||
|
@ -1021,7 +1067,7 @@ class ChatViewModel @Inject constructor(
|
|||
): ChatBoxMode()
|
||||
}
|
||||
|
||||
fun ChatBoxMode.updateIsSendingBlocked(isBlocked: Boolean): ChatBoxMode {
|
||||
private fun ChatBoxMode.updateIsSendingBlocked(isBlocked: Boolean): ChatBoxMode {
|
||||
return when (this) {
|
||||
is ChatBoxMode.Default -> copy(isSendingMessageBlocked = isBlocked)
|
||||
is ChatBoxMode.EditMessage -> copy(isSendingMessageBlocked = isBlocked)
|
||||
|
|
|
@ -10,11 +10,13 @@ import com.anytypeio.anytype.domain.chats.DeleteChatMessage
|
|||
import com.anytypeio.anytype.domain.chats.EditChatMessage
|
||||
import com.anytypeio.anytype.domain.chats.ToggleChatMessageReaction
|
||||
import com.anytypeio.anytype.domain.media.UploadFile
|
||||
import com.anytypeio.anytype.domain.misc.GetLinkPreview
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.domain.multiplayer.ActiveSpaceMemberSubscriptionContainer
|
||||
import com.anytypeio.anytype.domain.multiplayer.SpaceViewSubscriptionContainer
|
||||
import com.anytypeio.anytype.domain.`object`.OpenObject
|
||||
import com.anytypeio.anytype.domain.`object`.SetObjectDetails
|
||||
import com.anytypeio.anytype.domain.objects.CreateObjectFromUrl
|
||||
import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
|
||||
import com.anytypeio.anytype.presentation.util.CopyFileToCacheDirectory
|
||||
import com.anytypeio.anytype.presentation.vault.ExitToVaultDelegate
|
||||
|
@ -35,7 +37,9 @@ class ChatViewModelFactory @Inject constructor(
|
|||
private val uploadFile: UploadFile,
|
||||
private val storeOfObjectTypes: StoreOfObjectTypes,
|
||||
private val copyFileToCacheDirectory: CopyFileToCacheDirectory,
|
||||
private val exitToVaultDelegate: ExitToVaultDelegate
|
||||
private val exitToVaultDelegate: ExitToVaultDelegate,
|
||||
private val getLinkPreview: GetLinkPreview,
|
||||
private val createObjectFromUrl: CreateObjectFromUrl
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T = ChatViewModel(
|
||||
|
@ -53,6 +57,8 @@ class ChatViewModelFactory @Inject constructor(
|
|||
uploadFile = uploadFile,
|
||||
storeOfObjectTypes = storeOfObjectTypes,
|
||||
copyFileToCacheDirectory = copyFileToCacheDirectory,
|
||||
exitToVaultDelegate = exitToVaultDelegate
|
||||
exitToVaultDelegate = exitToVaultDelegate,
|
||||
getLinkPreview = getLinkPreview,
|
||||
createObjectFromUrl = createObjectFromUrl
|
||||
) as T
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package com.anytypeio.anytype.feature_chats.ui
|
||||
|
||||
import android.net.Uri
|
||||
import android.util.Patterns
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.PickVisualMediaRequest
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
|
@ -61,6 +62,7 @@ import androidx.compose.ui.text.style.TextDecoration
|
|||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.DpOffset
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.anytypeio.anytype.core_models.Url
|
||||
import com.anytypeio.anytype.core_ui.common.DEFAULT_DISABLED_ALPHA
|
||||
import com.anytypeio.anytype.core_ui.common.DefaultPreviews
|
||||
import com.anytypeio.anytype.core_ui.common.FULL_ALPHA
|
||||
|
@ -94,7 +96,8 @@ fun ChatBox(
|
|||
onChatBoxMediaPicked: (List<Uri>) -> Unit,
|
||||
onChatBoxFilePicked: (List<Uri>) -> Unit,
|
||||
onExitEditMessageMode: () -> Unit,
|
||||
onValueChange: (TextFieldValue, List<ChatBoxSpan>) -> Unit
|
||||
onValueChange: (TextFieldValue, List<ChatBoxSpan>) -> Unit,
|
||||
onUrlInserted: (Url) -> Unit,
|
||||
) {
|
||||
|
||||
val length = text.text.length
|
||||
|
@ -309,7 +312,8 @@ fun ChatBox(
|
|||
end = 4.dp,
|
||||
top = 16.dp,
|
||||
bottom = 16.dp
|
||||
)
|
||||
),
|
||||
onUrlInserted = onUrlInserted
|
||||
)
|
||||
if (length >= ChatConfig.MAX_MESSAGE_CHARACTER_OFFSET_LIMIT) {
|
||||
Box(
|
||||
|
@ -478,15 +482,29 @@ private fun ChatBoxUserInput(
|
|||
text: TextFieldValue,
|
||||
spans: List<ChatBoxSpan>,
|
||||
onValueChange: (TextFieldValue, List<ChatBoxSpan>) -> Unit,
|
||||
onUrlInserted: (Url) -> Unit,
|
||||
onFocusChanged: (Boolean) -> Unit
|
||||
) {
|
||||
BasicTextField(
|
||||
value = text,
|
||||
onValueChange = { newValue ->
|
||||
|
||||
val newText = newValue.text
|
||||
val oldText = text.text // Keep a reference to the current text before updating
|
||||
val textLengthDifference = newText.length - oldText.length
|
||||
|
||||
// URL insert detection
|
||||
if (textLengthDifference > 0) {
|
||||
val prefixLen = newText.commonPrefixWith(oldText).length
|
||||
val inserted = newText.substring(prefixLen, prefixLen + textLengthDifference)
|
||||
val urlMatcher = Patterns.WEB_URL.matcher(inserted)
|
||||
if (urlMatcher.find()) {
|
||||
val url = urlMatcher.group()
|
||||
onUrlInserted(url)
|
||||
}
|
||||
}
|
||||
|
||||
// SPANS normalization
|
||||
val updatedSpans = spans.mapNotNull { span ->
|
||||
// Detect the common prefix length
|
||||
val commonPrefixLength = newText.commonPrefixWith(oldText).length
|
||||
|
|
|
@ -58,7 +58,7 @@ internal fun ChatBoxAttachments(
|
|||
type = attachment.typeName,
|
||||
icon = attachment.icon,
|
||||
onAttachmentClicked = {
|
||||
// TODO
|
||||
// Do nothing
|
||||
}
|
||||
)
|
||||
Image(
|
||||
|
@ -90,7 +90,7 @@ internal fun ChatBoxAttachments(
|
|||
type = attachment.wrapper.type,
|
||||
icon = attachment.wrapper.icon,
|
||||
onAttachmentClicked = {
|
||||
// TODO
|
||||
// Do nothing
|
||||
}
|
||||
)
|
||||
Image(
|
||||
|
@ -191,7 +191,7 @@ internal fun ChatBoxAttachments(
|
|||
fileName = null
|
||||
),
|
||||
onAttachmentClicked = {
|
||||
// TODO
|
||||
// Do nothing
|
||||
}
|
||||
)
|
||||
Image(
|
||||
|
@ -210,6 +210,38 @@ internal fun ChatBoxAttachments(
|
|||
}
|
||||
}
|
||||
}
|
||||
is ChatView.Message.ChatBoxAttachment.Bookmark -> {
|
||||
item {
|
||||
Box {
|
||||
AttachedObject(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
top = 12.dp,
|
||||
end = 4.dp
|
||||
)
|
||||
.width(216.dp),
|
||||
title = attachment.preview.title,
|
||||
type = stringResource(R.string.bookmark),
|
||||
icon = ObjectIcon.None,
|
||||
onAttachmentClicked = {
|
||||
// Do nothing
|
||||
}
|
||||
)
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_clear_chatbox_attachment),
|
||||
contentDescription = "Close icon",
|
||||
modifier = Modifier
|
||||
.align(
|
||||
Alignment.TopEnd
|
||||
)
|
||||
.padding(top = 6.dp)
|
||||
.noRippleClickable {
|
||||
onClearAttachmentClicked(attachment)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -200,7 +200,8 @@ fun ChatScreenPreview() {
|
|||
onScrollToReplyClicked = {},
|
||||
onClearIntent = {},
|
||||
onScrollToBottomClicked = {},
|
||||
onVisibleRangeChanged = { _, _ -> }
|
||||
onVisibleRangeChanged = { _, _ -> },
|
||||
onUrlInserted = {}
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -70,6 +70,7 @@ import androidx.navigation.compose.composable
|
|||
import androidx.navigation.compose.rememberNavController
|
||||
import com.anytypeio.anytype.core_models.Block
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.Url
|
||||
import com.anytypeio.anytype.core_ui.foundation.AlertConfig
|
||||
import com.anytypeio.anytype.core_ui.foundation.AlertIcon
|
||||
import com.anytypeio.anytype.core_ui.foundation.Divider
|
||||
|
@ -238,7 +239,8 @@ fun ChatScreenWrapper(
|
|||
onScrollToReplyClicked = vm::onChatScrollToReply,
|
||||
onClearIntent = vm::onClearChatViewStateIntent,
|
||||
onScrollToBottomClicked = vm::onScrollToBottomClicked,
|
||||
onVisibleRangeChanged = vm::onVisibleRangeChanged
|
||||
onVisibleRangeChanged = vm::onVisibleRangeChanged,
|
||||
onUrlInserted = vm::onUrlPasted
|
||||
)
|
||||
LaunchedEffect(Unit) {
|
||||
vm.uXCommands.collect { command ->
|
||||
|
@ -352,7 +354,8 @@ fun ChatScreen(
|
|||
onScrollToReplyClicked: (Id) -> Unit,
|
||||
onClearIntent: () -> Unit,
|
||||
onScrollToBottomClicked: (Id?) -> Unit,
|
||||
onVisibleRangeChanged: (Id, Id) -> Unit
|
||||
onVisibleRangeChanged: (Id, Id) -> Unit,
|
||||
onUrlInserted: (Url) -> Unit,
|
||||
) {
|
||||
|
||||
Timber.d("DROID-2966 Render called with state, number of messages: ${messages.size}")
|
||||
|
@ -688,7 +691,8 @@ fun ChatScreen(
|
|||
onTextChanged(t)
|
||||
},
|
||||
text = text,
|
||||
spans = spans
|
||||
spans = spans,
|
||||
onUrlInserted = onUrlInserted
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import com.anytypeio.anytype.core_models.DeviceNetworkType
|
|||
import com.anytypeio.anytype.core_models.Event
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.Key
|
||||
import com.anytypeio.anytype.core_models.LinkPreview
|
||||
import com.anytypeio.anytype.core_models.ManifestInfo
|
||||
import com.anytypeio.anytype.core_models.NodeUsageInfo
|
||||
import com.anytypeio.anytype.core_models.ObjectType
|
||||
|
@ -1092,4 +1093,12 @@ class BlockMiddleware(
|
|||
override suspend fun setDataViewProperties(command: Command.SetDataViewProperties): Payload {
|
||||
return middleware.setDataViewProperties(command)
|
||||
}
|
||||
|
||||
override suspend fun getLinkPreview(url: Url): LinkPreview {
|
||||
return middleware.getLinkPreview(url)
|
||||
}
|
||||
|
||||
override suspend fun createObjectFromUrl(space: SpaceId, url: Url): ObjectWrapper.Basic {
|
||||
return middleware.createObjectFromUrl(space = space, url = url)
|
||||
}
|
||||
}
|
|
@ -22,9 +22,11 @@ import com.anytypeio.anytype.core_models.DeviceNetworkType
|
|||
import com.anytypeio.anytype.core_models.Event
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.Key
|
||||
import com.anytypeio.anytype.core_models.LinkPreview
|
||||
import com.anytypeio.anytype.core_models.ManifestInfo
|
||||
import com.anytypeio.anytype.core_models.NodeUsageInfo
|
||||
import com.anytypeio.anytype.core_models.ObjectType
|
||||
import com.anytypeio.anytype.core_models.ObjectTypeUniqueKeys
|
||||
import com.anytypeio.anytype.core_models.ObjectView
|
||||
import com.anytypeio.anytype.core_models.ObjectWrapper
|
||||
import com.anytypeio.anytype.core_models.Payload
|
||||
|
@ -64,6 +66,7 @@ import com.anytypeio.anytype.middleware.mappers.core
|
|||
import com.anytypeio.anytype.middleware.mappers.mw
|
||||
import com.anytypeio.anytype.middleware.mappers.parse
|
||||
import com.anytypeio.anytype.middleware.mappers.toCore
|
||||
import com.anytypeio.anytype.middleware.mappers.toCoreLinkPreview
|
||||
import com.anytypeio.anytype.middleware.mappers.toCoreModel
|
||||
import com.anytypeio.anytype.middleware.mappers.toCoreModelSearchResults
|
||||
import com.anytypeio.anytype.middleware.mappers.toCoreModels
|
||||
|
@ -2990,6 +2993,28 @@ class Middleware @Inject constructor(
|
|||
logResponseIfDebug(response, time)
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun getLinkPreview(url: Url): LinkPreview {
|
||||
val request = Rpc.LinkPreview.Request(url = url)
|
||||
logRequestIfDebug(request)
|
||||
val (response, time) = measureTimedValue { service.linkPreview(request) }
|
||||
logResponseIfDebug(response, time)
|
||||
return response.linkPreview?.toCoreLinkPreview() ?: throw Exception("MW return empty link preview")
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun createObjectFromUrl(space: SpaceId, url: Url) : ObjectWrapper.Basic {
|
||||
val request = Rpc.Object.CreateFromUrl.Request(
|
||||
url = url,
|
||||
spaceId = space.id,
|
||||
objectTypeUniqueKey = ObjectTypeUniqueKeys.BOOKMARK
|
||||
)
|
||||
logRequestIfDebug(request)
|
||||
val (response, time) = measureTimedValue { service.objectCreateFromUrl(request) }
|
||||
logResponseIfDebug(response, time)
|
||||
return ObjectWrapper.Basic(response.details.orEmpty())
|
||||
}
|
||||
|
||||
private fun logRequestIfDebug(request: Any) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
logger.logRequest(request).also {
|
||||
|
|
|
@ -112,4 +112,6 @@ typealias MP2PStatus = anytype.Event.P2PStatus.Status
|
|||
typealias MP2PStatusUpdate = P2PStatus.Update
|
||||
typealias MSyncStatusUpdate = Space.SyncStatus.Update
|
||||
|
||||
typealias MDeviceNetworkType = anytype.model.DeviceNetworkType
|
||||
typealias MDeviceNetworkType = anytype.model.DeviceNetworkType
|
||||
|
||||
typealias MLinkPreview = anytype.model.LinkPreview
|
|
@ -3,7 +3,6 @@ package com.anytypeio.anytype.middleware.mappers
|
|||
import anytype.ResponseEvent
|
||||
import anytype.Rpc
|
||||
import anytype.model.Account
|
||||
import anytype.model.ChatState
|
||||
import anytype.model.NameserviceNameType
|
||||
import anytype.model.ParticipantPermissions
|
||||
import anytype.model.Restrictions
|
||||
|
@ -28,6 +27,7 @@ import com.anytypeio.anytype.core_models.DVViewerType
|
|||
import com.anytypeio.anytype.core_models.Event
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.ImportErrorCode
|
||||
import com.anytypeio.anytype.core_models.LinkPreview
|
||||
import com.anytypeio.anytype.core_models.ManifestInfo
|
||||
import com.anytypeio.anytype.core_models.NodeUsage
|
||||
import com.anytypeio.anytype.core_models.NodeUsageInfo
|
||||
|
@ -1207,4 +1207,14 @@ fun Rpc.Relation.ListWithValue.Response.ResponseItem.toCoreModel(): RelationList
|
|||
key = RelationKey(key = relationKey),
|
||||
counter = counter
|
||||
)
|
||||
}
|
||||
|
||||
fun MLinkPreview.toCoreLinkPreview(): LinkPreview {
|
||||
return LinkPreview(
|
||||
url = url,
|
||||
faviconUrl = faviconUrl,
|
||||
imageUrl = imageUrl,
|
||||
description = description,
|
||||
title = title
|
||||
)
|
||||
}
|
|
@ -637,4 +637,10 @@ interface MiddlewareService {
|
|||
|
||||
@Throws(Exception::class)
|
||||
fun pushNotificationRegisterToken(request: Rpc.PushNotification.RegisterToken.Request): Rpc.PushNotification.RegisterToken.Response
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun linkPreview(request: Rpc.LinkPreview.Request) : Rpc.LinkPreview.Response
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun objectCreateFromUrl(request: Rpc.Object.CreateFromUrl.Request): Rpc.Object.CreateFromUrl.Response
|
||||
}
|
|
@ -2586,4 +2586,30 @@ class MiddlewareServiceImplementation @Inject constructor(
|
|||
return response
|
||||
}
|
||||
}
|
||||
|
||||
override fun linkPreview(request: Rpc.LinkPreview.Request): Rpc.LinkPreview.Response {
|
||||
val encoded = Service.linkPreview(
|
||||
Rpc.LinkPreview.Request.ADAPTER.encode(request)
|
||||
)
|
||||
val response = Rpc.LinkPreview.Response.ADAPTER.decode(encoded)
|
||||
val error = response.error
|
||||
if (error != null && error.code != Rpc.LinkPreview.Response.Error.Code.NULL) {
|
||||
throw Exception(error.description)
|
||||
} else {
|
||||
return response
|
||||
}
|
||||
}
|
||||
|
||||
override fun objectCreateFromUrl(request: Rpc.Object.CreateFromUrl.Request): Rpc.Object.CreateFromUrl.Response {
|
||||
val encoded = Service.objectCreateFromUrl(
|
||||
Rpc.Object.CreateFromUrl.Request.ADAPTER.encode(request)
|
||||
)
|
||||
val response = Rpc.Object.CreateFromUrl.Response.ADAPTER.decode(encoded)
|
||||
val error = response.error
|
||||
if (error != null && error.code != Rpc.Object.CreateFromUrl.Response.Error.Code.NULL) {
|
||||
throw Exception(error.description)
|
||||
} else {
|
||||
return response
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,7 +16,7 @@ class CreateBookmarkViewModel() : ViewStateViewModel<ViewState>() {
|
|||
sealed class ViewState {
|
||||
data class Success(val url: String) : ViewState()
|
||||
data class Error(val message: String) : ViewState()
|
||||
object Exit : ViewState()
|
||||
data object Exit : ViewState()
|
||||
}
|
||||
|
||||
class Factory() : ViewModelProvider.Factory {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue