mirror of
https://github.com/anyproto/anytype-kotlin.git
synced 2025-06-08 05:47:05 +09:00
DROID-3016 Chats | Enhancement | Add space-level chat widget by default (#1784)
This commit is contained in:
parent
a67b03f1d1
commit
7bfab8bfe1
19 changed files with 240 additions and 122 deletions
|
@ -25,4 +25,7 @@ class DefaultFeatureToggles @Inject constructor(
|
|||
override val isConciseLogging: Boolean = true
|
||||
|
||||
override val enableDiscussionDemo: Boolean = true
|
||||
|
||||
override val isSpaceLevelChatEnabled: Boolean
|
||||
get() = true
|
||||
}
|
|
@ -63,6 +63,7 @@ import com.anytypeio.anytype.ui.widgets.types.GalleryWidgetCard
|
|||
import com.anytypeio.anytype.ui.widgets.types.LibraryWidgetCard
|
||||
import com.anytypeio.anytype.ui.widgets.types.LinkWidgetCard
|
||||
import com.anytypeio.anytype.ui.widgets.types.ListWidgetCard
|
||||
import com.anytypeio.anytype.ui.widgets.types.SpaceChatWidgetCard
|
||||
import com.anytypeio.anytype.ui.widgets.types.SpaceWidgetCard
|
||||
import com.anytypeio.anytype.ui.widgets.types.TreeWidgetCard
|
||||
import org.burnoutcrew.reorderable.ReorderableItem
|
||||
|
@ -73,7 +74,6 @@ import org.burnoutcrew.reorderable.reorderable
|
|||
|
||||
@Composable
|
||||
fun HomeScreen(
|
||||
profileIcon: ProfileIconView,
|
||||
mode: InteractionMode,
|
||||
widgets: List<WidgetView>,
|
||||
onExpand: (TreePath) -> Unit,
|
||||
|
@ -423,6 +423,12 @@ private fun WidgetList(
|
|||
onWidgetClicked = { onBundledWidgetHeaderClicked(item.id) }
|
||||
)
|
||||
}
|
||||
is WidgetView.SpaceChat -> {
|
||||
SpaceChatWidgetCard(
|
||||
mode = mode,
|
||||
onWidgetClicked = { onBundledWidgetHeaderClicked(item.id) }
|
||||
)
|
||||
}
|
||||
is WidgetView.Library -> {
|
||||
LibraryWidgetCard(
|
||||
onDropDownMenuAction = { action ->
|
||||
|
|
|
@ -88,7 +88,6 @@ class HomeScreenFragment : BaseComposeFragment(),
|
|||
)
|
||||
) {
|
||||
HomeScreen(
|
||||
profileIcon = vm.icon.collectAsState().value,
|
||||
widgets = vm.views.collectAsState().value,
|
||||
mode = vm.mode.collectAsState().value,
|
||||
onExpand = { path -> vm.onExpand(path) },
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
package com.anytypeio.anytype.ui.widgets.types
|
||||
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_NO
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.anytypeio.anytype.R
|
||||
import com.anytypeio.anytype.core_ui.views.HeadlineSubheading
|
||||
import com.anytypeio.anytype.presentation.home.InteractionMode
|
||||
|
||||
@Composable
|
||||
fun SpaceChatWidgetCard(
|
||||
mode: InteractionMode,
|
||||
onWidgetClicked: () -> Unit = {}
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(start = 20.dp, end = 20.dp, top = 6.dp, bottom = 6.dp)
|
||||
.fillMaxWidth()
|
||||
.height(52.dp)
|
||||
.background(
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
color = colorResource(id = R.color.dashboard_card_background)
|
||||
)
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.then(
|
||||
if (mode !is InteractionMode.Edit) {
|
||||
Modifier.clickable {
|
||||
onWidgetClicked()
|
||||
}
|
||||
} else {
|
||||
Modifier
|
||||
}
|
||||
)
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_widget_all_content),
|
||||
contentDescription = "All content icon",
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterStart)
|
||||
.padding(start = 16.dp)
|
||||
)
|
||||
|
||||
Text(
|
||||
// Temporary hard-coded name for the widget
|
||||
text = "Space chat",
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterStart)
|
||||
.padding(start = 44.dp, end = 16.dp),
|
||||
style = HeadlineSubheading,
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(
|
||||
name = "Dark Mode",
|
||||
showBackground = true,
|
||||
uiMode = UI_MODE_NIGHT_YES
|
||||
)
|
||||
@Preview(
|
||||
name = "Light Mode",
|
||||
showBackground = true,
|
||||
uiMode = UI_MODE_NIGHT_NO
|
||||
)
|
||||
@Composable
|
||||
fun SpaceChatWidgetPreview() {
|
||||
SpaceChatWidgetCard(
|
||||
onWidgetClicked = {},
|
||||
mode = InteractionMode.Default
|
||||
)
|
||||
}
|
|
@ -15,4 +15,6 @@ interface FeatureToggles {
|
|||
val isLogEditorControlPanelMachine: Boolean
|
||||
|
||||
val enableDiscussionDemo: Boolean
|
||||
|
||||
val isSpaceLevelChatEnabled: Boolean
|
||||
}
|
|
@ -758,8 +758,9 @@ class BlockDataRepository(
|
|||
remote.deleteSpace(space)
|
||||
}
|
||||
|
||||
override suspend fun createWorkspace(details: Struct): Id = remote.createWorkspace(
|
||||
details = details
|
||||
override suspend fun createWorkspace(details: Struct, withChat: Boolean): Id = remote.createWorkspace(
|
||||
details = details,
|
||||
withChat = withChat
|
||||
)
|
||||
|
||||
override suspend fun setSpaceDetails(space: SpaceId, details: Struct) {
|
||||
|
|
|
@ -336,7 +336,7 @@ interface BlockRemote {
|
|||
suspend fun setSpaceDetails(space: SpaceId, details: Struct)
|
||||
|
||||
suspend fun deleteSpace(space: SpaceId)
|
||||
suspend fun createWorkspace(details: Struct): Id
|
||||
suspend fun createWorkspace(details: Struct, withChat: Boolean): Id
|
||||
|
||||
suspend fun getSpaceConfig(space: Id): Config
|
||||
|
||||
|
|
|
@ -390,7 +390,7 @@ interface BlockRepository {
|
|||
): Payload
|
||||
|
||||
suspend fun deleteSpace(space: SpaceId)
|
||||
suspend fun createWorkspace(details: Struct): Id
|
||||
suspend fun createWorkspace(details: Struct, withChat: Boolean): Id
|
||||
suspend fun getSpaceConfig(space: Id): Config
|
||||
suspend fun addObjectListToSpace(objects: List<Id>, space: Id) : List<Id>
|
||||
suspend fun addObjectToSpace(command: Command.AddObjectToSpace) : Pair<Id, Struct?>
|
||||
|
|
|
@ -109,6 +109,7 @@ interface SpaceViewSubscriptionContainer {
|
|||
keys = listOf(
|
||||
Relations.ID,
|
||||
Relations.TARGET_SPACE_ID,
|
||||
Relations.CHAT_ID,
|
||||
Relations.SPACE_ACCOUNT_STATUS,
|
||||
Relations.SPACE_LOCAL_STATUS,
|
||||
Relations.SPACE_ACCESS_TYPE,
|
||||
|
@ -119,7 +120,7 @@ interface SpaceViewSubscriptionContainer {
|
|||
Relations.CREATED_DATE,
|
||||
Relations.CREATOR,
|
||||
Relations.ICON_IMAGE,
|
||||
Relations.ICON_OPTION,
|
||||
Relations.ICON_OPTION
|
||||
),
|
||||
filters = listOf(
|
||||
DVFilter(
|
||||
|
|
|
@ -9,12 +9,13 @@ import javax.inject.Inject
|
|||
|
||||
class CreateSpace @Inject constructor(
|
||||
private val repo: BlockRepository,
|
||||
private val dispatchers: AppCoroutineDispatchers
|
||||
dispatchers: AppCoroutineDispatchers
|
||||
) : ResultInteractor<CreateSpace.Params, Id>(dispatchers.io) {
|
||||
|
||||
override suspend fun doWork(params: Params): Id = repo.createWorkspace(
|
||||
params.details
|
||||
details = params.details,
|
||||
withChat = params.withChat
|
||||
)
|
||||
|
||||
data class Params(val details: Struct)
|
||||
data class Params(val details: Struct, val withChat: Boolean = true)
|
||||
}
|
|
@ -51,9 +51,6 @@ class DiscussionViewModel(
|
|||
val navigation = MutableSharedFlow<OpenObjectNavigation>()
|
||||
val chatBoxMode = MutableStateFlow<ChatBoxMode>(ChatBoxMode.Default)
|
||||
|
||||
// TODO naive implementation; switch to state
|
||||
private lateinit var chat: Id
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
val account = requireNotNull(getAccount.async(Unit).getOrNull())
|
||||
|
@ -68,8 +65,7 @@ class DiscussionViewModel(
|
|||
val root = ObjectWrapper.Basic(obj.details[params.ctx].orEmpty())
|
||||
name.value = root.name
|
||||
proceedWithObservingChatMessages(
|
||||
account = account.id,
|
||||
root = root
|
||||
account = account.id
|
||||
)
|
||||
},
|
||||
onFailure = {
|
||||
|
@ -80,53 +76,46 @@ class DiscussionViewModel(
|
|||
}
|
||||
|
||||
private suspend fun proceedWithObservingChatMessages(
|
||||
account: Id,
|
||||
root: ObjectWrapper.Basic
|
||||
account: Id
|
||||
) {
|
||||
val chat = root.getValue<Id>(Relations.CHAT_ID)
|
||||
if (chat != null) {
|
||||
this.chat = chat
|
||||
chatContainer
|
||||
.watch(chat)
|
||||
.onEach { Timber.d("Got new update: $it") }
|
||||
.collect {
|
||||
messages.value = it.map { msg ->
|
||||
val member = members.get().let { type ->
|
||||
when(type) {
|
||||
is Store.Data -> type.members.find { member ->
|
||||
member.identity == msg.creator
|
||||
}
|
||||
is Store.Empty -> null
|
||||
chatContainer
|
||||
.watch(params.ctx)
|
||||
.onEach { Timber.d("Got new update: $it") }
|
||||
.collect {
|
||||
messages.value = it.map { msg ->
|
||||
val member = members.get().let { type ->
|
||||
when(type) {
|
||||
is Store.Data -> type.members.find { member ->
|
||||
member.identity == msg.creator
|
||||
}
|
||||
is Store.Empty -> null
|
||||
}
|
||||
DiscussionView.Message(
|
||||
id = msg.id,
|
||||
timestamp = msg.createdAt * 1000,
|
||||
content = msg.content?.text.orEmpty(),
|
||||
author = member?.name ?: msg.creator.takeLast(5),
|
||||
isUserAuthor = msg.creator == account,
|
||||
isEdited = msg.modifiedAt > msg.createdAt,
|
||||
reactions = msg.reactions.map{ (emoji, ids) ->
|
||||
DiscussionView.Message.Reaction(
|
||||
emoji = emoji,
|
||||
count = ids.size,
|
||||
isSelected = ids.contains(account)
|
||||
)
|
||||
},
|
||||
attachments = msg.attachments,
|
||||
avatar = if (member != null && !member.iconImage.isNullOrEmpty()) {
|
||||
DiscussionView.Message.Avatar.Image(
|
||||
urlBuilder.thumbnail(member.iconImage!!)
|
||||
)
|
||||
} else {
|
||||
DiscussionView.Message.Avatar.Initials(member?.name.orEmpty())
|
||||
}
|
||||
)
|
||||
}.reversed()
|
||||
}
|
||||
} else {
|
||||
Timber.w("Chat ID was missing in chat smart-object details")
|
||||
}
|
||||
}
|
||||
DiscussionView.Message(
|
||||
id = msg.id,
|
||||
timestamp = msg.createdAt * 1000,
|
||||
content = msg.content?.text.orEmpty(),
|
||||
author = member?.name ?: msg.creator.takeLast(5),
|
||||
isUserAuthor = msg.creator == account,
|
||||
isEdited = msg.modifiedAt > msg.createdAt,
|
||||
reactions = msg.reactions.map{ (emoji, ids) ->
|
||||
DiscussionView.Message.Reaction(
|
||||
emoji = emoji,
|
||||
count = ids.size,
|
||||
isSelected = ids.contains(account)
|
||||
)
|
||||
},
|
||||
attachments = msg.attachments,
|
||||
avatar = if (member != null && !member.iconImage.isNullOrEmpty()) {
|
||||
DiscussionView.Message.Avatar.Image(
|
||||
urlBuilder.thumbnail(member.iconImage!!)
|
||||
)
|
||||
} else {
|
||||
DiscussionView.Message.Avatar.Initials(member?.name.orEmpty())
|
||||
}
|
||||
)
|
||||
}.reversed()
|
||||
}
|
||||
}
|
||||
|
||||
fun onMessageSent(msg: String) {
|
||||
|
@ -137,7 +126,7 @@ class DiscussionViewModel(
|
|||
// TODO consider moving this use-case inside chat container
|
||||
addChatMessage.async(
|
||||
params = Command.ChatCommand.AddMessage(
|
||||
chat = chat,
|
||||
chat = params.ctx,
|
||||
message = Chat.Message.new(
|
||||
text = msg,
|
||||
attachments = attachments.value.map { a ->
|
||||
|
@ -160,7 +149,7 @@ class DiscussionViewModel(
|
|||
is ChatBoxMode.EditMessage -> {
|
||||
editChatMessage.async(
|
||||
params = Command.ChatCommand.EditMessage(
|
||||
chat = chat,
|
||||
chat = params.ctx,
|
||||
message = Chat.Message.updated(
|
||||
id = mode.msg,
|
||||
text = msg
|
||||
|
@ -197,7 +186,11 @@ class DiscussionViewModel(
|
|||
Relations.NAME to input
|
||||
)
|
||||
)
|
||||
)
|
||||
).onSuccess {
|
||||
Timber.d("Updated chat title successfully")
|
||||
}.onFailure {
|
||||
Timber.e(it, "Error while updating chat title")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -216,7 +209,7 @@ class DiscussionViewModel(
|
|||
if (message != null) {
|
||||
toggleChatMessageReaction.async(
|
||||
Command.ChatCommand.ToggleMessageReaction(
|
||||
chat = chat,
|
||||
chat = params.ctx,
|
||||
msg = msg,
|
||||
emoji = reaction
|
||||
)
|
||||
|
@ -234,7 +227,7 @@ class DiscussionViewModel(
|
|||
viewModelScope.launch {
|
||||
deleteChatMessage.async(
|
||||
Command.ChatCommand.DeleteMessage(
|
||||
chat = chat,
|
||||
chat = params.ctx,
|
||||
msg = msg.id
|
||||
)
|
||||
).onFailure {
|
||||
|
|
|
@ -729,8 +729,12 @@ class BlockMiddleware(
|
|||
middleware.spaceDelete(space)
|
||||
}
|
||||
|
||||
override suspend fun createWorkspace(details: Struct): Id = middleware.workspaceCreate(
|
||||
details = details
|
||||
override suspend fun createWorkspace(
|
||||
details: Struct,
|
||||
withChat: Boolean
|
||||
): Id = middleware.workspaceCreate(
|
||||
details = details,
|
||||
withChat = withChat
|
||||
)
|
||||
|
||||
override suspend fun getSpaceConfig(space: Id): Config = middleware.workspaceOpen(
|
||||
|
|
|
@ -1921,10 +1921,11 @@ class Middleware @Inject constructor(
|
|||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun workspaceCreate(details: Struct): Id {
|
||||
fun workspaceCreate(details: Struct, withChat: Boolean): Id {
|
||||
val request = Rpc.Workspace.Create.Request(
|
||||
details = details,
|
||||
useCase = Rpc.Object.ImportUseCase.Request.UseCase.GET_STARTED
|
||||
useCase = Rpc.Object.ImportUseCase.Request.UseCase.GET_STARTED,
|
||||
withChat = withChat
|
||||
)
|
||||
logRequestIfDebug(request)
|
||||
val (response, time) = measureTimedValue { service.workspaceCreate(request) }
|
||||
|
@ -1935,7 +1936,8 @@ class Middleware @Inject constructor(
|
|||
@Throws(Exception::class)
|
||||
fun workspaceOpen(space: Id): Config {
|
||||
val request = Rpc.Workspace.Open.Request(
|
||||
spaceId = space
|
||||
spaceId = space,
|
||||
withChat = true
|
||||
)
|
||||
logRequestIfDebug(request)
|
||||
val (response, time) = measureTimedValue { service.workspaceOpen(request) }
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.anytypeio.anytype.presentation.home
|
||||
|
||||
import android.R
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
|
@ -33,6 +34,7 @@ import com.anytypeio.anytype.core_models.primitives.TypeKey
|
|||
import com.anytypeio.anytype.core_utils.ext.cancel
|
||||
import com.anytypeio.anytype.core_utils.ext.replace
|
||||
import com.anytypeio.anytype.core_utils.ext.withLatestFrom
|
||||
import com.anytypeio.anytype.core_utils.tools.FeatureToggles
|
||||
import com.anytypeio.anytype.domain.auth.interactor.ClearLastOpenedObject
|
||||
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
|
||||
import com.anytypeio.anytype.domain.base.Resultat
|
||||
|
@ -89,8 +91,6 @@ import com.anytypeio.anytype.presentation.navigation.DeepLinkToObjectDelegate
|
|||
import com.anytypeio.anytype.presentation.navigation.NavigationViewModel
|
||||
import com.anytypeio.anytype.presentation.objects.SupportedLayouts
|
||||
import com.anytypeio.anytype.presentation.objects.getCreateObjectParams
|
||||
import com.anytypeio.anytype.presentation.profile.ProfileIconView
|
||||
import com.anytypeio.anytype.presentation.profile.profileIcon
|
||||
import com.anytypeio.anytype.presentation.search.Subscriptions
|
||||
import com.anytypeio.anytype.presentation.sets.prefillNewObjectDetails
|
||||
import com.anytypeio.anytype.presentation.sets.resolveSetByRelationPrefilledObjectData
|
||||
|
@ -106,6 +106,7 @@ import com.anytypeio.anytype.presentation.widgets.DropDownMenuAction
|
|||
import com.anytypeio.anytype.presentation.widgets.LinkWidgetContainer
|
||||
import com.anytypeio.anytype.presentation.widgets.ListWidgetContainer
|
||||
import com.anytypeio.anytype.presentation.widgets.SpaceBinWidgetContainer
|
||||
import com.anytypeio.anytype.presentation.widgets.SpaceChatWidgetContainer
|
||||
import com.anytypeio.anytype.presentation.widgets.SpaceWidgetContainer
|
||||
import com.anytypeio.anytype.presentation.widgets.TreePath
|
||||
import com.anytypeio.anytype.presentation.widgets.TreeWidgetBranchStateHolder
|
||||
|
@ -205,7 +206,8 @@ class HomeScreenViewModel(
|
|||
private val addObjectToCollection: AddObjectToCollection,
|
||||
private val clearLastOpenedSpace: ClearLastOpenedSpace,
|
||||
private val clearLastOpenedObject: ClearLastOpenedObject,
|
||||
private val spaceBinWidgetContainer: SpaceBinWidgetContainer
|
||||
private val spaceBinWidgetContainer: SpaceBinWidgetContainer,
|
||||
private val featureToggles: FeatureToggles
|
||||
) : NavigationViewModel<HomeScreenViewModel.Navigation>(),
|
||||
Reducer<ObjectView, Payload>,
|
||||
WidgetActiveViewStateHolder by widgetActiveViewStateHolder,
|
||||
|
@ -232,11 +234,10 @@ class HomeScreenViewModel(
|
|||
private val treeWidgetBranchStateHolder = TreeWidgetBranchStateHolder()
|
||||
|
||||
private val allContentWidget = AllContentWidgetContainer()
|
||||
private val spaceChatWidget = SpaceChatWidgetContainer()
|
||||
|
||||
private val spaceWidgetView = spaceWidgetContainer.view
|
||||
|
||||
val icon = MutableStateFlow<ProfileIconView>(ProfileIconView.Loading)
|
||||
|
||||
private val widgetObjectPipelineJobs = mutableListOf<Job>()
|
||||
|
||||
private val openWidgetObjectsHistory : MutableSet<OpenObjectHistoryItem> = LinkedHashSet()
|
||||
|
@ -348,7 +349,6 @@ class HomeScreenViewModel(
|
|||
init {
|
||||
Timber.i("HomeScreenViewModel, init")
|
||||
proceedWithUserPermissions()
|
||||
proceedWithObservingProfileIcon()
|
||||
proceedWithLaunchingUnsubscriber()
|
||||
proceedWithObjectViewStatePipeline()
|
||||
proceedWithWidgetContainerPipeline()
|
||||
|
@ -408,6 +408,9 @@ class HomeScreenViewModel(
|
|||
combine(
|
||||
flows = buildList<Flow<WidgetView>> {
|
||||
add(spaceWidgetView)
|
||||
if (featureToggles.isSpaceLevelChatEnabled) {
|
||||
add(spaceChatWidget.view)
|
||||
}
|
||||
add(allContentWidget.view)
|
||||
addAll(list.map { m -> m.view })
|
||||
}
|
||||
|
@ -581,35 +584,6 @@ class HomeScreenViewModel(
|
|||
}
|
||||
}
|
||||
|
||||
private fun proceedWithObservingProfileIcon() {
|
||||
viewModelScope.launch {
|
||||
spaceManager
|
||||
.observe()
|
||||
.flatMapLatest { config ->
|
||||
storelessSubscriptionContainer.subscribe(
|
||||
StoreSearchByIdsParams(
|
||||
space = SpaceId(config.techSpace),
|
||||
subscription = HOME_SCREEN_PROFILE_OBJECT_SUBSCRIPTION,
|
||||
targets = listOf(config.profile),
|
||||
keys = listOf(
|
||||
Relations.ID,
|
||||
Relations.NAME,
|
||||
Relations.ICON_EMOJI,
|
||||
Relations.ICON_IMAGE,
|
||||
Relations.ICON_OPTION
|
||||
)
|
||||
)
|
||||
).map { result ->
|
||||
val obj = result.firstOrNull()
|
||||
obj?.profileIcon(urlBuilder) ?: ProfileIconView.Placeholder(null)
|
||||
}
|
||||
}
|
||||
.catch { Timber.e(it, "Error while observing space icon") }
|
||||
.flowOn(appCoroutineDispatchers.io)
|
||||
.collect { icon.value = it }
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun proceedWithClosingWidgetObject(
|
||||
widgetObject: Id,
|
||||
space: SpaceId
|
||||
|
@ -1075,6 +1049,9 @@ class HomeScreenViewModel(
|
|||
)
|
||||
)
|
||||
}
|
||||
WidgetView.SpaceChat.id -> {
|
||||
proceedWithSpaceChatWidgetHeaderClick()
|
||||
}
|
||||
WidgetView.AllContent.ALL_CONTENT_WIDGET_ID -> {
|
||||
if (mode.value == InteractionMode.Edit) {
|
||||
return@launch
|
||||
|
@ -1085,10 +1062,37 @@ class HomeScreenViewModel(
|
|||
)
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
Timber.w("Skipping widget click: $widget")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun proceedWithSpaceChatWidgetHeaderClick() {
|
||||
if (mode.value == InteractionMode.Edit) {
|
||||
return
|
||||
}
|
||||
val view = views.value.find { it is WidgetView.SpaceWidget.View }
|
||||
if (view != null) {
|
||||
val spaceView = (view as WidgetView.SpaceWidget.View)
|
||||
val chat = spaceView.space.getValue<Id?>(Relations.CHAT_ID)
|
||||
val space = spaceView.space.targetSpaceId
|
||||
if (chat != null && space != null) {
|
||||
navigation(
|
||||
Navigation.OpenDiscussion(
|
||||
space = space,
|
||||
ctx = chat
|
||||
)
|
||||
)
|
||||
} else {
|
||||
Timber.w("Chat or space not found - not able to open space chat")
|
||||
}
|
||||
} else {
|
||||
Timber.w("Space widget not found")
|
||||
}
|
||||
}
|
||||
|
||||
private fun proceedWithAddingWidgetBelow(widget: Id) {
|
||||
viewModelScope.launch {
|
||||
sendAddWidgetEvent(
|
||||
|
@ -2174,7 +2178,8 @@ class HomeScreenViewModel(
|
|||
private val addObjectToCollection: AddObjectToCollection,
|
||||
private val clearLastOpenedSpace: ClearLastOpenedSpace,
|
||||
private val clearLastOpenedObject: ClearLastOpenedObject,
|
||||
private val spaceBinWidgetContainer: SpaceBinWidgetContainer
|
||||
private val spaceBinWidgetContainer: SpaceBinWidgetContainer,
|
||||
private val featureToggles: FeatureToggles
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T = HomeScreenViewModel(
|
||||
|
@ -2224,7 +2229,8 @@ class HomeScreenViewModel(
|
|||
addObjectToCollection = addObjectToCollection,
|
||||
clearLastOpenedSpace = clearLastOpenedSpace,
|
||||
clearLastOpenedObject = clearLastOpenedObject,
|
||||
spaceBinWidgetContainer = spaceBinWidgetContainer
|
||||
spaceBinWidgetContainer = spaceBinWidgetContainer,
|
||||
featureToggles = featureToggles
|
||||
) as T
|
||||
}
|
||||
|
||||
|
|
|
@ -1280,8 +1280,9 @@ object ObjectSearchConstants {
|
|||
|
||||
val spaceViewKeys = listOf(
|
||||
Relations.ID,
|
||||
Relations.NAME,
|
||||
Relations.TARGET_SPACE_ID,
|
||||
Relations.CHAT_ID,
|
||||
Relations.NAME,
|
||||
Relations.ICON_IMAGE,
|
||||
Relations.ICON_EMOJI,
|
||||
Relations.ICON_OPTION,
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
package com.anytypeio.anytype.presentation.widgets
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
|
||||
class SpaceChatWidgetContainer : WidgetContainer {
|
||||
override val view: Flow<WidgetView> = flowOf(
|
||||
WidgetView.SpaceChat
|
||||
)
|
||||
}
|
|
@ -45,6 +45,7 @@ class SpaceWidgetContainer @Inject constructor(
|
|||
targets = listOf(config.spaceView),
|
||||
keys = buildList {
|
||||
addAll(ObjectSearchConstants.defaultKeys)
|
||||
add(Relations.CHAT_ID)
|
||||
add(Relations.SPACE_ACCESS_TYPE)
|
||||
add(Relations.ICON_OPTION)
|
||||
}
|
||||
|
|
|
@ -117,6 +117,12 @@ sealed class WidgetView {
|
|||
override val id: Id = ALL_CONTENT_WIDGET_ID
|
||||
}
|
||||
|
||||
data object SpaceChat : WidgetView() {
|
||||
private const val SPACE_CHAT_WIDGET_ID = "bundled-widget.space-chat"
|
||||
override val isLoading: Boolean = false
|
||||
override val id: Id = SPACE_CHAT_WIDGET_ID
|
||||
}
|
||||
|
||||
sealed class SpaceWidget: WidgetView() {
|
||||
override val id: Id get() = SpaceWidgetContainer.SPACE_WIDGET_SUBSCRIPTION
|
||||
data class View(
|
||||
|
|
|
@ -29,6 +29,7 @@ import com.anytypeio.anytype.core_models.primitives.Space
|
|||
import com.anytypeio.anytype.core_models.primitives.SpaceId
|
||||
import com.anytypeio.anytype.core_models.primitives.TypeId
|
||||
import com.anytypeio.anytype.core_models.primitives.TypeKey
|
||||
import com.anytypeio.anytype.core_utils.tools.FeatureToggles
|
||||
import com.anytypeio.anytype.domain.auth.interactor.ClearLastOpenedObject
|
||||
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
|
||||
import com.anytypeio.anytype.domain.base.Resultat
|
||||
|
@ -251,6 +252,9 @@ class HomeScreenViewModelTest {
|
|||
@Mock
|
||||
lateinit var clearLastOpenedObject: ClearLastOpenedObject
|
||||
|
||||
@Mock
|
||||
lateinit var featureToggles: FeatureToggles
|
||||
|
||||
lateinit var userPermissionProvider: UserPermissionProvider
|
||||
|
||||
private val objectPayloadDispatcher = Dispatcher.Default<Payload>()
|
||||
|
@ -2504,20 +2508,6 @@ class HomeScreenViewModelTest {
|
|||
)
|
||||
)
|
||||
}
|
||||
verify(storelessSubscriptionContainer, times(1)).subscribe(
|
||||
StoreSearchByIdsParams(
|
||||
space = SpaceId(defaultSpaceConfig.techSpace),
|
||||
subscription = HomeScreenViewModel.HOME_SCREEN_PROFILE_OBJECT_SUBSCRIPTION,
|
||||
targets = listOf(defaultSpaceConfig.profile),
|
||||
keys = listOf(
|
||||
Relations.ID,
|
||||
Relations.NAME,
|
||||
Relations.ICON_EMOJI,
|
||||
Relations.ICON_IMAGE,
|
||||
Relations.ICON_OPTION
|
||||
)
|
||||
)
|
||||
)
|
||||
verify(storelessSubscriptionContainer, times(1)).subscribe(
|
||||
firstTimeParams
|
||||
)
|
||||
|
@ -2986,7 +2976,8 @@ class HomeScreenViewModelTest {
|
|||
spaceBinWidgetContainer = SpaceBinWidgetContainer(
|
||||
container = storelessSubscriptionContainer,
|
||||
manager = spaceManager
|
||||
)
|
||||
),
|
||||
featureToggles = featureToggles
|
||||
)
|
||||
|
||||
companion object {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue