mirror of
https://github.com/anyproto/anytype-kotlin.git
synced 2025-06-08 05:47:05 +09:00
DROID-3232 App | Tech | Renamings (#1998)
This commit is contained in:
parent
691fc84b23
commit
a1aa1619e0
51 changed files with 2208 additions and 2055 deletions
|
@ -173,7 +173,7 @@ dependencies {
|
|||
implementation project(':ui-settings')
|
||||
implementation project(':crash-reporting')
|
||||
implementation project(':payments')
|
||||
implementation project(':feature-discussions')
|
||||
implementation project(':feature-chats')
|
||||
implementation project(':gallery-experience')
|
||||
implementation project(':feature-all-content')
|
||||
implementation project(':feature-date')
|
||||
|
|
|
@ -48,11 +48,11 @@ import com.anytypeio.anytype.di.feature.TextBlockIconPickerModule
|
|||
import com.anytypeio.anytype.di.feature.ViewerFilterModule
|
||||
import com.anytypeio.anytype.di.feature.ViewerSortModule
|
||||
import com.anytypeio.anytype.di.feature.auth.DaggerDeletedAccountComponent
|
||||
import com.anytypeio.anytype.di.feature.chats.DaggerChatComponent
|
||||
import com.anytypeio.anytype.di.feature.cover.UnsplashModule
|
||||
import com.anytypeio.anytype.di.feature.discussions.DaggerChatReactionComponent
|
||||
import com.anytypeio.anytype.di.feature.discussions.DaggerDiscussionComponent
|
||||
import com.anytypeio.anytype.di.feature.discussions.DaggerSelectChatReactionComponent
|
||||
import com.anytypeio.anytype.di.feature.discussions.DaggerSpaceLevelChatComponent
|
||||
import com.anytypeio.anytype.di.feature.chats.DaggerChatReactionComponent
|
||||
import com.anytypeio.anytype.di.feature.chats.DaggerSelectChatReactionComponent
|
||||
import com.anytypeio.anytype.di.feature.chats.DaggerSpaceLevelChatComponent
|
||||
import com.anytypeio.anytype.di.feature.gallery.DaggerGalleryInstallationComponent
|
||||
import com.anytypeio.anytype.di.feature.home.DaggerHomeScreenComponent
|
||||
import com.anytypeio.anytype.di.feature.membership.DaggerMembershipComponent
|
||||
|
@ -104,9 +104,9 @@ import com.anytypeio.anytype.di.feature.widgets.DaggerSelectWidgetTypeComponent
|
|||
import com.anytypeio.anytype.di.main.MainComponent
|
||||
import com.anytypeio.anytype.feature_allcontent.presentation.AllContentViewModel
|
||||
import com.anytypeio.anytype.feature_date.viewmodel.DateObjectVmParams
|
||||
import com.anytypeio.anytype.feature_discussions.presentation.ChatReactionViewModel
|
||||
import com.anytypeio.anytype.feature_discussions.presentation.DiscussionViewModel
|
||||
import com.anytypeio.anytype.feature_discussions.presentation.SelectChatReactionViewModel
|
||||
import com.anytypeio.anytype.feature_chats.presentation.ChatReactionViewModel
|
||||
import com.anytypeio.anytype.feature_chats.presentation.ChatViewModel
|
||||
import com.anytypeio.anytype.feature_chats.presentation.SelectChatReactionViewModel
|
||||
import com.anytypeio.anytype.gallery_experience.viewmodel.GalleryInstallationViewModel
|
||||
import com.anytypeio.anytype.presentation.editor.EditorViewModel
|
||||
import com.anytypeio.anytype.presentation.history.VersionHistoryViewModel
|
||||
|
@ -1067,15 +1067,15 @@ class ComponentManager(
|
|||
.build()
|
||||
}
|
||||
|
||||
val discussionComponent = ComponentMapWithParam { params: DiscussionViewModel.Params ->
|
||||
DaggerDiscussionComponent
|
||||
val chatComponent = ComponentMapWithParam { params: ChatViewModel.Params ->
|
||||
DaggerChatComponent
|
||||
.builder()
|
||||
.withDependencies(findComponentDependencies())
|
||||
.withParams(params)
|
||||
.build()
|
||||
}
|
||||
|
||||
val spaceLevelChatComponent = ComponentMapWithParam { params: DiscussionViewModel.Params ->
|
||||
val spaceLevelChatComponent = ComponentMapWithParam { params: ChatViewModel.Params ->
|
||||
DaggerSpaceLevelChatComponent
|
||||
.builder()
|
||||
.withDependencies(findComponentDependencies())
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package com.anytypeio.anytype.di.feature.discussions
|
||||
package com.anytypeio.anytype.di.feature.chats
|
||||
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.anytypeio.anytype.core_utils.di.scope.PerScreen
|
||||
|
@ -7,7 +7,7 @@ import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
|
|||
import com.anytypeio.anytype.domain.block.repo.BlockRepository
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.domain.multiplayer.ActiveSpaceMemberSubscriptionContainer
|
||||
import com.anytypeio.anytype.feature_discussions.presentation.ChatReactionViewModel
|
||||
import com.anytypeio.anytype.feature_chats.presentation.ChatReactionViewModel
|
||||
import com.anytypeio.anytype.ui.chats.ChatReactionFragment
|
||||
import dagger.Binds
|
||||
import dagger.BindsInstance
|
|
@ -1,4 +1,4 @@
|
|||
package com.anytypeio.anytype.di.feature.discussions
|
||||
package com.anytypeio.anytype.di.feature.chats
|
||||
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
|
@ -6,7 +6,6 @@ import com.anytypeio.anytype.analytics.base.Analytics
|
|||
import com.anytypeio.anytype.core_utils.di.scope.PerScreen
|
||||
import com.anytypeio.anytype.core_utils.tools.FeatureToggles
|
||||
import com.anytypeio.anytype.di.common.ComponentDependencies
|
||||
import com.anytypeio.anytype.di.feature.EditorSubComponent.Builder
|
||||
import com.anytypeio.anytype.domain.auth.repo.AuthRepository
|
||||
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
|
||||
import com.anytypeio.anytype.domain.block.repo.BlockRepository
|
||||
|
@ -18,15 +17,12 @@ import com.anytypeio.anytype.domain.multiplayer.ActiveSpaceMemberSubscriptionCon
|
|||
import com.anytypeio.anytype.domain.multiplayer.SpaceViewSubscriptionContainer
|
||||
import com.anytypeio.anytype.domain.multiplayer.UserPermissionProvider
|
||||
import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes
|
||||
import com.anytypeio.anytype.emojifier.data.Emoji
|
||||
import com.anytypeio.anytype.emojifier.data.EmojiProvider
|
||||
import com.anytypeio.anytype.feature_discussions.presentation.DiscussionViewModel
|
||||
import com.anytypeio.anytype.feature_discussions.presentation.DiscussionViewModelFactory
|
||||
import com.anytypeio.anytype.feature_chats.presentation.ChatViewModel
|
||||
import com.anytypeio.anytype.feature_chats.presentation.ChatViewModelFactory
|
||||
import com.anytypeio.anytype.middleware.EventProxy
|
||||
import com.anytypeio.anytype.presentation.common.BaseViewModel
|
||||
import com.anytypeio.anytype.presentation.util.CopyFileToCacheDirectory
|
||||
import com.anytypeio.anytype.presentation.util.DefaultCopyFileToCacheDirectory
|
||||
import com.anytypeio.anytype.ui.home.HomeScreenFragment
|
||||
import com.anytypeio.anytype.ui.chats.ChatFragment
|
||||
import dagger.Binds
|
||||
import dagger.BindsInstance
|
||||
import dagger.Component
|
||||
|
@ -34,29 +30,29 @@ import dagger.Module
|
|||
import dagger.Provides
|
||||
|
||||
@Component(
|
||||
dependencies = [DiscussionComponentDependencies::class],
|
||||
dependencies = [ChatComponentDependencies::class],
|
||||
modules = [
|
||||
DiscussionModule::class,
|
||||
DiscussionModule.Declarations::class
|
||||
ChatModule::class,
|
||||
ChatModule.Declarations::class
|
||||
]
|
||||
)
|
||||
@PerScreen
|
||||
interface DiscussionComponent {
|
||||
interface ChatComponent {
|
||||
@Component.Builder
|
||||
interface Builder {
|
||||
@BindsInstance
|
||||
fun withParams(params: DiscussionViewModel.Params): Builder
|
||||
fun withDependencies(dependencies: DiscussionComponentDependencies): Builder
|
||||
fun build(): DiscussionComponent
|
||||
fun withParams(params: ChatViewModel.Params): Builder
|
||||
fun withDependencies(dependencies: ChatComponentDependencies): Builder
|
||||
fun build(): ChatComponent
|
||||
}
|
||||
fun inject(fragment: DiscussionFragment)
|
||||
fun inject(fragment: ChatFragment)
|
||||
}
|
||||
|
||||
@Component(
|
||||
dependencies = [DiscussionComponentDependencies::class],
|
||||
dependencies = [ChatComponentDependencies::class],
|
||||
modules = [
|
||||
DiscussionModule::class,
|
||||
DiscussionModule.Declarations::class
|
||||
ChatModule::class,
|
||||
ChatModule.Declarations::class
|
||||
]
|
||||
)
|
||||
@PerScreen
|
||||
|
@ -64,16 +60,16 @@ interface SpaceLevelChatComponent {
|
|||
@Component.Builder
|
||||
interface Builder {
|
||||
@BindsInstance
|
||||
fun withParams(params: DiscussionViewModel.Params): Builder
|
||||
fun withDependencies(dependencies: DiscussionComponentDependencies): Builder
|
||||
fun withParams(params: ChatViewModel.Params): Builder
|
||||
fun withDependencies(dependencies: ChatComponentDependencies): Builder
|
||||
fun build(): SpaceLevelChatComponent
|
||||
}
|
||||
|
||||
fun getViewModel(): DiscussionViewModel
|
||||
fun getViewModel(): ChatViewModel
|
||||
}
|
||||
|
||||
@Module
|
||||
object DiscussionModule {
|
||||
object ChatModule {
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
|
@ -87,12 +83,12 @@ object DiscussionModule {
|
|||
@PerScreen
|
||||
@Binds
|
||||
fun bindViewModelFactory(
|
||||
factory: DiscussionViewModelFactory
|
||||
factory: ChatViewModelFactory
|
||||
): ViewModelProvider.Factory
|
||||
}
|
||||
}
|
||||
|
||||
interface DiscussionComponentDependencies : ComponentDependencies {
|
||||
interface ChatComponentDependencies : ComponentDependencies {
|
||||
fun blockRepository(): BlockRepository
|
||||
fun authRepo(): AuthRepository
|
||||
fun appCoroutineDispatchers(): AppCoroutineDispatchers
|
|
@ -1,4 +1,4 @@
|
|||
package com.anytypeio.anytype.di.feature.discussions
|
||||
package com.anytypeio.anytype.di.feature.chats
|
||||
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.anytypeio.anytype.core_utils.di.scope.PerScreen
|
||||
|
@ -10,7 +10,7 @@ import com.anytypeio.anytype.domain.config.UserSettingsRepository
|
|||
import com.anytypeio.anytype.emojifier.data.Emoji
|
||||
import com.anytypeio.anytype.emojifier.data.EmojiProvider
|
||||
import com.anytypeio.anytype.emojifier.suggest.EmojiSuggester
|
||||
import com.anytypeio.anytype.feature_discussions.presentation.SelectChatReactionViewModel
|
||||
import com.anytypeio.anytype.feature_chats.presentation.SelectChatReactionViewModel
|
||||
import com.anytypeio.anytype.ui.chats.SelectChatReactionFragment
|
||||
import dagger.Binds
|
||||
import dagger.BindsInstance
|
|
@ -20,9 +20,9 @@ import com.anytypeio.anytype.di.feature.ObjectTypeChangeSubComponent
|
|||
import com.anytypeio.anytype.di.feature.PersonalizationSettingsSubComponent
|
||||
import com.anytypeio.anytype.di.feature.SplashDependencies
|
||||
import com.anytypeio.anytype.di.feature.auth.DeletedAccountDependencies
|
||||
import com.anytypeio.anytype.di.feature.discussions.ChatReactionDependencies
|
||||
import com.anytypeio.anytype.di.feature.discussions.SelectChatReactionDependencies
|
||||
import com.anytypeio.anytype.di.feature.discussions.DiscussionComponentDependencies
|
||||
import com.anytypeio.anytype.di.feature.chats.ChatReactionDependencies
|
||||
import com.anytypeio.anytype.di.feature.chats.SelectChatReactionDependencies
|
||||
import com.anytypeio.anytype.di.feature.chats.ChatComponentDependencies
|
||||
import com.anytypeio.anytype.di.feature.gallery.GalleryInstallationComponentDependencies
|
||||
import com.anytypeio.anytype.di.feature.home.HomeScreenDependencies
|
||||
import com.anytypeio.anytype.di.feature.membership.MembershipComponentDependencies
|
||||
|
@ -132,7 +132,7 @@ interface MainComponent :
|
|||
MembershipUpdateComponentDependencies,
|
||||
VaultComponentDependencies,
|
||||
AllContentDependencies,
|
||||
DiscussionComponentDependencies,
|
||||
ChatComponentDependencies,
|
||||
SelectWidgetSourceDependencies,
|
||||
SelectWidgetTypeDependencies,
|
||||
LinkToObjectDependencies,
|
||||
|
@ -353,7 +353,7 @@ abstract class ComponentDependenciesModule {
|
|||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ComponentDependenciesKey(DiscussionComponentDependencies::class)
|
||||
@ComponentDependenciesKey(ChatComponentDependencies::class)
|
||||
abstract fun provideDiscussionComponentDependencies(component: MainComponent): ComponentDependencies
|
||||
|
||||
@Binds
|
||||
|
|
|
@ -7,7 +7,7 @@ import com.anytypeio.anytype.R
|
|||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.Key
|
||||
import com.anytypeio.anytype.core_models.primitives.SpaceId
|
||||
import com.anytypeio.anytype.di.feature.discussions.DiscussionFragment
|
||||
import com.anytypeio.anytype.ui.chats.ChatFragment
|
||||
import com.anytypeio.anytype.presentation.navigation.AppNavigation
|
||||
import com.anytypeio.anytype.presentation.widgets.collection.Subscription
|
||||
import com.anytypeio.anytype.ui.allcontent.AllContentFragment
|
||||
|
@ -44,7 +44,7 @@ class Navigator : AppNavigation {
|
|||
override fun openChat(target: Id, space: Id) {
|
||||
navController?.navigate(
|
||||
R.id.chatScreen,
|
||||
DiscussionFragment.args(
|
||||
ChatFragment.args(
|
||||
ctx = target,
|
||||
space = space
|
||||
)
|
||||
|
@ -64,7 +64,7 @@ class Navigator : AppNavigation {
|
|||
override fun openDiscussion(target: Id, space: Id) {
|
||||
navController?.navigate(
|
||||
R.id.chatScreen,
|
||||
DiscussionFragment.args(
|
||||
ChatFragment.args(
|
||||
ctx = target,
|
||||
space = space
|
||||
)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package com.anytypeio.anytype.di.feature.discussions
|
||||
package com.anytypeio.anytype.ui.chats
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
|
@ -32,9 +32,9 @@ import com.anytypeio.anytype.core_utils.ext.toast
|
|||
import com.anytypeio.anytype.core_utils.ui.BaseComposeFragment
|
||||
import com.anytypeio.anytype.di.common.componentManager
|
||||
import com.anytypeio.anytype.ext.daggerViewModel
|
||||
import com.anytypeio.anytype.feature_discussions.presentation.DiscussionViewModel
|
||||
import com.anytypeio.anytype.feature_discussions.presentation.DiscussionViewModelFactory
|
||||
import com.anytypeio.anytype.feature_discussions.ui.DiscussionScreenWrapper
|
||||
import com.anytypeio.anytype.feature_chats.presentation.ChatViewModel
|
||||
import com.anytypeio.anytype.feature_chats.presentation.ChatViewModelFactory
|
||||
import com.anytypeio.anytype.feature_chats.ui.ChatScreenWrapper
|
||||
import com.anytypeio.anytype.presentation.home.OpenObjectNavigation
|
||||
import com.anytypeio.anytype.presentation.search.GlobalSearchViewModel
|
||||
import com.anytypeio.anytype.ui.editor.EditorFragment
|
||||
|
@ -43,12 +43,12 @@ import com.anytypeio.anytype.ui.settings.typography
|
|||
import javax.inject.Inject
|
||||
import timber.log.Timber
|
||||
|
||||
class DiscussionFragment : BaseComposeFragment() {
|
||||
class ChatFragment : BaseComposeFragment() {
|
||||
|
||||
@Inject
|
||||
lateinit var factory: DiscussionViewModelFactory
|
||||
lateinit var factory: ChatViewModelFactory
|
||||
|
||||
private val vm by viewModels<DiscussionViewModel> { factory }
|
||||
private val vm by viewModels<ChatViewModel> { factory }
|
||||
|
||||
private val ctx get() = arg<Id>(CTX_KEY)
|
||||
private val space get() = arg<Id>(SPACE_KEY)
|
||||
|
@ -70,7 +70,7 @@ class DiscussionFragment : BaseComposeFragment() {
|
|||
|
||||
var showBottomSheet by remember { mutableStateOf(false) }
|
||||
|
||||
DiscussionScreenWrapper(
|
||||
ChatScreenWrapper(
|
||||
vm = vm,
|
||||
onAttachObjectClicked = {
|
||||
showBottomSheet = true
|
||||
|
@ -160,10 +160,10 @@ class DiscussionFragment : BaseComposeFragment() {
|
|||
|
||||
override fun injectDependencies() {
|
||||
componentManager()
|
||||
.discussionComponent
|
||||
.chatComponent
|
||||
.get(
|
||||
key = ctx,
|
||||
param = DiscussionViewModel.Params.Default(
|
||||
param = ChatViewModel.Params.Default(
|
||||
ctx = ctx,
|
||||
space = SpaceId(space)
|
||||
)
|
||||
|
@ -172,7 +172,7 @@ class DiscussionFragment : BaseComposeFragment() {
|
|||
}
|
||||
|
||||
override fun releaseDependencies() {
|
||||
componentManager().discussionComponent.release(ctx)
|
||||
componentManager().chatComponent.release(ctx)
|
||||
}
|
||||
|
||||
override fun onApplyWindowRootInsets(view: View) {
|
|
@ -15,8 +15,8 @@ import com.anytypeio.anytype.core_models.primitives.SpaceId
|
|||
import com.anytypeio.anytype.core_utils.ext.arg
|
||||
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetComposeFragment
|
||||
import com.anytypeio.anytype.di.common.componentManager
|
||||
import com.anytypeio.anytype.feature_discussions.presentation.ChatReactionViewModel
|
||||
import com.anytypeio.anytype.feature_discussions.ui.ChatReactionScreen
|
||||
import com.anytypeio.anytype.feature_chats.presentation.ChatReactionViewModel
|
||||
import com.anytypeio.anytype.feature_chats.ui.ChatReactionScreen
|
||||
import com.anytypeio.anytype.ui.settings.typography
|
||||
import javax.inject.Inject
|
||||
import kotlin.getValue
|
||||
|
|
|
@ -16,8 +16,8 @@ import com.anytypeio.anytype.core_models.primitives.SpaceId
|
|||
import com.anytypeio.anytype.core_utils.ext.arg
|
||||
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetComposeFragment
|
||||
import com.anytypeio.anytype.di.common.componentManager
|
||||
import com.anytypeio.anytype.feature_discussions.presentation.SelectChatReactionViewModel
|
||||
import com.anytypeio.anytype.feature_discussions.ui.SelectChatReactionScreen
|
||||
import com.anytypeio.anytype.feature_chats.presentation.SelectChatReactionViewModel
|
||||
import com.anytypeio.anytype.feature_chats.ui.SelectChatReactionScreen
|
||||
import com.anytypeio.anytype.ui.settings.typography
|
||||
import javax.inject.Inject
|
||||
import kotlin.getValue
|
||||
|
|
|
@ -53,8 +53,8 @@ import com.anytypeio.anytype.core_utils.tools.FeatureToggles
|
|||
import com.anytypeio.anytype.core_utils.ui.BaseComposeFragment
|
||||
import com.anytypeio.anytype.di.common.componentManager
|
||||
import com.anytypeio.anytype.ext.daggerViewModel
|
||||
import com.anytypeio.anytype.feature_discussions.presentation.DiscussionViewModel
|
||||
import com.anytypeio.anytype.feature_discussions.ui.DiscussionScreenWrapper
|
||||
import com.anytypeio.anytype.feature_chats.presentation.ChatViewModel
|
||||
import com.anytypeio.anytype.feature_chats.ui.ChatScreenWrapper
|
||||
import com.anytypeio.anytype.other.DefaultDeepLinkResolver
|
||||
import com.anytypeio.anytype.presentation.home.Command
|
||||
import com.anytypeio.anytype.presentation.home.HomeScreenViewModel
|
||||
|
@ -140,7 +140,7 @@ class HomeScreenFragment : BaseComposeFragment(),
|
|||
val spaceLevelChatViewModel = daggerViewModel {
|
||||
component.get(
|
||||
key = space,
|
||||
param = DiscussionViewModel.Params.SpaceLevelChat(
|
||||
param = ChatViewModel.Params.SpaceLevelChat(
|
||||
space = Space(space)
|
||||
)
|
||||
).getViewModel()
|
||||
|
@ -180,7 +180,7 @@ class HomeScreenFragment : BaseComposeFragment(),
|
|||
focus.clearFocus(force = true)
|
||||
PageWithWidgets(showSpaceWidget = false)
|
||||
} else if (page == 0) {
|
||||
DiscussionScreenWrapper(
|
||||
ChatScreenWrapper(
|
||||
isSpaceLevelChat = true,
|
||||
vm = spaceLevelChatViewModel,
|
||||
onAttachObjectClicked = {
|
||||
|
@ -569,7 +569,7 @@ class HomeScreenFragment : BaseComposeFragment(),
|
|||
view = destination.view
|
||||
)
|
||||
}
|
||||
is Navigation.OpenDiscussion -> runCatching {
|
||||
is Navigation.OpenChat -> runCatching {
|
||||
navigation().openDiscussion(
|
||||
target = destination.ctx,
|
||||
space = destination.space
|
||||
|
|
|
@ -2,7 +2,6 @@ package com.anytypeio.anytype.ui.home
|
|||
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
|
@ -14,7 +13,6 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.pluralStringResource
|
||||
|
@ -25,8 +23,7 @@ import com.anytypeio.anytype.core_ui.features.SpaceIconView
|
|||
import com.anytypeio.anytype.core_ui.foundation.noRippleClickable
|
||||
import com.anytypeio.anytype.core_ui.views.PreviewTitle2Medium
|
||||
import com.anytypeio.anytype.core_ui.views.Relations2
|
||||
import com.anytypeio.anytype.core_ui.views.Relations3
|
||||
import com.anytypeio.anytype.feature_discussions.R
|
||||
import com.anytypeio.anytype.feature_chats.R
|
||||
import com.anytypeio.anytype.presentation.spaces.SpaceIconView
|
||||
|
||||
@Composable
|
||||
|
|
|
@ -18,7 +18,6 @@ import androidx.lifecycle.repeatOnLifecycle
|
|||
import androidx.navigation.NavOptions
|
||||
import androidx.navigation.NavOptions.*
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.anytypeio.anytype.BuildConfig
|
||||
import com.anytypeio.anytype.R
|
||||
import com.anytypeio.anytype.analytics.base.EventsDictionary
|
||||
|
@ -226,7 +225,7 @@ class MainActivity : AppCompatActivity(R.layout.activity_main), AppNavigation.Pr
|
|||
Timber.e(it, "Error while editor navigation")
|
||||
}
|
||||
}
|
||||
is OpenObjectNavigation.OpenDiscussion -> {
|
||||
is OpenObjectNavigation.OpenChat -> {
|
||||
toast("Cannot open chat from here")
|
||||
}
|
||||
is OpenObjectNavigation.UnexpectedLayoutError -> {
|
||||
|
|
|
@ -22,7 +22,7 @@ import com.anytypeio.anytype.core_utils.ext.setupBottomSheetBehavior
|
|||
import com.anytypeio.anytype.core_utils.ext.toast
|
||||
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetComposeFragment
|
||||
import com.anytypeio.anytype.di.common.componentManager
|
||||
import com.anytypeio.anytype.di.feature.discussions.DiscussionFragment
|
||||
import com.anytypeio.anytype.ui.chats.ChatFragment
|
||||
import com.anytypeio.anytype.presentation.home.OpenObjectNavigation
|
||||
import com.anytypeio.anytype.presentation.search.GlobalSearchViewModel
|
||||
import com.anytypeio.anytype.ui.date.DateObjectFragment
|
||||
|
@ -92,10 +92,10 @@ class GlobalSearchFragment : BaseBottomSheetComposeFragment() {
|
|||
)
|
||||
)
|
||||
}
|
||||
is OpenObjectNavigation.OpenDiscussion -> {
|
||||
is OpenObjectNavigation.OpenChat -> {
|
||||
findNavController().navigate(
|
||||
R.id.chatScreen,
|
||||
DiscussionFragment.args(
|
||||
ChatFragment.args(
|
||||
ctx = nav.target,
|
||||
space = nav.space
|
||||
)
|
||||
|
|
|
@ -159,7 +159,7 @@
|
|||
|
||||
<fragment
|
||||
android:id="@+id/chatScreen"
|
||||
android:name="com.anytypeio.anytype.di.feature.discussions.DiscussionFragment"
|
||||
android:name="com.anytypeio.anytype.ui.chats.ChatFragment"
|
||||
android:label="Discussion" />
|
||||
|
||||
<dialog
|
||||
|
|
|
@ -197,7 +197,7 @@ fun cornerRadius(size: Dp): Dp {
|
|||
fun imageAsset(emptyType: ObjectIcon.Empty): Int {
|
||||
return when (emptyType) {
|
||||
ObjectIcon.Empty.Bookmark -> R.drawable.ic_empty_state_link
|
||||
ObjectIcon.Empty.Discussion -> R.drawable.ic_empty_state_chat
|
||||
ObjectIcon.Empty.Chat -> R.drawable.ic_empty_state_chat
|
||||
ObjectIcon.Empty.List -> R.drawable.ic_empty_state_list
|
||||
ObjectIcon.Empty.ObjectType -> R.drawable.ic_empty_state_type
|
||||
ObjectIcon.Empty.Page -> R.drawable.ic_empty_state_page
|
||||
|
|
|
@ -363,7 +363,7 @@ class ObjectIconWidget @JvmOverloads constructor(
|
|||
private fun ObjectIcon.Empty.setEmptyIcon() {
|
||||
val (drawable, containerBackground) = when (this) {
|
||||
ObjectIcon.Empty.Bookmark -> R.drawable.ic_empty_state_link to true
|
||||
ObjectIcon.Empty.Discussion -> R.drawable.ic_empty_state_chat to true
|
||||
ObjectIcon.Empty.Chat -> R.drawable.ic_empty_state_chat to true
|
||||
ObjectIcon.Empty.List -> R.drawable.ic_empty_state_list to true
|
||||
ObjectIcon.Empty.ObjectType -> R.drawable.ic_empty_state_type to true
|
||||
ObjectIcon.Empty.Page -> R.drawable.ic_empty_state_page to true
|
||||
|
|
|
@ -710,7 +710,7 @@ class AllContentViewModel(
|
|||
Timber.e("Unexpected layout: ${navigation.layout}")
|
||||
commands.emit(Command.SendToast.UnexpectedLayout(navigation.layout?.name.orEmpty()))
|
||||
}
|
||||
is OpenObjectNavigation.OpenDiscussion -> {
|
||||
is OpenObjectNavigation.OpenChat -> {
|
||||
commands.emit(
|
||||
Command.OpenChat(
|
||||
target = navigation.target,
|
||||
|
|
|
@ -9,7 +9,7 @@ android {
|
|||
compose true
|
||||
}
|
||||
|
||||
namespace 'com.anytypeio.anytype.feature_discussions'
|
||||
namespace 'com.anytypeio.anytype.feature_chats'
|
||||
|
||||
testOptions {
|
||||
unitTests.returnDefaultValues = true
|
|
@ -1,4 +1,4 @@
|
|||
package com.anytypeio.anytype.feature_discussions.presentation
|
||||
package com.anytypeio.anytype.feature_chats.presentation
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
|
@ -1,19 +1,18 @@
|
|||
package com.anytypeio.anytype.feature_discussions.presentation
|
||||
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.ObjectWrapper
|
||||
import com.anytypeio.anytype.core_models.Url
|
||||
import com.anytypeio.anytype.presentation.objects.ObjectIcon
|
||||
import com.anytypeio.anytype.presentation.search.GlobalSearchItemView
|
||||
|
||||
sealed interface DiscussionView {
|
||||
sealed interface ChatView {
|
||||
|
||||
data class DateSection(
|
||||
val formattedDate: String,
|
||||
val timeInMillis: Long
|
||||
) : DiscussionView
|
||||
) : ChatView
|
||||
|
||||
data class Message(
|
||||
val id: String,
|
||||
|
@ -26,7 +25,7 @@ sealed interface DiscussionView {
|
|||
val isEdited: Boolean = false,
|
||||
val avatar: Avatar = Avatar.Initials(),
|
||||
val reply: Reply? = null
|
||||
) : DiscussionView {
|
||||
) : ChatView {
|
||||
|
||||
data class Content(val msg: String, val parts: List<Part>) {
|
||||
data class Part(
|
|
@ -1,4 +1,4 @@
|
|||
package com.anytypeio.anytype.feature_discussions.presentation
|
||||
package com.anytypeio.anytype.feature_chats.presentation
|
||||
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.anytypeio.anytype.core_models.Command
|
||||
|
@ -46,7 +46,7 @@ import kotlinx.coroutines.launch
|
|||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
|
||||
class DiscussionViewModel @Inject constructor(
|
||||
class ChatViewModel @Inject constructor(
|
||||
private val vmParams: Params,
|
||||
private val setObjectDetails: SetObjectDetails,
|
||||
private val openObject: OpenObject,
|
||||
|
@ -66,8 +66,8 @@ class DiscussionViewModel @Inject constructor(
|
|||
) : BaseViewModel() {
|
||||
|
||||
val name = MutableStateFlow<String?>(null)
|
||||
val messages = MutableStateFlow<List<DiscussionView>>(emptyList())
|
||||
val chatBoxAttachments = MutableStateFlow<List<DiscussionView.Message.ChatBoxAttachment>>(emptyList())
|
||||
val messages = MutableStateFlow<List<ChatView>>(emptyList())
|
||||
val chatBoxAttachments = MutableStateFlow<List<ChatView.Message.ChatBoxAttachment>>(emptyList())
|
||||
val commands = MutableSharedFlow<UXCommand>()
|
||||
val navigation = MutableSharedFlow<OpenObjectNavigation>()
|
||||
val chatBoxMode = MutableStateFlow<ChatBoxMode>(ChatBoxMode.Default)
|
||||
|
@ -129,8 +129,8 @@ class DiscussionViewModel @Inject constructor(
|
|||
chatContainer.fetchReplies(chat = chat)
|
||||
) { result, dependencies, replies ->
|
||||
data.value = result
|
||||
var previousDate: DiscussionView.DateSection? = null
|
||||
buildList<DiscussionView> {
|
||||
var previousDate: ChatView.DateSection? = null
|
||||
buildList<ChatView> {
|
||||
result.forEach { msg ->
|
||||
val allMembers = members.get()
|
||||
val member = allMembers.let { type ->
|
||||
|
@ -152,7 +152,7 @@ class DiscussionViewModel @Inject constructor(
|
|||
} else {
|
||||
val msg = replies[replyToId]
|
||||
if (msg != null) {
|
||||
DiscussionView.Message.Reply(
|
||||
ChatView.Message.Reply(
|
||||
msg = msg.id,
|
||||
text = msg.content?.text.orEmpty().ifEmpty {
|
||||
// Fallback to attachment name if empty
|
||||
|
@ -184,16 +184,16 @@ class DiscussionViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
val view = DiscussionView.Message(
|
||||
val view = ChatView.Message(
|
||||
id = msg.id,
|
||||
timestamp = msg.createdAt * 1000,
|
||||
content = DiscussionView.Message.Content(
|
||||
content = ChatView.Message.Content(
|
||||
msg = content?.text.orEmpty(),
|
||||
parts = content?.text
|
||||
.orEmpty()
|
||||
.splitByMarks(marks = content?.marks.orEmpty())
|
||||
.map { (part, styles) ->
|
||||
DiscussionView.Message.Content.Part(
|
||||
ChatView.Message.Content.Part(
|
||||
part = part,
|
||||
styles = styles
|
||||
)
|
||||
|
@ -204,7 +204,7 @@ class DiscussionViewModel @Inject constructor(
|
|||
isUserAuthor = msg.creator == account,
|
||||
isEdited = msg.modifiedAt > msg.createdAt,
|
||||
reactions = msg.reactions.map { (emoji, ids) ->
|
||||
DiscussionView.Message.Reaction(
|
||||
ChatView.Message.Reaction(
|
||||
emoji = emoji,
|
||||
count = ids.size,
|
||||
isSelected = ids.contains(account)
|
||||
|
@ -214,7 +214,7 @@ class DiscussionViewModel @Inject constructor(
|
|||
when (attachment.type) {
|
||||
Chat.Message.Attachment.Type.Image -> {
|
||||
val wrapper = dependencies[attachment.target]
|
||||
DiscussionView.Message.Attachment.Image(
|
||||
ChatView.Message.Attachment.Image(
|
||||
target = attachment.target,
|
||||
url = urlBuilder.medium(path = attachment.target),
|
||||
name = wrapper?.name.orEmpty(),
|
||||
|
@ -224,7 +224,7 @@ class DiscussionViewModel @Inject constructor(
|
|||
else -> {
|
||||
val wrapper = dependencies[attachment.target]
|
||||
if (wrapper?.layout == ObjectType.Layout.IMAGE) {
|
||||
DiscussionView.Message.Attachment.Image(
|
||||
ChatView.Message.Attachment.Image(
|
||||
target = attachment.target,
|
||||
url = urlBuilder.large(path = attachment.target),
|
||||
name = wrapper.name.orEmpty(),
|
||||
|
@ -232,7 +232,7 @@ class DiscussionViewModel @Inject constructor(
|
|||
)
|
||||
} else {
|
||||
val type = wrapper?.type?.firstOrNull()
|
||||
DiscussionView.Message.Attachment.Link(
|
||||
ChatView.Message.Attachment.Link(
|
||||
target = attachment.target,
|
||||
wrapper = wrapper,
|
||||
icon = wrapper?.objectIcon(urlBuilder) ?: ObjectIcon.None,
|
||||
|
@ -246,14 +246,14 @@ class DiscussionViewModel @Inject constructor(
|
|||
}
|
||||
},
|
||||
avatar = if (member != null && !member.iconImage.isNullOrEmpty()) {
|
||||
DiscussionView.Message.Avatar.Image(
|
||||
ChatView.Message.Avatar.Image(
|
||||
urlBuilder.thumbnail(member.iconImage!!)
|
||||
)
|
||||
} else {
|
||||
DiscussionView.Message.Avatar.Initials(member?.name.orEmpty())
|
||||
ChatView.Message.Avatar.Initials(member?.name.orEmpty())
|
||||
}
|
||||
)
|
||||
val currDate = DiscussionView.DateSection(
|
||||
val currDate = ChatView.DateSection(
|
||||
formattedDate = dateFormatter.format(msg.createdAt * 1000),
|
||||
timeInMillis = msg.createdAt * 1000L
|
||||
)
|
||||
|
@ -275,7 +275,7 @@ class DiscussionViewModel @Inject constructor(
|
|||
val attachments = buildList {
|
||||
chatBoxAttachments.value.forEach { attachment ->
|
||||
when(attachment) {
|
||||
is DiscussionView.Message.ChatBoxAttachment.Link -> {
|
||||
is ChatView.Message.ChatBoxAttachment.Link -> {
|
||||
add(
|
||||
Chat.Message.Attachment(
|
||||
target = attachment.target,
|
||||
|
@ -283,7 +283,7 @@ class DiscussionViewModel @Inject constructor(
|
|||
)
|
||||
)
|
||||
}
|
||||
is DiscussionView.Message.ChatBoxAttachment.Media -> {
|
||||
is ChatView.Message.ChatBoxAttachment.Media -> {
|
||||
uploadFile.async(
|
||||
UploadFile.Params(
|
||||
space = vmParams.space,
|
||||
|
@ -298,7 +298,7 @@ class DiscussionViewModel @Inject constructor(
|
|||
)
|
||||
}
|
||||
}
|
||||
is DiscussionView.Message.ChatBoxAttachment.File -> {
|
||||
is ChatView.Message.ChatBoxAttachment.File -> {
|
||||
val path = withContext(dispatchers.io) {
|
||||
copyFileToCacheDirectory.copy(attachment.uri)
|
||||
}
|
||||
|
@ -391,7 +391,7 @@ class DiscussionViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun onRequestEditMessageClicked(msg: DiscussionView.Message) {
|
||||
fun onRequestEditMessageClicked(msg: ChatView.Message) {
|
||||
Timber.d("onRequestEditMessageClicked")
|
||||
viewModelScope.launch {
|
||||
chatBoxMode.value = ChatBoxMode.EditMessage(msg.id)
|
||||
|
@ -419,14 +419,14 @@ class DiscussionViewModel @Inject constructor(
|
|||
|
||||
fun onAttachObject(obj: GlobalSearchItemView) {
|
||||
chatBoxAttachments.value = chatBoxAttachments.value + listOf(
|
||||
DiscussionView.Message.ChatBoxAttachment.Link(
|
||||
ChatView.Message.ChatBoxAttachment.Link(
|
||||
target = obj.id,
|
||||
wrapper = obj
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun onClearAttachmentClicked(attachment: DiscussionView.Message.ChatBoxAttachment) {
|
||||
fun onClearAttachmentClicked(attachment: ChatView.Message.ChatBoxAttachment) {
|
||||
chatBoxAttachments.value = chatBoxAttachments.value.filter {
|
||||
it != attachment
|
||||
}
|
||||
|
@ -441,7 +441,7 @@ class DiscussionViewModel @Inject constructor(
|
|||
fun onReacted(msg: Id, reaction: String) {
|
||||
Timber.d("onReacted")
|
||||
viewModelScope.launch {
|
||||
val message = messages.value.find { it is DiscussionView.Message && it.id == msg }
|
||||
val message = messages.value.find { it is ChatView.Message && it.id == msg }
|
||||
if (message != null) {
|
||||
toggleChatMessageReaction.async(
|
||||
Command.ChatCommand.ToggleMessageReaction(
|
||||
|
@ -458,7 +458,7 @@ class DiscussionViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun onReplyMessage(msg: DiscussionView.Message) {
|
||||
fun onReplyMessage(msg: ChatView.Message) {
|
||||
viewModelScope.launch {
|
||||
chatBoxMode.value = ChatBoxMode.Reply(
|
||||
msg = msg.id,
|
||||
|
@ -467,14 +467,14 @@ class DiscussionViewModel @Inject constructor(
|
|||
if (msg.attachments.isNotEmpty()) {
|
||||
val attachment = msg.attachments.last()
|
||||
when(attachment) {
|
||||
is DiscussionView.Message.Attachment.Image -> {
|
||||
is ChatView.Message.Attachment.Image -> {
|
||||
if (attachment.ext.isNotEmpty()) {
|
||||
"${attachment.name}.${attachment.ext}"
|
||||
} else {
|
||||
attachment.name
|
||||
}
|
||||
}
|
||||
is DiscussionView.Message.Attachment.Link -> {
|
||||
is ChatView.Message.Attachment.Link -> {
|
||||
attachment.wrapper?.name.orEmpty()
|
||||
}
|
||||
}
|
||||
|
@ -487,7 +487,7 @@ class DiscussionViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun onDeleteMessage(msg: DiscussionView.Message) {
|
||||
fun onDeleteMessage(msg: ChatView.Message) {
|
||||
Timber.d("onDeleteMessageClicked")
|
||||
viewModelScope.launch {
|
||||
deleteChatMessage.async(
|
||||
|
@ -501,18 +501,18 @@ class DiscussionViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun onAttachmentClicked(attachment: DiscussionView.Message.Attachment) {
|
||||
fun onAttachmentClicked(attachment: ChatView.Message.Attachment) {
|
||||
Timber.d("onAttachmentClicked")
|
||||
viewModelScope.launch {
|
||||
when(attachment) {
|
||||
is DiscussionView.Message.Attachment.Image -> {
|
||||
is ChatView.Message.Attachment.Image -> {
|
||||
commands.emit(
|
||||
UXCommand.OpenFullScreenImage(
|
||||
url = urlBuilder.original(attachment.target)
|
||||
)
|
||||
)
|
||||
}
|
||||
is DiscussionView.Message.Attachment.Link -> {
|
||||
is ChatView.Message.Attachment.Link -> {
|
||||
val wrapper = attachment.wrapper
|
||||
if (wrapper != null) {
|
||||
navigation.emit(wrapper.navigation())
|
||||
|
@ -527,7 +527,7 @@ class DiscussionViewModel @Inject constructor(
|
|||
fun onChatBoxMediaPicked(uris: List<String>) {
|
||||
Timber.d("onChatBoxMediaPicked: $uris")
|
||||
chatBoxAttachments.value = chatBoxAttachments.value + uris.map {
|
||||
DiscussionView.Message.ChatBoxAttachment.Media(
|
||||
ChatView.Message.ChatBoxAttachment.Media(
|
||||
uri = it
|
||||
)
|
||||
}
|
||||
|
@ -536,7 +536,7 @@ class DiscussionViewModel @Inject constructor(
|
|||
fun onChatBoxFilePicked(infos: List<DefaultFileInfo>) {
|
||||
Timber.d("onChatBoxFilePicked: $infos")
|
||||
chatBoxAttachments.value = chatBoxAttachments.value + infos.map { info ->
|
||||
DiscussionView.Message.ChatBoxAttachment.File(
|
||||
ChatView.Message.ChatBoxAttachment.File(
|
||||
uri = info.uri,
|
||||
name = info.name,
|
||||
size = info.size
|
|
@ -1,4 +1,4 @@
|
|||
package com.anytypeio.anytype.feature_discussions.presentation
|
||||
package com.anytypeio.anytype.feature_chats.presentation
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
|
@ -9,7 +9,6 @@ import com.anytypeio.anytype.domain.chats.ChatContainer
|
|||
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.FileDrop
|
||||
import com.anytypeio.anytype.domain.media.UploadFile
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.domain.multiplayer.ActiveSpaceMemberSubscriptionContainer
|
||||
|
@ -17,13 +16,11 @@ 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.StoreOfObjectTypes
|
||||
import com.anytypeio.anytype.emojifier.data.EmojiProvider
|
||||
import com.anytypeio.anytype.presentation.common.BaseViewModel
|
||||
import com.anytypeio.anytype.presentation.util.CopyFileToCacheDirectory
|
||||
import javax.inject.Inject
|
||||
|
||||
class DiscussionViewModelFactory @Inject constructor(
|
||||
private val params: DiscussionViewModel.Params,
|
||||
class ChatViewModelFactory @Inject constructor(
|
||||
private val params: ChatViewModel.Params,
|
||||
private val setObjectDetails: SetObjectDetails,
|
||||
private val openObject: OpenObject,
|
||||
private val chatContainer: ChatContainer,
|
||||
|
@ -41,7 +38,7 @@ class DiscussionViewModelFactory @Inject constructor(
|
|||
private val copyFileToCacheDirectory: CopyFileToCacheDirectory
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T = DiscussionViewModel(
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T = ChatViewModel(
|
||||
vmParams = params,
|
||||
setObjectDetails = setObjectDetails,
|
||||
openObject = openObject,
|
|
@ -1,4 +1,4 @@
|
|||
package com.anytypeio.anytype.feature_discussions.presentation
|
||||
package com.anytypeio.anytype.feature_chats.presentation
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
|
@ -0,0 +1,184 @@
|
|||
package com.anytypeio.anytype.feature_chats.ui
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
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.layout.size
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
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.layout.ContentScale
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.anytypeio.anytype.core_ui.views.PreviewTitle2Medium
|
||||
import com.anytypeio.anytype.core_ui.views.Relations3
|
||||
import com.anytypeio.anytype.core_ui.widgets.ListWidgetObjectIcon
|
||||
import com.anytypeio.anytype.feature_chats.R
|
||||
import com.anytypeio.anytype.feature_chats.presentation.ChatView
|
||||
import com.anytypeio.anytype.presentation.objects.ObjectIcon
|
||||
import com.bumptech.glide.integration.compose.ExperimentalGlideComposeApi
|
||||
import com.bumptech.glide.integration.compose.GlideImage
|
||||
|
||||
@Composable
|
||||
@OptIn(ExperimentalGlideComposeApi::class)
|
||||
fun BubbleAttachments(
|
||||
attachments: List<ChatView.Message.Attachment>,
|
||||
onAttachmentClicked: (ChatView.Message.Attachment) -> Unit,
|
||||
isUserAuthor: Boolean
|
||||
) {
|
||||
attachments.forEachIndexed { idx, attachment ->
|
||||
when (attachment) {
|
||||
is ChatView.Message.Attachment.Image -> {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
start = 4.dp,
|
||||
end = 4.dp,
|
||||
bottom = 4.dp,
|
||||
top = if (idx == 0) 4.dp else 0.dp
|
||||
)
|
||||
.size(300.dp)
|
||||
.background(
|
||||
color = colorResource(R.color.shape_tertiary),
|
||||
shape = RoundedCornerShape(16.dp)
|
||||
)
|
||||
) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier
|
||||
.align(alignment = Alignment.Center)
|
||||
.size(64.dp),
|
||||
color = colorResource(R.color.glyph_active),
|
||||
trackColor = colorResource(R.color.glyph_active).copy(alpha = 0.5f),
|
||||
strokeWidth = 8.dp
|
||||
)
|
||||
GlideImage(
|
||||
model = attachment.url,
|
||||
contentDescription = "Attachment image",
|
||||
contentScale = ContentScale.Crop,
|
||||
modifier = Modifier
|
||||
.size(300.dp)
|
||||
.clip(shape = RoundedCornerShape(16.dp))
|
||||
.clickable {
|
||||
onAttachmentClicked(attachment)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
is ChatView.Message.Attachment.Link -> {
|
||||
AttachedObject(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
start = 4.dp,
|
||||
end = 4.dp,
|
||||
bottom = 4.dp,
|
||||
top = if (idx == 0) 4.dp else 0.dp
|
||||
)
|
||||
.fillMaxWidth()
|
||||
,
|
||||
title = attachment.wrapper?.name.orEmpty(),
|
||||
type = attachment.typeName,
|
||||
icon = attachment.icon,
|
||||
onAttachmentClicked = {
|
||||
onAttachmentClicked(attachment)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AttachedObject(
|
||||
modifier: Modifier,
|
||||
title: String,
|
||||
type: String,
|
||||
icon: ObjectIcon,
|
||||
onAttachmentClicked: () -> Unit
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.height(72.dp)
|
||||
.clip(RoundedCornerShape(18.dp))
|
||||
.border(
|
||||
width = 1.dp,
|
||||
color = colorResource(id = R.color.shape_transparent_secondary),
|
||||
shape = RoundedCornerShape(18.dp)
|
||||
)
|
||||
.background(
|
||||
color = colorResource(id = R.color.background_secondary)
|
||||
)
|
||||
.clickable {
|
||||
onAttachmentClicked()
|
||||
}
|
||||
) {
|
||||
ListWidgetObjectIcon(
|
||||
icon = icon,
|
||||
iconSize = 48.dp,
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
start = 12.dp
|
||||
)
|
||||
.align(alignment = Alignment.CenterStart),
|
||||
onTaskIconClicked = {
|
||||
// Do nothing
|
||||
}
|
||||
)
|
||||
Text(
|
||||
text = title.ifEmpty { stringResource(R.string.untitled) },
|
||||
modifier = Modifier.padding(
|
||||
start = if (icon != ObjectIcon.None)
|
||||
72.dp
|
||||
else
|
||||
12.dp,
|
||||
top = 17.5.dp,
|
||||
end = 12.dp
|
||||
),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
style = PreviewTitle2Medium,
|
||||
color = colorResource(id = R.color.text_primary)
|
||||
)
|
||||
Text(
|
||||
text = type.ifEmpty { stringResource(R.string.unknown_type) },
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomStart)
|
||||
.padding(
|
||||
start = if (icon != ObjectIcon.None)
|
||||
72.dp
|
||||
else
|
||||
12.dp,
|
||||
bottom = 17.5.dp,
|
||||
end = 12.dp
|
||||
),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
style = Relations3,
|
||||
color = colorResource(id = R.color.text_secondary)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, name = "Light Mode")
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO, name = "Dark Mode")
|
||||
@Composable
|
||||
fun AttachmentPreview() {
|
||||
AttachedObject(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
icon = ObjectIcon.None,
|
||||
type = "Project",
|
||||
title = "Travel to Switzerland",
|
||||
onAttachmentClicked = {}
|
||||
)
|
||||
}
|
|
@ -0,0 +1,470 @@
|
|||
package com.anytypeio.anytype.feature_chats.ui
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.PickVisualMediaRequest
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.scaleIn
|
||||
import androidx.compose.animation.scaleOut
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyRow
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.DropdownMenu
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.DpOffset
|
||||
import androidx.compose.ui.unit.dp
|
||||
import coil.compose.rememberAsyncImagePainter
|
||||
import com.anytypeio.anytype.core_ui.foundation.Divider
|
||||
import com.anytypeio.anytype.core_ui.foundation.noRippleClickable
|
||||
import com.anytypeio.anytype.core_ui.views.BodyRegular
|
||||
import com.anytypeio.anytype.core_ui.views.Caption1Medium
|
||||
import com.anytypeio.anytype.core_ui.views.Caption1Regular
|
||||
import com.anytypeio.anytype.feature_chats.R
|
||||
import com.anytypeio.anytype.feature_chats.presentation.ChatConfig
|
||||
import com.anytypeio.anytype.feature_chats.presentation.ChatView
|
||||
import com.anytypeio.anytype.feature_chats.presentation.ChatViewModel.ChatBoxMode
|
||||
import com.anytypeio.anytype.presentation.objects.ObjectIcon
|
||||
import kotlin.collections.forEach
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun ChatBox(
|
||||
mode: ChatBoxMode = ChatBoxMode.Default,
|
||||
modifier: Modifier = Modifier,
|
||||
onBackButtonClicked: () -> Unit,
|
||||
chatBoxFocusRequester: FocusRequester,
|
||||
textState: TextFieldValue,
|
||||
onMessageSent: (String) -> Unit = {},
|
||||
onAttachClicked: () -> Unit = {},
|
||||
resetScroll: () -> Unit = {},
|
||||
isTitleFocused: Boolean,
|
||||
attachments: List<ChatView.Message.ChatBoxAttachment>,
|
||||
clearText: () -> Unit,
|
||||
updateValue: (TextFieldValue) -> Unit,
|
||||
onAttachObjectClicked: () -> Unit,
|
||||
onAttachMediaClicked: () -> Unit,
|
||||
onAttachFileClicked: () -> Unit,
|
||||
onUploadAttachmentClicked: () -> Unit,
|
||||
onClearAttachmentClicked: (ChatView.Message.ChatBoxAttachment) -> Unit,
|
||||
onClearReplyClicked: () -> Unit,
|
||||
onChatBoxMediaPicked: (List<Uri>) -> Unit,
|
||||
onChatBoxFilePicked: (List<Uri>) -> Unit,
|
||||
onExitEditMessageMode: () -> Unit
|
||||
) {
|
||||
|
||||
val uploadMediaLauncher = rememberLauncherForActivityResult(
|
||||
ActivityResultContracts.PickMultipleVisualMedia(maxItems = ChatConfig.MAX_ATTACHMENT_COUNT)
|
||||
) {
|
||||
onChatBoxMediaPicked(it)
|
||||
}
|
||||
|
||||
val uploadFileLauncher = rememberLauncherForActivityResult(
|
||||
ActivityResultContracts.OpenMultipleDocuments()
|
||||
) { uris ->
|
||||
onChatBoxFilePicked(uris.take(ChatConfig.MAX_ATTACHMENT_COUNT))
|
||||
}
|
||||
|
||||
var showDropdownMenu by remember { mutableStateOf(false) }
|
||||
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
val focus = LocalFocusManager.current
|
||||
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.defaultMinSize(minHeight = 56.dp)
|
||||
.padding(
|
||||
start = 12.dp,
|
||||
end = 12.dp,
|
||||
bottom = 20.dp
|
||||
)
|
||||
.background(
|
||||
color = colorResource(R.color.navigation_panel),
|
||||
shape = RoundedCornerShape(16.dp)
|
||||
)
|
||||
) {
|
||||
if (mode is ChatBoxMode.EditMessage) {
|
||||
EditMessageToolbar(
|
||||
onExitClicked = onExitEditMessageMode
|
||||
)
|
||||
}
|
||||
LazyRow(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
) {
|
||||
attachments.forEach { attachment ->
|
||||
when(attachment) {
|
||||
is ChatView.Message.ChatBoxAttachment.Link -> {
|
||||
item {
|
||||
Box {
|
||||
AttachedObject(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
top = 12.dp,
|
||||
end = 4.dp
|
||||
)
|
||||
.width(216.dp),
|
||||
title = attachment.wrapper.title,
|
||||
type = attachment.wrapper.type,
|
||||
icon = attachment.wrapper.icon,
|
||||
onAttachmentClicked = {
|
||||
// TODO
|
||||
}
|
||||
)
|
||||
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)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
is ChatView.Message.ChatBoxAttachment.Media -> {
|
||||
item {
|
||||
Box(modifier = Modifier.padding()) {
|
||||
Image(
|
||||
painter = rememberAsyncImagePainter(attachment.uri),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
top = 12.dp,
|
||||
end = 4.dp
|
||||
)
|
||||
.size(72.dp)
|
||||
.clip(RoundedCornerShape(8.dp))
|
||||
|
||||
,
|
||||
contentScale = ContentScale.Crop
|
||||
)
|
||||
Image(
|
||||
painter = painterResource(R.drawable.ic_clear_chatbox_attachment),
|
||||
contentDescription = "Clear attachment icon",
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopEnd)
|
||||
.padding(top = 6.dp)
|
||||
.noRippleClickable {
|
||||
onClearAttachmentClicked(attachment)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
is ChatView.Message.ChatBoxAttachment.File -> {
|
||||
item {
|
||||
Box {
|
||||
AttachedObject(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
top = 12.dp,
|
||||
end = 4.dp
|
||||
)
|
||||
.width(216.dp),
|
||||
title = attachment.name,
|
||||
type = stringResource(R.string.file),
|
||||
icon = ObjectIcon.File(
|
||||
mime = null,
|
||||
fileName = null
|
||||
),
|
||||
onAttachmentClicked = {
|
||||
// TODO
|
||||
}
|
||||
)
|
||||
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)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
when(mode) {
|
||||
is ChatBoxMode.Default -> {
|
||||
|
||||
}
|
||||
is ChatBoxMode.EditMessage -> {
|
||||
|
||||
}
|
||||
is ChatBoxMode.Reply -> {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(54.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "Reply to ${mode.author}",
|
||||
modifier = Modifier.padding(
|
||||
start = 12.dp,
|
||||
top = 8.dp,
|
||||
end = 44.dp
|
||||
),
|
||||
style = Caption1Medium,
|
||||
color = colorResource(R.color.text_primary),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
Text(
|
||||
text = mode.text,
|
||||
modifier = Modifier.padding(
|
||||
start = 12.dp,
|
||||
top = 28.dp,
|
||||
end = 44.dp
|
||||
),
|
||||
style = Caption1Regular,
|
||||
color = colorResource(R.color.text_primary),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
Image(
|
||||
painter = painterResource(R.drawable.ic_chat_close_chat_box_reply),
|
||||
contentDescription = "Clear reply to icon",
|
||||
modifier = Modifier
|
||||
.padding(end = 12.dp)
|
||||
.align(Alignment.CenterEnd)
|
||||
.clickable {
|
||||
onClearReplyClicked()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Row {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 4.dp, vertical = 8.dp)
|
||||
.clip(CircleShape)
|
||||
.align(Alignment.Bottom)
|
||||
.clickable {
|
||||
scope.launch {
|
||||
focus.clearFocus(force = true)
|
||||
showDropdownMenu = true
|
||||
}
|
||||
}
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_chat_box_add_attachment),
|
||||
contentDescription = "Plus button",
|
||||
modifier = Modifier
|
||||
.align(Alignment.Center)
|
||||
.padding(horizontal = 4.dp, vertical = 4.dp)
|
||||
)
|
||||
if (attachments.size < ChatConfig.MAX_ATTACHMENT_COUNT) {
|
||||
MaterialTheme(
|
||||
shapes = MaterialTheme.shapes.copy(
|
||||
medium = RoundedCornerShape(
|
||||
12.dp
|
||||
)
|
||||
),
|
||||
colors = MaterialTheme.colors.copy(
|
||||
surface = colorResource(id = R.color.background_secondary)
|
||||
)
|
||||
) {
|
||||
DropdownMenu(
|
||||
offset = DpOffset(8.dp, 40.dp),
|
||||
expanded = showDropdownMenu,
|
||||
onDismissRequest = {
|
||||
showDropdownMenu = false
|
||||
},
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomEnd)
|
||||
.defaultMinSize(
|
||||
minWidth = 252.dp
|
||||
)
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
text = {
|
||||
Text(
|
||||
text = stringResource(R.string.chat_attachment_object),
|
||||
color = colorResource(id = R.color.text_primary)
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
showDropdownMenu = false
|
||||
onAttachObjectClicked()
|
||||
}
|
||||
)
|
||||
Divider(
|
||||
paddingStart = 0.dp,
|
||||
paddingEnd = 0.dp
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = {
|
||||
Text(
|
||||
text = stringResource(R.string.chat_attachment_media),
|
||||
color = colorResource(id = R.color.text_primary)
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
showDropdownMenu = false
|
||||
uploadMediaLauncher.launch(
|
||||
PickVisualMediaRequest(mediaType = ActivityResultContracts.PickVisualMedia.ImageOnly)
|
||||
)
|
||||
}
|
||||
)
|
||||
Divider(
|
||||
paddingStart = 0.dp,
|
||||
paddingEnd = 0.dp
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = {
|
||||
Text(
|
||||
text = stringResource(R.string.chat_attachment_file),
|
||||
color = colorResource(id = R.color.text_primary)
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
showDropdownMenu = false
|
||||
uploadFileLauncher.launch(
|
||||
arrayOf("*/*")
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ChatBoxUserInput(
|
||||
textState = textState,
|
||||
onMessageSent = {
|
||||
onMessageSent(it)
|
||||
clearText()
|
||||
resetScroll()
|
||||
},
|
||||
onTextChanged = { value ->
|
||||
updateValue(value)
|
||||
},
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.align(Alignment.Bottom)
|
||||
.focusRequester(chatBoxFocusRequester)
|
||||
)
|
||||
AnimatedVisibility(
|
||||
visible = attachments.isNotEmpty() || textState.text.isNotEmpty(),
|
||||
exit = fadeOut() + scaleOut(),
|
||||
enter = fadeIn() + scaleIn(),
|
||||
modifier = Modifier.align(Alignment.Bottom)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 4.dp, vertical = 8.dp)
|
||||
.clip(CircleShape)
|
||||
.clickable {
|
||||
onMessageSent(textState.text)
|
||||
clearText()
|
||||
resetScroll()
|
||||
}
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_send_message),
|
||||
contentDescription = "Send message button",
|
||||
modifier = Modifier
|
||||
.align(Alignment.Center)
|
||||
.padding(horizontal = 4.dp, vertical = 4.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ChatBoxUserInput(
|
||||
modifier: Modifier,
|
||||
textState: TextFieldValue,
|
||||
onMessageSent: (String) -> Unit,
|
||||
onTextChanged: (TextFieldValue) -> Unit,
|
||||
) {
|
||||
BasicTextField(
|
||||
value = textState,
|
||||
onValueChange = { onTextChanged(it) },
|
||||
textStyle = BodyRegular.copy(
|
||||
color = colorResource(id = R.color.text_primary)
|
||||
),
|
||||
keyboardOptions = KeyboardOptions(
|
||||
imeAction = ImeAction.Send
|
||||
),
|
||||
keyboardActions = KeyboardActions {
|
||||
if (textState.text.isNotBlank()) {
|
||||
onMessageSent(textState.text)
|
||||
}
|
||||
},
|
||||
modifier = modifier
|
||||
.padding(
|
||||
start = 4.dp,
|
||||
end = 4.dp,
|
||||
top = 16.dp,
|
||||
bottom = 16.dp
|
||||
)
|
||||
,
|
||||
cursorBrush = SolidColor(colorResource(id = R.color.palette_system_blue)),
|
||||
maxLines = 5,
|
||||
decorationBox = @Composable { innerTextField ->
|
||||
DefaultHintDecorationBox(
|
||||
text = textState.text,
|
||||
hint = stringResource(R.string.write_a_message),
|
||||
innerTextField = innerTextField,
|
||||
textStyle = BodyRegular.copy(color = colorResource(R.color.text_tertiary))
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
|
@ -0,0 +1,421 @@
|
|||
package com.anytypeio.anytype.feature_chats.ui
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.IntrinsicSize
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.DropdownMenu
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.LinkAnnotation
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.TextLinkStyles
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.text.withLink
|
||||
import androidx.compose.ui.text.withStyle
|
||||
import androidx.compose.ui.unit.DpOffset
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import coil.compose.AsyncImage
|
||||
import coil.request.ImageRequest
|
||||
import com.anytypeio.anytype.core_ui.foundation.AlertConfig
|
||||
import com.anytypeio.anytype.core_ui.foundation.BUTTON_SECONDARY
|
||||
import com.anytypeio.anytype.core_ui.foundation.BUTTON_WARNING
|
||||
import com.anytypeio.anytype.core_ui.foundation.Divider
|
||||
import com.anytypeio.anytype.core_ui.foundation.GRADIENT_TYPE_RED
|
||||
import com.anytypeio.anytype.core_ui.foundation.GenericAlert
|
||||
import com.anytypeio.anytype.core_ui.views.BodyRegular
|
||||
import com.anytypeio.anytype.core_ui.views.Caption1Medium
|
||||
import com.anytypeio.anytype.core_ui.views.Caption1Regular
|
||||
import com.anytypeio.anytype.core_ui.views.PreviewTitle2Medium
|
||||
import com.anytypeio.anytype.core_ui.views.fontIBM
|
||||
import com.anytypeio.anytype.core_utils.const.DateConst.TIME_H24
|
||||
import com.anytypeio.anytype.core_utils.ext.formatTimeInMillis
|
||||
import com.anytypeio.anytype.feature_chats.R
|
||||
import com.anytypeio.anytype.feature_chats.presentation.ChatView
|
||||
import com.bumptech.glide.integration.compose.ExperimentalGlideComposeApi
|
||||
|
||||
@OptIn(ExperimentalGlideComposeApi::class, ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun Bubble(
|
||||
modifier: Modifier = Modifier,
|
||||
name: String,
|
||||
reply: ChatView.Message.Reply? = null,
|
||||
content: ChatView.Message.Content,
|
||||
timestamp: Long,
|
||||
attachments: List<ChatView.Message.Attachment> = emptyList(),
|
||||
isUserAuthor: Boolean = false,
|
||||
isEdited: Boolean = false,
|
||||
reactions: List<ChatView.Message.Reaction> = emptyList(),
|
||||
onReacted: (String) -> Unit,
|
||||
onDeleteMessage: () -> Unit,
|
||||
onCopyMessage: () -> Unit,
|
||||
onEditMessage: () -> Unit,
|
||||
onReply: () -> Unit,
|
||||
onAttachmentClicked: (ChatView.Message.Attachment) -> Unit,
|
||||
onMarkupLinkClicked: (String) -> Unit,
|
||||
onScrollToReplyClicked: (ChatView.Message.Reply) -> Unit,
|
||||
onAddReactionClicked: () -> Unit,
|
||||
onViewChatReaction: (String) -> Unit
|
||||
) {
|
||||
var showDropdownMenu by remember { mutableStateOf(false) }
|
||||
var showDeleteMessageWarning by remember { mutableStateOf(false) }
|
||||
if (showDeleteMessageWarning) {
|
||||
ModalBottomSheet(
|
||||
onDismissRequest = {
|
||||
showDeleteMessageWarning = false
|
||||
},
|
||||
containerColor = colorResource(id = R.color.background_secondary),
|
||||
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp),
|
||||
dragHandle = null
|
||||
) {
|
||||
GenericAlert(
|
||||
config = AlertConfig.WithTwoButtons(
|
||||
title = stringResource(R.string.chats_alert_delete_this_message),
|
||||
description = stringResource(R.string.chats_alert_delete_this_message_description),
|
||||
firstButtonText = stringResource(R.string.cancel),
|
||||
secondButtonText = stringResource(R.string.delete),
|
||||
secondButtonType = BUTTON_WARNING,
|
||||
firstButtonType = BUTTON_SECONDARY,
|
||||
icon = AlertConfig.Icon(
|
||||
gradient = GRADIENT_TYPE_RED,
|
||||
icon = R.drawable.ic_alert_question_warning
|
||||
)
|
||||
),
|
||||
onFirstButtonClicked = {
|
||||
showDeleteMessageWarning = false
|
||||
},
|
||||
onSecondButtonClicked = {
|
||||
onDeleteMessage()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
Column(
|
||||
modifier = modifier
|
||||
.width(IntrinsicSize.Max)
|
||||
.background(
|
||||
color = if (isUserAuthor)
|
||||
colorResource(R.color.background_primary)
|
||||
else
|
||||
colorResource(R.color.shape_transparent_secondary),
|
||||
shape = RoundedCornerShape(20.dp)
|
||||
)
|
||||
.clip(RoundedCornerShape(20.dp))
|
||||
.clickable {
|
||||
showDropdownMenu = !showDropdownMenu
|
||||
}
|
||||
) {
|
||||
if (reply != null) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(4.dp)
|
||||
.fillMaxWidth()
|
||||
.height(52.dp)
|
||||
.background(
|
||||
color = colorResource(R.color.shape_transparent_secondary),
|
||||
shape = RoundedCornerShape(16.dp)
|
||||
)
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.clickable {
|
||||
onScrollToReplyClicked(reply)
|
||||
}
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.width(4.dp)
|
||||
.fillMaxHeight()
|
||||
.background(
|
||||
color = colorResource(R.color.shape_transparent_primary)
|
||||
)
|
||||
)
|
||||
Text(
|
||||
text = reply.author,
|
||||
modifier = Modifier.padding(
|
||||
start = 12.dp,
|
||||
top = 8.dp,
|
||||
end = 12.dp
|
||||
),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
style = Caption1Medium
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.padding(
|
||||
start = 12.dp,
|
||||
top = 26.dp,
|
||||
end = 12.dp
|
||||
),
|
||||
text = reply.text,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
style = Caption1Regular
|
||||
)
|
||||
}
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
start = 12.dp,
|
||||
end = 12.dp,
|
||||
top = if (reply == null) 12.dp else 0.dp
|
||||
),
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Text(
|
||||
text = name,
|
||||
style = PreviewTitle2Medium,
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
maxLines = 1
|
||||
)
|
||||
Spacer(Modifier.width(12.dp))
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 1.dp),
|
||||
text = timestamp.formatTimeInMillis(
|
||||
TIME_H24
|
||||
),
|
||||
style = Caption1Regular,
|
||||
color = colorResource(id = R.color.text_secondary),
|
||||
maxLines = 1
|
||||
)
|
||||
}
|
||||
if (content.msg.isNotEmpty()) {
|
||||
Text(
|
||||
modifier = Modifier.padding(
|
||||
top = 0.dp,
|
||||
start = 12.dp,
|
||||
end = 12.dp,
|
||||
bottom = if (reactions.isEmpty() && attachments.isEmpty()) 12.dp else 0.dp
|
||||
),
|
||||
text = buildAnnotatedString {
|
||||
content.parts.forEach { part ->
|
||||
if (part.link != null && part.link.param != null) {
|
||||
withLink(
|
||||
LinkAnnotation.Clickable(
|
||||
tag = "link",
|
||||
styles = TextLinkStyles(
|
||||
style = SpanStyle(
|
||||
fontWeight = if (part.isBold) FontWeight.Bold else null,
|
||||
fontStyle = if (part.isItalic) FontStyle.Italic else null,
|
||||
textDecoration = TextDecoration.Underline
|
||||
)
|
||||
)
|
||||
) {
|
||||
onMarkupLinkClicked(part.link.param.orEmpty())
|
||||
}
|
||||
) {
|
||||
append(part.part)
|
||||
}
|
||||
} else {
|
||||
withStyle(
|
||||
style = SpanStyle(
|
||||
fontWeight = if (part.isBold) FontWeight.Bold else null,
|
||||
fontStyle = if (part.isItalic) FontStyle.Italic else null,
|
||||
textDecoration = if (part.underline)
|
||||
TextDecoration.Underline
|
||||
else if (part.isStrike)
|
||||
TextDecoration.LineThrough
|
||||
else null,
|
||||
fontFamily = if (part.isCode) fontIBM else null,
|
||||
)
|
||||
) {
|
||||
append(part.part)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isEdited) {
|
||||
withStyle(
|
||||
style = SpanStyle(color = colorResource(id = R.color.text_tertiary))
|
||||
) {
|
||||
append(
|
||||
" (${stringResource(R.string.chats_message_edited)})"
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
style = BodyRegular,
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
)
|
||||
}
|
||||
BubbleAttachments(
|
||||
attachments = attachments,
|
||||
isUserAuthor = isUserAuthor,
|
||||
onAttachmentClicked = onAttachmentClicked
|
||||
)
|
||||
if (reactions.isNotEmpty()) {
|
||||
ReactionList(
|
||||
reactions = reactions,
|
||||
onReacted = onReacted,
|
||||
onViewReaction = onViewChatReaction
|
||||
)
|
||||
}
|
||||
MaterialTheme(
|
||||
shapes = MaterialTheme.shapes.copy(
|
||||
medium = RoundedCornerShape(
|
||||
16.dp
|
||||
)
|
||||
),
|
||||
colors = MaterialTheme.colors.copy(
|
||||
surface = colorResource(id = R.color.background_secondary)
|
||||
)
|
||||
) {
|
||||
DropdownMenu(
|
||||
offset = DpOffset(0.dp, 8.dp),
|
||||
expanded = showDropdownMenu,
|
||||
onDismissRequest = {
|
||||
showDropdownMenu = false
|
||||
}
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
text = {
|
||||
Text(
|
||||
text = stringResource(R.string.chats_add_reaction),
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
modifier = Modifier.padding(end = 64.dp)
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
onAddReactionClicked()
|
||||
showDropdownMenu = false
|
||||
}
|
||||
)
|
||||
Divider(paddingStart = 0.dp, paddingEnd = 0.dp)
|
||||
DropdownMenuItem(
|
||||
text = {
|
||||
Text(
|
||||
text = stringResource(R.string.chats_reply),
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
modifier = Modifier.padding(end = 64.dp)
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
onReply()
|
||||
showDropdownMenu = false
|
||||
}
|
||||
)
|
||||
if (content.msg.isNotEmpty()) {
|
||||
Divider(paddingStart = 0.dp, paddingEnd = 0.dp)
|
||||
DropdownMenuItem(
|
||||
text = {
|
||||
Text(
|
||||
text = stringResource(R.string.copy),
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
modifier = Modifier.padding(end = 64.dp)
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
onCopyMessage()
|
||||
showDropdownMenu = false
|
||||
}
|
||||
)
|
||||
}
|
||||
if (isUserAuthor) {
|
||||
Divider(paddingStart = 0.dp, paddingEnd = 0.dp)
|
||||
DropdownMenuItem(
|
||||
text = {
|
||||
Text(
|
||||
text = stringResource(R.string.edit),
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
modifier = Modifier.padding(end = 64.dp)
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
onEditMessage()
|
||||
showDropdownMenu = false
|
||||
}
|
||||
)
|
||||
}
|
||||
if (isUserAuthor) {
|
||||
Divider(paddingStart = 0.dp, paddingEnd = 0.dp)
|
||||
DropdownMenuItem(
|
||||
text = {
|
||||
Text(
|
||||
text = stringResource(id = R.string.delete),
|
||||
color = colorResource(id = R.color.palette_system_red),
|
||||
modifier = Modifier.padding(end = 64.dp)
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
showDeleteMessageWarning = true
|
||||
showDropdownMenu = false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ChatUserAvatar(
|
||||
msg: ChatView.Message,
|
||||
avatar: ChatView.Message.Avatar,
|
||||
modifier: Modifier
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.size(32.dp)
|
||||
.background(
|
||||
color = colorResource(id = R.color.text_tertiary),
|
||||
shape = CircleShape
|
||||
)
|
||||
) {
|
||||
Text(
|
||||
text = msg.author.take(1).uppercase().ifEmpty { stringResource(id = R.string.u) },
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
style = TextStyle(
|
||||
fontSize = 20.sp,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
color = colorResource(id = R.color.text_white)
|
||||
)
|
||||
)
|
||||
if (avatar is ChatView.Message.Avatar.Image) {
|
||||
AsyncImage(
|
||||
model = ImageRequest.Builder(LocalContext.current)
|
||||
.data(avatar.hash)
|
||||
.crossfade(true)
|
||||
.build(),
|
||||
contentDescription = "Space member profile icon",
|
||||
modifier = modifier
|
||||
.size(32.dp)
|
||||
.clip(CircleShape),
|
||||
contentScale = ContentScale.Crop
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,14 +1,13 @@
|
|||
package com.anytypeio.anytype.feature_discussions.ui
|
||||
package com.anytypeio.anytype.feature_chats.ui
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.anytypeio.anytype.core_models.chats.Chat
|
||||
import com.anytypeio.anytype.feature_discussions.R
|
||||
import com.anytypeio.anytype.feature_discussions.presentation.DiscussionView
|
||||
import com.anytypeio.anytype.feature_discussions.presentation.DiscussionViewModel
|
||||
import com.anytypeio.anytype.feature_chats.R
|
||||
import com.anytypeio.anytype.feature_chats.presentation.ChatView
|
||||
import com.anytypeio.anytype.feature_chats.presentation.ChatViewModel
|
||||
import kotlin.time.DurationUnit
|
||||
import kotlin.time.toDuration
|
||||
|
||||
|
@ -18,12 +17,12 @@ import kotlin.time.toDuration
|
|||
fun DiscussionPreview() {
|
||||
Messages(
|
||||
messages = listOf(
|
||||
DiscussionView.Message(
|
||||
ChatView.Message(
|
||||
id = "1",
|
||||
content = DiscussionView.Message.Content(
|
||||
content = ChatView.Message.Content(
|
||||
msg = stringResource(id = R.string.default_text_placeholder),
|
||||
parts = listOf(
|
||||
DiscussionView.Message.Content.Part(
|
||||
ChatView.Message.Content.Part(
|
||||
part = stringResource(id = R.string.default_text_placeholder)
|
||||
)
|
||||
)
|
||||
|
@ -31,12 +30,12 @@ fun DiscussionPreview() {
|
|||
author = "Walter",
|
||||
timestamp = System.currentTimeMillis()
|
||||
),
|
||||
DiscussionView.Message(
|
||||
ChatView.Message(
|
||||
id = "2",
|
||||
content = DiscussionView.Message.Content(
|
||||
content = ChatView.Message.Content(
|
||||
msg = stringResource(id = R.string.default_text_placeholder),
|
||||
parts = listOf(
|
||||
DiscussionView.Message.Content.Part(
|
||||
ChatView.Message.Content.Part(
|
||||
part = stringResource(id = R.string.default_text_placeholder)
|
||||
)
|
||||
)
|
||||
|
@ -44,12 +43,12 @@ fun DiscussionPreview() {
|
|||
author = "Leo",
|
||||
timestamp = System.currentTimeMillis()
|
||||
),
|
||||
DiscussionView.Message(
|
||||
ChatView.Message(
|
||||
id = "3",
|
||||
content = DiscussionView.Message.Content(
|
||||
content = ChatView.Message.Content(
|
||||
msg = stringResource(id = R.string.default_text_placeholder),
|
||||
parts = listOf(
|
||||
DiscussionView.Message.Content.Part(
|
||||
ChatView.Message.Content.Part(
|
||||
part = stringResource(id = R.string.default_text_placeholder)
|
||||
)
|
||||
)
|
||||
|
@ -78,17 +77,17 @@ fun DiscussionPreview() {
|
|||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO, name = "Dark Mode")
|
||||
@Composable
|
||||
fun DiscussionScreenPreview() {
|
||||
DiscussionScreen(
|
||||
ChatScreen(
|
||||
title = "Conversations with friends",
|
||||
messages = buildList {
|
||||
repeat(30) { idx ->
|
||||
add(
|
||||
DiscussionView.Message(
|
||||
ChatView.Message(
|
||||
id = idx.toString(),
|
||||
content = DiscussionView.Message.Content(
|
||||
content = ChatView.Message.Content(
|
||||
msg = stringResource(id = R.string.default_text_placeholder),
|
||||
parts = listOf(
|
||||
DiscussionView.Message.Content.Part(
|
||||
ChatView.Message.Content.Part(
|
||||
part = stringResource(id = R.string.default_text_placeholder)
|
||||
)
|
||||
)
|
||||
|
@ -122,7 +121,7 @@ fun DiscussionScreenPreview() {
|
|||
onAttachMediaClicked = {},
|
||||
onAttachObjectClicked = {},
|
||||
onReplyMessage = {},
|
||||
chatBoxMode = DiscussionViewModel.ChatBoxMode.Default,
|
||||
chatBoxMode = ChatViewModel.ChatBoxMode.Default,
|
||||
onClearReplyClicked = {},
|
||||
onChatBoxMediaPicked = {},
|
||||
onChatBoxFilePicked = {},
|
||||
|
@ -137,10 +136,10 @@ fun DiscussionScreenPreview() {
|
|||
fun BubblePreview() {
|
||||
Bubble(
|
||||
name = "Leo Marx",
|
||||
content = DiscussionView.Message.Content(
|
||||
content = ChatView.Message.Content(
|
||||
msg = stringResource(id = R.string.default_text_placeholder),
|
||||
parts = listOf(
|
||||
DiscussionView.Message.Content.Part(
|
||||
ChatView.Message.Content.Part(
|
||||
part = stringResource(id = R.string.default_text_placeholder)
|
||||
)
|
||||
)
|
||||
|
@ -165,10 +164,10 @@ fun BubblePreview() {
|
|||
fun BubbleEditedPreview() {
|
||||
Bubble(
|
||||
name = "Leo Marx",
|
||||
content = DiscussionView.Message.Content(
|
||||
content = ChatView.Message.Content(
|
||||
msg = stringResource(id = R.string.default_text_placeholder),
|
||||
parts = listOf(
|
||||
DiscussionView.Message.Content.Part(
|
||||
ChatView.Message.Content.Part(
|
||||
part = stringResource(id = R.string.default_text_placeholder)
|
||||
)
|
||||
)
|
||||
|
@ -194,10 +193,10 @@ fun BubbleEditedPreview() {
|
|||
fun BubbleWithAttachmentPreview() {
|
||||
Bubble(
|
||||
name = "Leo Marx",
|
||||
content = DiscussionView.Message.Content(
|
||||
content = ChatView.Message.Content(
|
||||
msg = stringResource(id = R.string.default_text_placeholder),
|
||||
parts = listOf(
|
||||
DiscussionView.Message.Content.Part(
|
||||
ChatView.Message.Content.Part(
|
||||
part = stringResource(id = R.string.default_text_placeholder)
|
||||
)
|
||||
)
|
||||
|
@ -208,7 +207,7 @@ fun BubbleWithAttachmentPreview() {
|
|||
onCopyMessage = {},
|
||||
attachments = buildList {
|
||||
add(
|
||||
DiscussionView.Message.Attachment.Link(
|
||||
ChatView.Message.Attachment.Link(
|
||||
target = "ID",
|
||||
wrapper = null,
|
||||
typeName = "Page"
|
|
@ -1,4 +1,4 @@
|
|||
package com.anytypeio.anytype.feature_discussions.ui
|
||||
package com.anytypeio.anytype.feature_chats.ui
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
|
@ -22,13 +22,12 @@ import androidx.compose.ui.res.stringResource
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.anytypeio.anytype.core_ui.common.DefaultPreviews
|
||||
import com.anytypeio.anytype.core_ui.foundation.Divider
|
||||
import com.anytypeio.anytype.core_ui.foundation.Dragger
|
||||
import com.anytypeio.anytype.core_ui.foundation.noRippleClickable
|
||||
import com.anytypeio.anytype.core_ui.views.Caption1Medium
|
||||
import com.anytypeio.anytype.emojifier.data.Emoji
|
||||
import com.anytypeio.anytype.feature_discussions.R
|
||||
import com.anytypeio.anytype.feature_discussions.presentation.SelectChatReactionViewModel.ReactionPickerView
|
||||
import com.anytypeio.anytype.feature_chats.R
|
||||
import com.anytypeio.anytype.feature_chats.presentation.SelectChatReactionViewModel.ReactionPickerView
|
||||
import com.bumptech.glide.integration.compose.ExperimentalGlideComposeApi
|
||||
import com.bumptech.glide.integration.compose.GlideImage
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
package com.anytypeio.anytype.feature_discussions.ui
|
||||
package com.anytypeio.anytype.feature_chats.ui
|
||||
|
||||
import com.anytypeio.anytype.feature_discussions.R
|
||||
import com.anytypeio.anytype.feature_chats.R
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
|
@ -25,7 +25,7 @@ import com.anytypeio.anytype.core_ui.foundation.Dragger
|
|||
import com.anytypeio.anytype.core_ui.views.BodyCallout
|
||||
import com.anytypeio.anytype.core_ui.views.BodyRegular
|
||||
import com.anytypeio.anytype.core_ui.views.Relations3
|
||||
import com.anytypeio.anytype.feature_discussions.presentation.ChatReactionViewModel.ViewState
|
||||
import com.anytypeio.anytype.feature_chats.presentation.ChatReactionViewModel.ViewState
|
||||
import com.anytypeio.anytype.presentation.objects.SpaceMemberIconView
|
||||
|
||||
@Composable
|
|
@ -0,0 +1,617 @@
|
|||
package com.anytypeio.anytype.feature_chats.ui
|
||||
|
||||
import android.content.res.Configuration
|
||||
import android.net.Uri
|
||||
import android.provider.OpenableColumns
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.imePadding
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalClipboardManager
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.TextRange
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_ui.foundation.AlertConfig
|
||||
import com.anytypeio.anytype.core_ui.foundation.AlertIcon
|
||||
import com.anytypeio.anytype.core_ui.foundation.GRADIENT_TYPE_BLUE
|
||||
import com.anytypeio.anytype.core_ui.views.Caption1Medium
|
||||
import com.anytypeio.anytype.core_ui.views.Caption1Regular
|
||||
import com.anytypeio.anytype.core_ui.views.PreviewTitle2Regular
|
||||
import com.anytypeio.anytype.core_ui.views.Relations2
|
||||
import com.anytypeio.anytype.core_utils.common.DefaultFileInfo
|
||||
import com.anytypeio.anytype.core_utils.ext.parseImagePath
|
||||
import com.anytypeio.anytype.feature_chats.R
|
||||
import com.anytypeio.anytype.feature_chats.presentation.ChatView
|
||||
import com.anytypeio.anytype.feature_chats.presentation.ChatViewModel
|
||||
import com.anytypeio.anytype.feature_chats.presentation.ChatViewModel.ChatBoxMode
|
||||
import com.anytypeio.anytype.feature_chats.presentation.ChatViewModel.UXCommand
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun ChatScreenWrapper(
|
||||
isSpaceLevelChat: Boolean = false,
|
||||
vm: ChatViewModel,
|
||||
// TODO move to view model
|
||||
onAttachObjectClicked: () -> Unit,
|
||||
onBackButtonClicked: () -> Unit,
|
||||
onMarkupLinkClicked: (String) -> Unit,
|
||||
onRequestOpenFullScreenImage: (String) -> Unit,
|
||||
onSelectChatReaction: (String) -> Unit,
|
||||
onViewChatReaction: (Id, String) -> Unit
|
||||
) {
|
||||
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = false)
|
||||
var showReactionSheet by remember { mutableStateOf(false) }
|
||||
val context = LocalContext.current
|
||||
NavHost(
|
||||
navController = rememberNavController(),
|
||||
startDestination = "discussions"
|
||||
) {
|
||||
composable(
|
||||
route = "discussions"
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.then(
|
||||
if (!isSpaceLevelChat) {
|
||||
Modifier.background(
|
||||
color = colorResource(id = R.color.background_primary)
|
||||
)
|
||||
} else {
|
||||
Modifier
|
||||
}
|
||||
)
|
||||
) {
|
||||
val clipboard = LocalClipboardManager.current
|
||||
val lazyListState = rememberLazyListState()
|
||||
|
||||
ChatScreen(
|
||||
chatBoxMode = vm.chatBoxMode.collectAsState().value,
|
||||
isSpaceLevelChat = isSpaceLevelChat,
|
||||
title = vm.name.collectAsState().value,
|
||||
messages = vm.messages.collectAsState().value,
|
||||
attachments = vm.chatBoxAttachments.collectAsState().value,
|
||||
onMessageSent = vm::onMessageSent,
|
||||
onTitleChanged = vm::onTitleChanged,
|
||||
onAttachClicked = onAttachObjectClicked,
|
||||
onClearAttachmentClicked = vm::onClearAttachmentClicked,
|
||||
lazyListState = lazyListState,
|
||||
onReacted = vm::onReacted,
|
||||
onCopyMessage = { msg ->
|
||||
clipboard.setText(AnnotatedString(text = msg.content.msg))
|
||||
},
|
||||
onDeleteMessage = vm::onDeleteMessage,
|
||||
onEditMessage = vm::onRequestEditMessageClicked,
|
||||
onAttachmentClicked = vm::onAttachmentClicked,
|
||||
isInEditMessageMode = vm.chatBoxMode.collectAsState().value is ChatBoxMode.EditMessage,
|
||||
onExitEditMessageMode = vm::onExitEditMessageMode,
|
||||
onBackButtonClicked = onBackButtonClicked,
|
||||
onMarkupLinkClicked = onMarkupLinkClicked,
|
||||
onAttachObjectClicked = onAttachObjectClicked,
|
||||
onAttachMediaClicked = {
|
||||
|
||||
},
|
||||
onAttachFileClicked = {
|
||||
|
||||
},
|
||||
onUploadAttachmentClicked = {
|
||||
|
||||
},
|
||||
onReplyMessage = vm::onReplyMessage,
|
||||
onClearReplyClicked = vm::onClearReplyClicked,
|
||||
onChatBoxMediaPicked = { uris ->
|
||||
vm.onChatBoxMediaPicked(uris.map { it.parseImagePath(context = context) })
|
||||
},
|
||||
onChatBoxFilePicked = { uris ->
|
||||
val infos = uris.mapNotNull { uri ->
|
||||
val cursor = context.contentResolver.query(
|
||||
uri,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
)
|
||||
if (cursor != null) {
|
||||
val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
|
||||
val sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE)
|
||||
cursor.moveToFirst()
|
||||
DefaultFileInfo(
|
||||
uri = uri.toString(),
|
||||
name = cursor.getString(nameIndex),
|
||||
size = cursor.getLong(sizeIndex).toInt()
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
vm.onChatBoxFilePicked(infos)
|
||||
},
|
||||
onAddReactionClicked = onSelectChatReaction,
|
||||
onViewChatReaction = onViewChatReaction
|
||||
)
|
||||
LaunchedEffect(Unit) {
|
||||
vm.commands.collect { command ->
|
||||
when(command) {
|
||||
is UXCommand.JumpToBottom -> {
|
||||
lazyListState.animateScrollToItem(0)
|
||||
}
|
||||
is UXCommand.SetChatBoxInput -> {
|
||||
// TODO
|
||||
}
|
||||
is UXCommand.OpenFullScreenImage -> {
|
||||
onRequestOpenFullScreenImage(command.url)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (showReactionSheet) {
|
||||
ModalBottomSheet(
|
||||
onDismissRequest = {
|
||||
showReactionSheet = false
|
||||
},
|
||||
sheetState = sheetState,
|
||||
containerColor = colorResource(id = R.color.background_secondary),
|
||||
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp),
|
||||
dragHandle = null
|
||||
) {
|
||||
SelectChatReactionScreen(
|
||||
onEmojiClicked = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: do date formating before rendering?
|
||||
*/
|
||||
@Composable
|
||||
fun ChatScreen(
|
||||
chatBoxMode: ChatBoxMode,
|
||||
isSpaceLevelChat: Boolean,
|
||||
isInEditMessageMode: Boolean = false,
|
||||
lazyListState: LazyListState,
|
||||
title: String?,
|
||||
messages: List<ChatView>,
|
||||
attachments: List<ChatView.Message.ChatBoxAttachment>,
|
||||
onMessageSent: (String) -> Unit,
|
||||
onTitleChanged: (String) -> Unit,
|
||||
onAttachClicked: () -> Unit,
|
||||
onBackButtonClicked: () -> Unit,
|
||||
onClearAttachmentClicked: (ChatView.Message.ChatBoxAttachment) -> Unit,
|
||||
onClearReplyClicked: () -> Unit,
|
||||
onReacted: (Id, String) -> Unit,
|
||||
onDeleteMessage: (ChatView.Message) -> Unit,
|
||||
onCopyMessage: (ChatView.Message) -> Unit,
|
||||
onEditMessage: (ChatView.Message) -> Unit,
|
||||
onReplyMessage: (ChatView.Message) -> Unit,
|
||||
onAttachmentClicked: (ChatView.Message.Attachment) -> Unit,
|
||||
onExitEditMessageMode: () -> Unit,
|
||||
onMarkupLinkClicked: (String) -> Unit,
|
||||
onAttachObjectClicked: () -> Unit,
|
||||
onAttachMediaClicked: () -> Unit,
|
||||
onAttachFileClicked: () -> Unit,
|
||||
onUploadAttachmentClicked: () -> Unit,
|
||||
onChatBoxMediaPicked: (List<Uri>) -> Unit,
|
||||
onChatBoxFilePicked: (List<Uri>) -> Unit,
|
||||
onAddReactionClicked: (String) -> Unit,
|
||||
onViewChatReaction: (Id, String) -> Unit
|
||||
) {
|
||||
var textState by rememberSaveable(stateSaver = TextFieldValue.Saver) {
|
||||
mutableStateOf(TextFieldValue(""))
|
||||
}
|
||||
var isTitleFocused by remember { mutableStateOf(false) }
|
||||
val chatBoxFocusRequester = FocusRequester()
|
||||
val isHeaderVisible by remember {
|
||||
derivedStateOf {
|
||||
val layoutInfo = lazyListState.layoutInfo
|
||||
val visibleItems = layoutInfo.visibleItemsInfo
|
||||
if (visibleItems.isEmpty()) {
|
||||
false
|
||||
} else {
|
||||
visibleItems.last().key == HEADER_KEY
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
|
||||
// Scrolling to bottom when list size changes and we are at the bottom of the list
|
||||
LaunchedEffect(messages.size) {
|
||||
if (lazyListState.firstVisibleItemScrollOffset == 0) {
|
||||
scope.launch {
|
||||
lazyListState.animateScrollToItem(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
if (!isSpaceLevelChat) {
|
||||
TopDiscussionToolbar(
|
||||
title = title,
|
||||
isHeaderVisible = isHeaderVisible
|
||||
)
|
||||
}
|
||||
Box(modifier = Modifier.weight(1f)) {
|
||||
Messages(
|
||||
isSpaceLevelChat = isSpaceLevelChat,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
messages = messages,
|
||||
scrollState = lazyListState,
|
||||
onTitleChanged = onTitleChanged,
|
||||
title = title,
|
||||
onTitleFocusChanged = {
|
||||
isTitleFocused = it
|
||||
},
|
||||
onReacted = onReacted,
|
||||
onCopyMessage = onCopyMessage,
|
||||
onDeleteMessage = onDeleteMessage,
|
||||
onAttachmentClicked = onAttachmentClicked,
|
||||
onEditMessage = { msg ->
|
||||
onEditMessage(msg).also {
|
||||
textState = TextFieldValue(
|
||||
msg.content.msg,
|
||||
selection = TextRange(msg.content.msg.length)
|
||||
)
|
||||
chatBoxFocusRequester.requestFocus()
|
||||
}
|
||||
},
|
||||
onReplyMessage = {
|
||||
onReplyMessage(it)
|
||||
chatBoxFocusRequester.requestFocus()
|
||||
},
|
||||
onMarkupLinkClicked = onMarkupLinkClicked,
|
||||
onAddReactionClicked = onAddReactionClicked,
|
||||
onViewChatReaction = onViewChatReaction
|
||||
)
|
||||
// Jump to bottom button shows up when user scrolls past a threshold.
|
||||
// Convert to pixels:
|
||||
val jumpThreshold = with(LocalDensity.current) {
|
||||
JumpToBottomThreshold.toPx()
|
||||
}
|
||||
|
||||
// Show the button if the first visible item is not the first one or if the offset is
|
||||
// greater than the threshold.
|
||||
val jumpToBottomButtonEnabled by remember {
|
||||
derivedStateOf {
|
||||
lazyListState.firstVisibleItemIndex != 0 ||
|
||||
lazyListState.firstVisibleItemScrollOffset > jumpThreshold
|
||||
}
|
||||
}
|
||||
|
||||
GoToBottomButton(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomEnd)
|
||||
.padding(end = 12.dp),
|
||||
onGoToBottomClicked = {
|
||||
scope.launch {
|
||||
lazyListState.animateScrollToItem(index = 0)
|
||||
}
|
||||
},
|
||||
enabled = jumpToBottomButtonEnabled
|
||||
)
|
||||
}
|
||||
ChatBox(
|
||||
mode = chatBoxMode,
|
||||
modifier = Modifier
|
||||
.imePadding()
|
||||
.navigationBarsPadding(),
|
||||
chatBoxFocusRequester = chatBoxFocusRequester,
|
||||
textState = textState,
|
||||
onMessageSent = onMessageSent,
|
||||
onAttachClicked = onAttachClicked,
|
||||
resetScroll = {
|
||||
if (lazyListState.firstVisibleItemScrollOffset > 0) {
|
||||
scope.launch {
|
||||
lazyListState.animateScrollToItem(index = 0)
|
||||
}
|
||||
}
|
||||
},
|
||||
isTitleFocused = isTitleFocused,
|
||||
attachments = attachments,
|
||||
updateValue = {
|
||||
textState = it
|
||||
},
|
||||
clearText = {
|
||||
textState = TextFieldValue()
|
||||
},
|
||||
onBackButtonClicked = onBackButtonClicked,
|
||||
onAttachFileClicked = onAttachFileClicked,
|
||||
onAttachMediaClicked = onAttachMediaClicked,
|
||||
onUploadAttachmentClicked = onUploadAttachmentClicked,
|
||||
onAttachObjectClicked = onAttachObjectClicked,
|
||||
onClearAttachmentClicked = onClearAttachmentClicked,
|
||||
onClearReplyClicked = onClearReplyClicked,
|
||||
onChatBoxMediaPicked = onChatBoxMediaPicked,
|
||||
onChatBoxFilePicked = onChatBoxFilePicked,
|
||||
onExitEditMessageMode = {
|
||||
onExitEditMessageMode().also {
|
||||
textState = TextFieldValue()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Messages(
|
||||
isSpaceLevelChat: Boolean = true,
|
||||
title: String?,
|
||||
onTitleChanged: (String) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
messages: List<ChatView>,
|
||||
scrollState: LazyListState,
|
||||
onTitleFocusChanged: (Boolean) -> Unit,
|
||||
onReacted: (Id, String) -> Unit,
|
||||
onDeleteMessage: (ChatView.Message) -> Unit,
|
||||
onCopyMessage: (ChatView.Message) -> Unit,
|
||||
onAttachmentClicked: (ChatView.Message.Attachment) -> Unit,
|
||||
onEditMessage: (ChatView.Message) -> Unit,
|
||||
onReplyMessage: (ChatView.Message) -> Unit,
|
||||
onMarkupLinkClicked: (String) -> Unit,
|
||||
onAddReactionClicked: (String) -> Unit,
|
||||
onViewChatReaction: (Id, String) -> Unit
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
LazyColumn(
|
||||
modifier = modifier,
|
||||
reverseLayout = true,
|
||||
state = scrollState,
|
||||
) {
|
||||
itemsIndexed(
|
||||
messages,
|
||||
key = { _, msg ->
|
||||
when(msg) {
|
||||
is ChatView.DateSection -> msg.timeInMillis
|
||||
is ChatView.Message -> msg.id
|
||||
}
|
||||
}
|
||||
) { idx, msg ->
|
||||
if (msg is ChatView.Message) {
|
||||
if (idx == 0)
|
||||
Spacer(modifier = Modifier.height(36.dp))
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 12.dp, vertical = 6.dp)
|
||||
.animateItem(),
|
||||
horizontalArrangement = if (msg.isUserAuthor)
|
||||
Arrangement.End
|
||||
else
|
||||
Arrangement.Start
|
||||
) {
|
||||
if (!msg.isUserAuthor) {
|
||||
ChatUserAvatar(
|
||||
msg = msg,
|
||||
avatar = msg.avatar,
|
||||
modifier = Modifier.align(Alignment.Bottom)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
}
|
||||
Bubble(
|
||||
modifier = Modifier.padding(
|
||||
start = if (msg.isUserAuthor) 32.dp else 0.dp,
|
||||
end = if (msg.isUserAuthor) 0.dp else 32.dp
|
||||
),
|
||||
name = msg.author,
|
||||
content = msg.content,
|
||||
timestamp = msg.timestamp,
|
||||
attachments = msg.attachments,
|
||||
isUserAuthor = msg.isUserAuthor,
|
||||
isEdited = msg.isEdited,
|
||||
onReacted = { emoji ->
|
||||
onReacted(msg.id, emoji)
|
||||
},
|
||||
reactions = msg.reactions,
|
||||
onDeleteMessage = {
|
||||
onDeleteMessage(msg)
|
||||
},
|
||||
onCopyMessage = {
|
||||
onCopyMessage(msg)
|
||||
},
|
||||
onAttachmentClicked = onAttachmentClicked,
|
||||
onEditMessage = {
|
||||
onEditMessage(msg)
|
||||
},
|
||||
onMarkupLinkClicked = onMarkupLinkClicked,
|
||||
onReply = {
|
||||
onReplyMessage(msg)
|
||||
},
|
||||
reply = msg.reply,
|
||||
onScrollToReplyClicked = { reply ->
|
||||
// Naive implementation
|
||||
val idx = messages.indexOfFirst { it is ChatView.Message && it.id == reply.msg }
|
||||
if (idx != -1) {
|
||||
scope.launch {
|
||||
scrollState.animateScrollToItem(index = idx)
|
||||
}
|
||||
}
|
||||
},
|
||||
onAddReactionClicked = {
|
||||
onAddReactionClicked(msg.id)
|
||||
},
|
||||
onViewChatReaction = { emoji ->
|
||||
onViewChatReaction(msg.id, emoji)
|
||||
}
|
||||
)
|
||||
}
|
||||
if (idx == messages.lastIndex) {
|
||||
Spacer(modifier = Modifier.height(36.dp))
|
||||
}
|
||||
} else if (msg is ChatView.DateSection) {
|
||||
Text(
|
||||
text = msg.formattedDate,
|
||||
style = Caption1Medium,
|
||||
modifier = Modifier.fillMaxWidth().padding(16.dp),
|
||||
textAlign = TextAlign.Center,
|
||||
color = colorResource(R.color.transparent_active)
|
||||
)
|
||||
}
|
||||
}
|
||||
if (messages.isEmpty()) {
|
||||
item {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillParentMaxSize()
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterStart)
|
||||
) {
|
||||
AlertIcon(
|
||||
icon = AlertConfig.Icon(
|
||||
gradient = GRADIENT_TYPE_BLUE,
|
||||
icon = R.drawable.ic_alert_message
|
||||
)
|
||||
)
|
||||
Text(
|
||||
text = stringResource(R.string.chat_empty_state_message),
|
||||
style = Caption1Regular,
|
||||
color = colorResource(id = R.color.text_secondary),
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
start = 20.dp,
|
||||
end = 20.dp,
|
||||
top = 12.dp
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!isSpaceLevelChat) {
|
||||
item(key = HEADER_KEY) {
|
||||
Column {
|
||||
DiscussionTitle(
|
||||
title = title,
|
||||
onTitleChanged = onTitleChanged,
|
||||
onFocusChanged = onTitleFocusChanged
|
||||
)
|
||||
Text(
|
||||
style = Relations2,
|
||||
text = stringResource(R.string.chat),
|
||||
color = colorResource(id = R.color.text_secondary),
|
||||
modifier = Modifier.padding(
|
||||
start = 20.dp
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TopDiscussionToolbar(
|
||||
title: String? = null,
|
||||
isHeaderVisible: Boolean = false
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(48.dp)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.width(48.dp)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(10.dp)
|
||||
.align(Alignment.Center)
|
||||
.background(color = Color.Green, shape = CircleShape)
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = if (isHeaderVisible) "" else title ?: stringResource(id = R.string.untitled),
|
||||
style = PreviewTitle2Regular,
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterVertically)
|
||||
.weight(1f),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.width(48.dp)
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_toolbar_three_dots),
|
||||
contentDescription = "Three dots menu",
|
||||
modifier = Modifier.align(Alignment.Center)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, name = "Light Mode")
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO, name = "Dark Mode")
|
||||
@Composable
|
||||
fun TopDiscussionToolbarPreview() {
|
||||
TopDiscussionToolbar()
|
||||
}
|
||||
|
||||
private const val HEADER_KEY = "key.discussions.item.header"
|
||||
private val JumpToBottomThreshold = 200.dp
|
|
@ -0,0 +1,141 @@
|
|||
package com.anytypeio.anytype.feature_chats.ui
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
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.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.anytypeio.anytype.core_ui.views.BodyCalloutMedium
|
||||
import com.anytypeio.anytype.core_ui.views.Caption1Regular
|
||||
import com.anytypeio.anytype.feature_chats.R
|
||||
import com.anytypeio.anytype.feature_chats.presentation.ChatView
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class, ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun ReactionList(
|
||||
reactions: List<ChatView.Message.Reaction>,
|
||||
onReacted: (String) -> Unit,
|
||||
onViewReaction: (String) -> Unit
|
||||
) {
|
||||
FlowRow(
|
||||
modifier = Modifier.padding(start = 12.dp, end = 12.dp, bottom = 12.dp, top = 4.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
reactions.forEach { reaction ->
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(28.dp)
|
||||
.width(46.dp)
|
||||
.background(
|
||||
color = if (reaction.isSelected)
|
||||
colorResource(id = R.color.palette_very_light_orange)
|
||||
else
|
||||
colorResource(id = R.color.shape_transparent_primary),
|
||||
shape = RoundedCornerShape(100.dp)
|
||||
)
|
||||
.clip(RoundedCornerShape(100.dp))
|
||||
.then(
|
||||
if (reaction.isSelected)
|
||||
Modifier.border(
|
||||
width = 1.dp,
|
||||
color = colorResource(id = R.color.palette_system_amber_50),
|
||||
shape = RoundedCornerShape(100.dp)
|
||||
)
|
||||
else
|
||||
Modifier
|
||||
)
|
||||
.combinedClickable(
|
||||
onClick = {
|
||||
onReacted(reaction.emoji)
|
||||
},
|
||||
onLongClick = {
|
||||
onViewReaction(reaction.emoji)
|
||||
}
|
||||
)
|
||||
) {
|
||||
Text(
|
||||
text = reaction.emoji,
|
||||
style = BodyCalloutMedium,
|
||||
modifier = Modifier
|
||||
.align(
|
||||
alignment = Alignment.CenterStart
|
||||
)
|
||||
.padding(
|
||||
start = 8.dp
|
||||
)
|
||||
)
|
||||
Text(
|
||||
text = reaction.count.toString(),
|
||||
style = Caption1Regular,
|
||||
modifier = Modifier
|
||||
.align(
|
||||
alignment = Alignment.CenterEnd
|
||||
)
|
||||
.padding(
|
||||
end = 8.dp
|
||||
),
|
||||
color = colorResource(id = R.color.text_primary)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, name = "Light Mode")
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO, name = "Dark Mode")
|
||||
@Composable
|
||||
fun ReactionListPreview() {
|
||||
ReactionList(
|
||||
reactions = listOf(
|
||||
ChatView.Message.Reaction(
|
||||
emoji = "❤\uFE0F",
|
||||
count = 1,
|
||||
isSelected = false
|
||||
),
|
||||
ChatView.Message.Reaction(
|
||||
emoji = "❤\uFE0F",
|
||||
count = 1,
|
||||
isSelected = true
|
||||
),
|
||||
ChatView.Message.Reaction(
|
||||
emoji = "❤\uFE0F",
|
||||
count = 1,
|
||||
isSelected = false
|
||||
),
|
||||
ChatView.Message.Reaction(
|
||||
emoji = "❤\uFE0F",
|
||||
count = 1,
|
||||
isSelected = false
|
||||
),
|
||||
ChatView.Message.Reaction(
|
||||
emoji = "❤\uFE0F",
|
||||
count = 1,
|
||||
isSelected = false
|
||||
),
|
||||
ChatView.Message.Reaction(
|
||||
emoji = "❤\uFE0F",
|
||||
count = 1,
|
||||
isSelected = false
|
||||
)
|
||||
),
|
||||
onReacted = {},
|
||||
onViewReaction = {}
|
||||
)
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
package com.anytypeio.anytype.feature_chats.ui
|
||||
|
||||
import androidx.compose.animation.core.animateDp
|
||||
import androidx.compose.animation.core.updateTransition
|
||||
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.offset
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
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.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.anytypeio.anytype.core_ui.foundation.noRippleClickable
|
||||
import com.anytypeio.anytype.core_ui.views.Caption1Medium
|
||||
import com.anytypeio.anytype.feature_chats.R
|
||||
|
||||
@Composable
|
||||
fun EditMessageToolbar(
|
||||
onExitClicked: () -> Unit
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(40.dp)
|
||||
.fillMaxWidth()
|
||||
.background(
|
||||
color = colorResource(id = R.color.background_highlighted_light),
|
||||
shape = RoundedCornerShape(
|
||||
topStart = 16.dp,
|
||||
topEnd = 16.dp
|
||||
)
|
||||
)
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
start = 12.dp
|
||||
)
|
||||
.align(
|
||||
Alignment.CenterStart
|
||||
),
|
||||
text = stringResource(R.string.chats_edit_message),
|
||||
style = Caption1Medium,
|
||||
color = colorResource(id = R.color.text_primary)
|
||||
)
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
end = 12.dp
|
||||
)
|
||||
.align(
|
||||
Alignment.CenterEnd
|
||||
)
|
||||
.noRippleClickable {
|
||||
onExitClicked()
|
||||
}
|
||||
,
|
||||
painter = painterResource(id = R.drawable.ic_edit_message_close),
|
||||
contentDescription = "Close edit-message mode"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun GoToBottomButton(
|
||||
enabled: Boolean,
|
||||
modifier: Modifier,
|
||||
onGoToBottomClicked: () -> Unit
|
||||
) {
|
||||
val transition = updateTransition(
|
||||
enabled,
|
||||
label = "JumpToBottom visibility animation"
|
||||
)
|
||||
val bottomOffset by transition.animateDp(label = "JumpToBottom offset animation") {
|
||||
if (it) {
|
||||
(12).dp
|
||||
} else {
|
||||
(-12).dp
|
||||
}
|
||||
}
|
||||
if (bottomOffset > 0.dp) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.offset(x = 0.dp, y = -bottomOffset)
|
||||
.size(48.dp)
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
.background(color = colorResource(id = R.color.navigation_panel))
|
||||
.clickable {
|
||||
onGoToBottomClicked()
|
||||
}
|
||||
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_go_to_bottom_arrow),
|
||||
contentDescription = "Arrow icon",
|
||||
modifier = Modifier.align(Alignment.Center)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
package com.anytypeio.anytype.feature_chats.ui
|
||||
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.OutlinedTextFieldDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.onFocusChanged
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.VisualTransformation
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.anytypeio.anytype.core_ui.views.HeadlineTitle
|
||||
import com.anytypeio.anytype.feature_chats.R
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
|
||||
fun DefaultHintDecorationBox(
|
||||
text: String,
|
||||
hint: String,
|
||||
innerTextField: @Composable () -> Unit,
|
||||
textStyle: TextStyle
|
||||
) {
|
||||
OutlinedTextFieldDefaults.DecorationBox(
|
||||
value = text,
|
||||
visualTransformation = VisualTransformation.None,
|
||||
innerTextField = innerTextField,
|
||||
singleLine = true,
|
||||
enabled = true,
|
||||
placeholder = {
|
||||
Text(
|
||||
text = hint,
|
||||
color = colorResource(id = R.color.text_tertiary),
|
||||
style = textStyle
|
||||
)
|
||||
},
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
colors = OutlinedTextFieldDefaults.colors(
|
||||
disabledBorderColor = Color.Transparent,
|
||||
errorBorderColor = Color.Transparent,
|
||||
focusedBorderColor = Color.Transparent,
|
||||
unfocusedBorderColor = Color.Transparent
|
||||
),
|
||||
contentPadding = PaddingValues()
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DiscussionTitle(
|
||||
title: String?,
|
||||
onTitleChanged: (String) -> Unit = {},
|
||||
onFocusChanged: (Boolean) -> Unit = {}
|
||||
) {
|
||||
var lastFocusState by remember { mutableStateOf(false) }
|
||||
BasicTextField(
|
||||
textStyle = HeadlineTitle.copy(
|
||||
color = colorResource(id = R.color.text_primary)
|
||||
),
|
||||
value = title.orEmpty(),
|
||||
onValueChange = {
|
||||
onTitleChanged(it)
|
||||
},
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
top = 24.dp,
|
||||
start = 20.dp,
|
||||
end = 20.dp,
|
||||
bottom = 8.dp
|
||||
)
|
||||
.onFocusChanged { state ->
|
||||
if (lastFocusState != state.isFocused) {
|
||||
onFocusChanged(state.isFocused)
|
||||
}
|
||||
lastFocusState = state.isFocused
|
||||
}
|
||||
,
|
||||
keyboardOptions = KeyboardOptions(
|
||||
imeAction = ImeAction.Done
|
||||
),
|
||||
decorationBox = @Composable { innerTextField ->
|
||||
DefaultHintDecorationBox(
|
||||
hint = stringResource(id = R.string.untitled),
|
||||
text = title.orEmpty(),
|
||||
innerTextField = innerTextField,
|
||||
textStyle = HeadlineTitle
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
|
@ -62,7 +62,6 @@ import kotlinx.coroutines.flow.debounce
|
|||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.drop
|
||||
import kotlinx.coroutines.flow.emitAll
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
|
@ -646,7 +645,7 @@ class DateObjectViewModel(
|
|||
effects.emit(DateObjectCommand.SendToast.UnexpectedLayout(navigation.layout?.name.orEmpty()))
|
||||
}
|
||||
|
||||
is OpenObjectNavigation.OpenDiscussion -> {
|
||||
is OpenObjectNavigation.OpenChat -> {
|
||||
effects.emit(
|
||||
DateObjectCommand.OpenChat(
|
||||
target = navigation.target,
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -4456,7 +4456,7 @@ class EditorViewModel(
|
|||
)
|
||||
)
|
||||
}
|
||||
is OpenObjectNavigation.OpenDiscussion -> {
|
||||
is OpenObjectNavigation.OpenChat -> {
|
||||
sendToast("not implemented")
|
||||
}
|
||||
is OpenObjectNavigation.UnexpectedLayoutError -> {
|
||||
|
|
|
@ -1097,7 +1097,7 @@ class HomeScreenViewModel(
|
|||
val space = spaceView.space.targetSpaceId
|
||||
if (chat != null && space != null) {
|
||||
navigation(
|
||||
Navigation.OpenDiscussion(
|
||||
Navigation.OpenChat(
|
||||
space = space,
|
||||
ctx = chat
|
||||
)
|
||||
|
@ -1422,9 +1422,9 @@ class HomeScreenViewModel(
|
|||
)
|
||||
)
|
||||
}
|
||||
is OpenObjectNavigation.OpenDiscussion -> {
|
||||
is OpenObjectNavigation.OpenChat -> {
|
||||
navigate(
|
||||
Navigation.OpenDiscussion(
|
||||
Navigation.OpenChat(
|
||||
ctx = navigation.target,
|
||||
space = navigation.space
|
||||
)
|
||||
|
@ -2168,7 +2168,7 @@ class HomeScreenViewModel(
|
|||
|
||||
sealed class Navigation {
|
||||
data class OpenObject(val ctx: Id, val space: Id) : Navigation()
|
||||
data class OpenDiscussion(val ctx: Id, val space: Id) : Navigation()
|
||||
data class OpenChat(val ctx: Id, val space: Id) : Navigation()
|
||||
data class OpenSet(val ctx: Id, val space: Id, val view: Id?) : Navigation()
|
||||
data class ExpandWidget(val subscription: Subscription, val space: Id) : Navigation()
|
||||
data object OpenSpaceSwitcher: Navigation()
|
||||
|
@ -2403,7 +2403,7 @@ sealed class OpenObjectNavigation {
|
|||
data class OpenDataView(val target: Id, val space: Id): OpenObjectNavigation()
|
||||
data class UnexpectedLayoutError(val layout: ObjectType.Layout?): OpenObjectNavigation()
|
||||
data object NonValidObject: OpenObjectNavigation()
|
||||
data class OpenDiscussion(val target: Id, val space: Id): OpenObjectNavigation()
|
||||
data class OpenChat(val target: Id, val space: Id): OpenObjectNavigation()
|
||||
data class OpenDataObject(val target: Id, val space: Id): OpenObjectNavigation()
|
||||
}
|
||||
|
||||
|
@ -2448,7 +2448,7 @@ fun ObjectWrapper.Basic.navigation() : OpenObjectNavigation {
|
|||
)
|
||||
}
|
||||
ObjectType.Layout.CHAT_DERIVED -> {
|
||||
OpenObjectNavigation.OpenDiscussion(
|
||||
OpenObjectNavigation.OpenChat(
|
||||
target = id,
|
||||
space = requireNotNull(spaceId)
|
||||
)
|
||||
|
@ -2500,7 +2500,7 @@ fun ObjectType.Layout.navigation(
|
|||
)
|
||||
}
|
||||
ObjectType.Layout.CHAT_DERIVED -> {
|
||||
OpenObjectNavigation.OpenDiscussion(
|
||||
OpenObjectNavigation.OpenChat(
|
||||
target = target,
|
||||
space = space
|
||||
)
|
||||
|
|
|
@ -47,7 +47,7 @@ fun ObjectType.Layout?.emptyType(): ObjectIcon.Empty {
|
|||
ObjectType.Layout.SET, ObjectType.Layout.COLLECTION -> ObjectIcon.Empty.List
|
||||
ObjectType.Layout.OBJECT_TYPE -> ObjectIcon.Empty.ObjectType
|
||||
ObjectType.Layout.BOOKMARK -> ObjectIcon.Empty.Bookmark
|
||||
ObjectType.Layout.CHAT, ObjectType.Layout.CHAT_DERIVED -> ObjectIcon.Empty.Discussion
|
||||
ObjectType.Layout.CHAT, ObjectType.Layout.CHAT_DERIVED -> ObjectIcon.Empty.Chat
|
||||
ObjectType.Layout.DATE -> ObjectIcon.Empty.Date
|
||||
else -> ObjectIcon.Empty.Page
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ sealed class ObjectIcon {
|
|||
data object Page : Empty()
|
||||
data object List : Empty()
|
||||
data object Bookmark : Empty()
|
||||
data object Discussion : Empty()
|
||||
data object Chat : Empty()
|
||||
data object ObjectType : Empty()
|
||||
data object Date : Empty()
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ import com.anytypeio.anytype.core_models.ObjectWrapper
|
|||
import com.anytypeio.anytype.core_models.Wallpaper
|
||||
import com.anytypeio.anytype.core_models.primitives.Space
|
||||
import com.anytypeio.anytype.core_models.primitives.SpaceId
|
||||
import com.anytypeio.anytype.core_models.restrictions.SpaceStatus
|
||||
import com.anytypeio.anytype.domain.base.fold
|
||||
import com.anytypeio.anytype.domain.base.onFailure
|
||||
import com.anytypeio.anytype.domain.base.onSuccess
|
||||
|
@ -281,7 +280,7 @@ class VaultViewModel(
|
|||
)
|
||||
)
|
||||
}
|
||||
is OpenObjectNavigation.OpenDiscussion -> {
|
||||
is OpenObjectNavigation.OpenChat -> {
|
||||
navigate(
|
||||
Navigation.OpenChat(
|
||||
ctx = navigation.target,
|
||||
|
|
|
@ -920,7 +920,7 @@ class CollectionViewModel(
|
|||
)
|
||||
)
|
||||
}
|
||||
is OpenObjectNavigation.OpenDiscussion -> {
|
||||
is OpenObjectNavigation.OpenChat -> {
|
||||
commands.emit(
|
||||
Command.OpenChat(
|
||||
target = navigation.target,
|
||||
|
|
|
@ -65,6 +65,6 @@ include ':crash-reporting'
|
|||
include ':localization'
|
||||
include ':payments'
|
||||
include ':gallery-experience'
|
||||
include ':feature-discussions'
|
||||
include ':feature-chats'
|
||||
include ':feature-all-content'
|
||||
include ':feature-date'
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue