mirror of
https://github.com/anyproto/anytype-kotlin.git
synced 2025-06-07 21:37:02 +09:00
DROID-3590 Vault 2.0 | Ui + logic, part 1 (#2494)
This commit is contained in:
parent
86e20dfadd
commit
bda6de3208
17 changed files with 558 additions and 447 deletions
|
@ -69,6 +69,7 @@ import com.anytypeio.anytype.presentation.common.Action
|
|||
import com.anytypeio.anytype.presentation.common.Delegator
|
||||
import com.anytypeio.anytype.presentation.editor.cover.CoverImageHashProvider
|
||||
import com.anytypeio.anytype.core_models.ObjectViewDetails
|
||||
import com.anytypeio.anytype.domain.resources.StringResourceProvider
|
||||
import com.anytypeio.anytype.presentation.sets.ObjectSetDatabase
|
||||
import com.anytypeio.anytype.presentation.sets.ObjectSetPaginator
|
||||
import com.anytypeio.anytype.presentation.sets.ObjectSetSession
|
||||
|
@ -87,6 +88,7 @@ import kotlinx.coroutines.flow.emptyFlow
|
|||
import kotlinx.coroutines.test.StandardTestDispatcher
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito
|
||||
import org.mockito.Mockito.mock
|
||||
import org.mockito.MockitoAnnotations
|
||||
import org.mockito.kotlin.any
|
||||
import org.mockito.kotlin.doReturn
|
||||
|
@ -244,7 +246,10 @@ abstract class TestObjectSetSetup {
|
|||
private val dateProvider = DateProviderImpl(
|
||||
defaultZoneId = ZoneId.systemDefault(),
|
||||
localeProvider = localeProvider,
|
||||
appDefaultDateFormatProvider = AppDefaultDateFormatProviderImpl(localeProvider)
|
||||
appDefaultDateFormatProvider = AppDefaultDateFormatProviderImpl(localeProvider),
|
||||
stringResourceProvider = mock(
|
||||
StringResourceProvider::class.java
|
||||
)
|
||||
)
|
||||
|
||||
open fun setup() {
|
||||
|
|
|
@ -13,15 +13,17 @@ import com.anytypeio.anytype.domain.config.UserSettingsRepository
|
|||
import com.anytypeio.anytype.domain.debugging.Logger
|
||||
import com.anytypeio.anytype.domain.deeplink.PendingIntentStore
|
||||
import com.anytypeio.anytype.domain.misc.AppActionManager
|
||||
import com.anytypeio.anytype.domain.misc.DateProvider
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.domain.multiplayer.SpaceInviteResolver
|
||||
import com.anytypeio.anytype.domain.multiplayer.SpaceViewSubscriptionContainer
|
||||
import com.anytypeio.anytype.domain.multiplayer.UserPermissionProvider
|
||||
import com.anytypeio.anytype.domain.resources.StringResourceProvider
|
||||
import com.anytypeio.anytype.domain.search.ProfileSubscriptionManager
|
||||
import com.anytypeio.anytype.domain.workspace.SpaceManager
|
||||
import com.anytypeio.anytype.other.DefaultSpaceInviteResolver
|
||||
import com.anytypeio.anytype.presentation.navigation.DeepLinkToObjectDelegate
|
||||
import com.anytypeio.anytype.presentation.vault.VaultViewModel
|
||||
import com.anytypeio.anytype.presentation.vault.VaultViewModelFactory
|
||||
import com.anytypeio.anytype.ui.vault.VaultFragment
|
||||
import dagger.Binds
|
||||
import dagger.Component
|
||||
|
@ -53,7 +55,7 @@ object VaultModule {
|
|||
@PerScreen
|
||||
@Binds
|
||||
fun bindViewModelFactory(
|
||||
factory: VaultViewModel.Factory
|
||||
factory: VaultViewModelFactory
|
||||
): ViewModelProvider.Factory
|
||||
|
||||
@PerScreen
|
||||
|
@ -84,4 +86,6 @@ interface VaultComponentDependencies : ComponentDependencies {
|
|||
fun profileContainer(): ProfileSubscriptionManager
|
||||
fun chatPreviewContainer(): ChatPreviewContainer
|
||||
fun pendingIntentStore(): PendingIntentStore
|
||||
fun stringResourceProvider(): StringResourceProvider
|
||||
fun dateProvider(): DateProvider
|
||||
}
|
|
@ -69,11 +69,13 @@ object UtilModule {
|
|||
@Singleton
|
||||
fun provideDateProvider(
|
||||
localeProvider: LocaleProvider,
|
||||
appDefaultDateFormatProvider: AppDefaultDateFormatProvider
|
||||
appDefaultDateFormatProvider: AppDefaultDateFormatProvider,
|
||||
stringResourceProvider: StringResourceProvider
|
||||
): DateProvider = DateProviderImpl(
|
||||
defaultZoneId = ZoneId.systemDefault(),
|
||||
localeProvider = localeProvider,
|
||||
appDefaultDateFormatProvider = appDefaultDateFormatProvider
|
||||
appDefaultDateFormatProvider = appDefaultDateFormatProvider,
|
||||
stringResourceProvider = stringResourceProvider
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
|
|
|
@ -25,10 +25,11 @@ import com.anytypeio.anytype.core_ui.views.ButtonSize
|
|||
|
||||
@Composable
|
||||
fun VaultEmptyState(
|
||||
modifier: Modifier = Modifier,
|
||||
onCreateSpaceClicked: () -> Unit
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.padding(horizontal = 16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
|
|
|
@ -15,7 +15,6 @@ import androidx.compose.ui.platform.ViewCompositionStrategy
|
|||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.navigation.NavOptions
|
||||
import androidx.navigation.NavOptions.*
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.anytypeio.anytype.R
|
||||
|
@ -25,9 +24,10 @@ import com.anytypeio.anytype.core_utils.insets.EDGE_TO_EDGE_MIN_SDK
|
|||
import com.anytypeio.anytype.core_utils.ui.BaseComposeFragment
|
||||
import com.anytypeio.anytype.di.common.componentManager
|
||||
import com.anytypeio.anytype.other.DefaultDeepLinkResolver
|
||||
import com.anytypeio.anytype.presentation.vault.VaultCommand
|
||||
import com.anytypeio.anytype.presentation.vault.VaultNavigation
|
||||
import com.anytypeio.anytype.presentation.vault.VaultViewModel
|
||||
import com.anytypeio.anytype.presentation.vault.VaultViewModel.Navigation
|
||||
import com.anytypeio.anytype.presentation.vault.VaultViewModel.Command
|
||||
import com.anytypeio.anytype.presentation.vault.VaultViewModelFactory
|
||||
import com.anytypeio.anytype.ui.base.navigation
|
||||
import com.anytypeio.anytype.ui.chats.ChatFragment
|
||||
import com.anytypeio.anytype.ui.gallery.GalleryInstallationFragment
|
||||
|
@ -35,7 +35,6 @@ import com.anytypeio.anytype.ui.home.HomeScreenFragment
|
|||
import com.anytypeio.anytype.ui.multiplayer.RequestJoinSpaceFragment
|
||||
import com.anytypeio.anytype.ui.payments.MembershipFragment
|
||||
import com.anytypeio.anytype.ui.settings.typography
|
||||
import com.anytypeio.anytype.ui.spaces.CreateSpaceFragment
|
||||
import com.anytypeio.anytype.ui.spaces.CreateSpaceFragment.Companion.ARG_SPACE_TYPE
|
||||
import com.anytypeio.anytype.ui.spaces.CreateSpaceFragment.Companion.TYPE_CHAT
|
||||
import com.anytypeio.anytype.ui.spaces.CreateSpaceFragment.Companion.TYPE_SPACE
|
||||
|
@ -47,7 +46,7 @@ class VaultFragment : BaseComposeFragment() {
|
|||
private val deepLink: String? get() = argOrNull(DEEP_LINK_KEY)
|
||||
|
||||
@Inject
|
||||
lateinit var factory: VaultViewModel.Factory
|
||||
lateinit var factory: VaultViewModelFactory
|
||||
|
||||
private val vm by viewModels<VaultViewModel> { factory }
|
||||
|
||||
|
@ -68,7 +67,7 @@ class VaultFragment : BaseComposeFragment() {
|
|||
onOrderChanged = vm::onOrderChanged,
|
||||
profile = vm.profileView.collectAsStateWithLifecycle().value
|
||||
)
|
||||
|
||||
|
||||
if (vm.showChooseSpaceType.collectAsStateWithLifecycle().value) {
|
||||
ChooseSpaceTypeScreen(
|
||||
onCreateChatClicked = {
|
||||
|
@ -88,14 +87,14 @@ class VaultFragment : BaseComposeFragment() {
|
|||
vm.commands.collect { command -> proceed(command) }
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
vm.navigation.collect { command -> proceed(command) }
|
||||
vm.navigations.collect { command -> proceed(command) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun proceed(command: Command) {
|
||||
private fun proceed(command: VaultCommand) {
|
||||
when (command) {
|
||||
is Command.EnterSpaceHomeScreen -> {
|
||||
is VaultCommand.EnterSpaceHomeScreen -> {
|
||||
runCatching {
|
||||
findNavController().navigate(
|
||||
R.id.actionOpenSpaceFromVault,
|
||||
|
@ -108,7 +107,8 @@ class VaultFragment : BaseComposeFragment() {
|
|||
Timber.e(it, "Error while opening space from vault")
|
||||
}
|
||||
}
|
||||
is Command.EnterSpaceLevelChat -> {
|
||||
|
||||
is VaultCommand.EnterSpaceLevelChat -> {
|
||||
runCatching {
|
||||
findNavController().navigate(
|
||||
R.id.actionOpenChatFromVault,
|
||||
|
@ -121,7 +121,8 @@ class VaultFragment : BaseComposeFragment() {
|
|||
Timber.e(it, "Error while opening space-level chat from vault")
|
||||
}
|
||||
}
|
||||
is Command.CreateNewSpace -> {
|
||||
|
||||
is VaultCommand.CreateNewSpace -> {
|
||||
runCatching {
|
||||
findNavController().navigate(
|
||||
R.id.actionCreateSpaceFromVault,
|
||||
|
@ -131,7 +132,8 @@ class VaultFragment : BaseComposeFragment() {
|
|||
Timber.e(it, "Error while opening create space screen from vault")
|
||||
}
|
||||
}
|
||||
Command.CreateChat -> {
|
||||
|
||||
VaultCommand.CreateChat -> {
|
||||
runCatching {
|
||||
findNavController().navigate(
|
||||
R.id.actionCreateChatFromVault,
|
||||
|
@ -141,7 +143,8 @@ class VaultFragment : BaseComposeFragment() {
|
|||
Timber.e(it, "Error while opening create chat screen from vault")
|
||||
}
|
||||
}
|
||||
is Command.OpenProfileSettings -> {
|
||||
|
||||
is VaultCommand.OpenProfileSettings -> {
|
||||
runCatching {
|
||||
findNavController().navigate(
|
||||
R.id.profileSettingsScreen,
|
||||
|
@ -151,13 +154,15 @@ class VaultFragment : BaseComposeFragment() {
|
|||
Timber.e(it, "Error while opening profile settings from vault")
|
||||
}
|
||||
}
|
||||
is Command.Deeplink.Invite -> {
|
||||
|
||||
is VaultCommand.Deeplink.Invite -> {
|
||||
findNavController().navigate(
|
||||
R.id.requestJoinSpaceScreen,
|
||||
RequestJoinSpaceFragment.args(link = command.link)
|
||||
)
|
||||
}
|
||||
is Command.Deeplink.GalleryInstallation -> {
|
||||
|
||||
is VaultCommand.Deeplink.GalleryInstallation -> {
|
||||
findNavController().navigate(
|
||||
R.id.galleryInstallationScreen,
|
||||
GalleryInstallationFragment.args(
|
||||
|
@ -166,14 +171,16 @@ class VaultFragment : BaseComposeFragment() {
|
|||
)
|
||||
)
|
||||
}
|
||||
is Command.Deeplink.MembershipScreen -> {
|
||||
|
||||
is VaultCommand.Deeplink.MembershipScreen -> {
|
||||
findNavController().navigate(
|
||||
R.id.paymentsScreen,
|
||||
MembershipFragment.args(command.tierId),
|
||||
Builder().setLaunchSingleTop(true).build()
|
||||
)
|
||||
}
|
||||
is Command.Deeplink.DeepLinkToObjectNotWorking -> {
|
||||
|
||||
is VaultCommand.Deeplink.DeepLinkToObjectNotWorking -> {
|
||||
toast(
|
||||
getString(R.string.multiplayer_deeplink_to_your_object_error)
|
||||
)
|
||||
|
@ -181,9 +188,9 @@ class VaultFragment : BaseComposeFragment() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun proceed(destination: Navigation) {
|
||||
private fun proceed(destination: VaultNavigation) {
|
||||
when (destination) {
|
||||
is Navigation.OpenObject -> runCatching {
|
||||
is VaultNavigation.OpenObject -> runCatching {
|
||||
findNavController().navigate(
|
||||
R.id.actionOpenSpaceFromVault,
|
||||
HomeScreenFragment.args(
|
||||
|
@ -198,7 +205,8 @@ class VaultFragment : BaseComposeFragment() {
|
|||
}.onFailure {
|
||||
Timber.e(it, "Error while opening object from vault")
|
||||
}
|
||||
is Navigation.OpenSet -> runCatching {
|
||||
|
||||
is VaultNavigation.OpenSet -> runCatching {
|
||||
findNavController().navigate(
|
||||
R.id.actionOpenSpaceFromVault,
|
||||
HomeScreenFragment.args(
|
||||
|
@ -214,7 +222,8 @@ class VaultFragment : BaseComposeFragment() {
|
|||
}.onFailure {
|
||||
Timber.e(it, "Error while opening set or collection from vault")
|
||||
}
|
||||
is Navigation.OpenChat -> {
|
||||
|
||||
is VaultNavigation.OpenChat -> {
|
||||
findNavController().navigate(
|
||||
R.id.actionOpenSpaceFromVault,
|
||||
HomeScreenFragment.args(
|
||||
|
@ -227,7 +236,8 @@ class VaultFragment : BaseComposeFragment() {
|
|||
space = destination.space
|
||||
)
|
||||
}
|
||||
is Navigation.OpenDateObject -> {
|
||||
|
||||
is VaultNavigation.OpenDateObject -> {
|
||||
runCatching {
|
||||
findNavController().navigate(
|
||||
R.id.actionOpenSpaceFromVault,
|
||||
|
@ -245,7 +255,7 @@ class VaultFragment : BaseComposeFragment() {
|
|||
}
|
||||
}
|
||||
|
||||
is Navigation.OpenParticipant -> {
|
||||
is VaultNavigation.OpenParticipant -> {
|
||||
runCatching {
|
||||
findNavController().navigate(
|
||||
R.id.actionOpenSpaceFromVault,
|
||||
|
@ -262,9 +272,14 @@ class VaultFragment : BaseComposeFragment() {
|
|||
Timber.e(e, "Error while opening participant object from widgets")
|
||||
}
|
||||
}
|
||||
is Navigation.OpenType -> {
|
||||
|
||||
is VaultNavigation.OpenType -> {
|
||||
Timber.e("Illegal command: type cannot be opened from vault")
|
||||
}
|
||||
|
||||
is VaultNavigation.ShowError -> {
|
||||
toast(destination.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -304,7 +319,7 @@ class VaultFragment : BaseComposeFragment() {
|
|||
companion object {
|
||||
private const val SHOW_MNEMONIC_KEY = "arg.vault-screen.show-mnemonic"
|
||||
private const val DEEP_LINK_KEY = "arg.vault-screen.deep-link"
|
||||
fun args(deeplink: String?) : Bundle = bundleOf(
|
||||
fun args(deeplink: String?): Bundle = bundleOf(
|
||||
DEEP_LINK_KEY to deeplink
|
||||
)
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ 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.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
|
@ -24,6 +23,7 @@ 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.Scaffold
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
|
@ -33,7 +33,6 @@ 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.alpha
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
@ -42,41 +41,26 @@ import androidx.compose.ui.res.colorResource
|
|||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
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.compose.ui.unit.sp
|
||||
import androidx.core.graphics.toColorInt
|
||||
import coil3.compose.rememberAsyncImagePainter
|
||||
import com.anytypeio.anytype.R
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.ObjectWrapper
|
||||
import com.anytypeio.anytype.core_models.Relations
|
||||
import com.anytypeio.anytype.core_models.Wallpaper
|
||||
import com.anytypeio.anytype.core_models.ext.EMPTY_STRING_VALUE
|
||||
import com.anytypeio.anytype.core_models.multiplayer.SpaceAccessType
|
||||
import com.anytypeio.anytype.core_ui.common.DefaultPreviews
|
||||
import com.anytypeio.anytype.core_ui.features.SpaceIconView
|
||||
import com.anytypeio.anytype.core_ui.features.wallpaper.gradient
|
||||
import com.anytypeio.anytype.core_ui.foundation.noRippleClickable
|
||||
import com.anytypeio.anytype.core_ui.foundation.util.DraggableItem
|
||||
import com.anytypeio.anytype.core_ui.foundation.util.dragContainer
|
||||
import com.anytypeio.anytype.core_ui.foundation.util.rememberDragDropState
|
||||
import com.anytypeio.anytype.core_ui.views.AvatarTitle
|
||||
import com.anytypeio.anytype.core_ui.views.BodySemiBold
|
||||
import com.anytypeio.anytype.core_ui.views.Caption1Regular
|
||||
import com.anytypeio.anytype.core_ui.views.HeadlineTitle
|
||||
import com.anytypeio.anytype.core_ui.views.Relations3
|
||||
import com.anytypeio.anytype.core_ui.views.Title1
|
||||
import com.anytypeio.anytype.core_ui.views.animations.conditionalBackground
|
||||
import com.anytypeio.anytype.core_utils.insets.EDGE_TO_EDGE_MIN_SDK
|
||||
import com.anytypeio.anytype.presentation.editor.cover.CoverGradient
|
||||
import com.anytypeio.anytype.presentation.profile.AccountProfile
|
||||
import com.anytypeio.anytype.presentation.profile.ProfileIconView
|
||||
import com.anytypeio.anytype.presentation.spaces.SelectSpaceViewModel
|
||||
import com.anytypeio.anytype.presentation.spaces.SpaceIconView
|
||||
import com.anytypeio.anytype.presentation.vault.VaultViewModel.VaultSpaceView
|
||||
import com.anytypeio.anytype.presentation.wallpaper.WallpaperColor
|
||||
import com.anytypeio.anytype.presentation.vault.VaultSpaceView
|
||||
import com.bumptech.glide.integration.compose.ExperimentalGlideComposeApi
|
||||
|
||||
|
||||
|
@ -114,8 +98,8 @@ fun VaultScreen(
|
|||
}
|
||||
)
|
||||
|
||||
Box(
|
||||
Modifier
|
||||
Scaffold(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(
|
||||
color = colorResource(id = R.color.background_primary)
|
||||
|
@ -125,25 +109,27 @@ fun VaultScreen(
|
|||
Modifier.windowInsetsPadding(WindowInsets.systemBars)
|
||||
else
|
||||
Modifier
|
||||
),
|
||||
topBar = {
|
||||
VaultScreenToolbar(
|
||||
profile = profile,
|
||||
onPlusClicked = onCreateSpaceClicked,
|
||||
onSettingsClicked = onSettingsClicked,
|
||||
spaceCountLimitReached = spaces.size >= SelectSpaceViewModel.MAX_SPACE_COUNT,
|
||||
isScrolled = isScrolled.value
|
||||
)
|
||||
) {
|
||||
VaultScreenToolbar(
|
||||
profile = profile,
|
||||
onPlusClicked = onCreateSpaceClicked,
|
||||
onSettingsClicked = onSettingsClicked,
|
||||
spaceCountLimitReached = spaces.size >= SelectSpaceViewModel.MAX_SPACE_COUNT,
|
||||
isScrolled = isScrolled.value
|
||||
)
|
||||
|
||||
}
|
||||
) { paddings ->
|
||||
if (spaces.isEmpty()) {
|
||||
VaultEmptyState(
|
||||
modifier = Modifier.padding(paddings),
|
||||
onCreateSpaceClicked = onCreateSpaceClicked
|
||||
)
|
||||
} else {
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(top = 48.dp)
|
||||
.padding(paddings)
|
||||
.dragContainer(dragDropState),
|
||||
state = lazyListState,
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
|
@ -152,36 +138,52 @@ fun VaultScreen(
|
|||
items = spaceList,
|
||||
key = { _, item ->
|
||||
item.space.id
|
||||
},
|
||||
contentType = { _, item ->
|
||||
when (item) {
|
||||
is VaultSpaceView.Chat -> TYPE_CHAT
|
||||
is VaultSpaceView.Space -> TYPE_SPACE
|
||||
is VaultSpaceView.Loading -> TYPE_LOADING
|
||||
}
|
||||
}
|
||||
) { idx, item ->
|
||||
if (idx == 0) {
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
}
|
||||
DraggableItem(dragDropState = dragDropState, index = idx) {
|
||||
if (item.space.isLoading) {
|
||||
LoadingSpaceCard()
|
||||
} else {
|
||||
VaultSpaceCard(
|
||||
title = item.space.name.orEmpty(),
|
||||
subtitle = when (item.space.spaceAccessType) {
|
||||
SpaceAccessType.PRIVATE -> stringResource(id = R.string.space_type_private_space)
|
||||
SpaceAccessType.DEFAULT -> stringResource(id = R.string.space_type_default_space)
|
||||
SpaceAccessType.SHARED -> stringResource(id = R.string.space_type_shared_space)
|
||||
else -> EMPTY_STRING_VALUE
|
||||
},
|
||||
wallpaper = item.wallpaper,
|
||||
onCardClicked = { onSpaceClicked(item) },
|
||||
icon = item.icon,
|
||||
unreadMessageCount = item.unreadMessageCount,
|
||||
unreadMentionCount = item.unreadMentionCount
|
||||
)
|
||||
when (item) {
|
||||
is VaultSpaceView.Chat -> {
|
||||
DraggableItem(dragDropState = dragDropState, index = idx) {
|
||||
VaultChatCard(
|
||||
title = item.space.name.orEmpty(),
|
||||
onCardClicked = {
|
||||
onSpaceClicked(item)
|
||||
},
|
||||
icon = item.icon,
|
||||
previewText = item.previewText,
|
||||
creatorName = item.creatorName,
|
||||
messageText = item.messageText,
|
||||
messageTime = item.messageTime,
|
||||
chatPreview = item.chatPreview
|
||||
)
|
||||
}
|
||||
}
|
||||
is VaultSpaceView.Loading -> {
|
||||
DraggableItem(dragDropState = dragDropState, index = idx) {
|
||||
LoadingSpaceCard()
|
||||
}
|
||||
}
|
||||
is VaultSpaceView.Space -> {
|
||||
DraggableItem(dragDropState = dragDropState, index = idx) {
|
||||
VaultSpaceCard(
|
||||
title = item.space.name.orEmpty(),
|
||||
subtitle = item.accessType,
|
||||
onCardClicked = {
|
||||
onSpaceClicked(item)
|
||||
},
|
||||
icon = item.icon,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (idx == spaces.lastIndex && spaces.size < SelectSpaceViewModel.MAX_SPACE_COUNT) {
|
||||
VaultSpaceAddCard(
|
||||
onCreateSpaceClicked = onCreateSpaceClicked
|
||||
)
|
||||
Spacer(modifier = Modifier.height(40.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -304,154 +306,6 @@ fun VaultScreenToolbar(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun VaultSpaceCard(
|
||||
title: String,
|
||||
subtitle: String,
|
||||
onCardClicked: () -> Unit,
|
||||
icon: SpaceIconView,
|
||||
wallpaper: Wallpaper,
|
||||
unreadMessageCount: Int = 0,
|
||||
unreadMentionCount: Int = 0
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(96.dp)
|
||||
.padding(horizontal = 8.dp)
|
||||
.clip(RoundedCornerShape(20.dp))
|
||||
.then(
|
||||
when (wallpaper) {
|
||||
is Wallpaper.Color -> {
|
||||
val color = WallpaperColor.entries.find {
|
||||
it.code == wallpaper.code
|
||||
}
|
||||
if (color != null) {
|
||||
Modifier.background(
|
||||
color = Color(color.hex.toColorInt()).copy(0.3f),
|
||||
shape = RoundedCornerShape(20.dp)
|
||||
)
|
||||
} else {
|
||||
Modifier
|
||||
}
|
||||
}
|
||||
|
||||
is Wallpaper.Gradient -> {
|
||||
Modifier.background(
|
||||
brush = Brush.verticalGradient(
|
||||
colors = gradient(
|
||||
gradient = wallpaper.code,
|
||||
alpha = 0.3f
|
||||
)
|
||||
),
|
||||
shape = RoundedCornerShape(20.dp)
|
||||
)
|
||||
}
|
||||
|
||||
is Wallpaper.Default -> {
|
||||
Modifier.background(
|
||||
brush = Brush.verticalGradient(
|
||||
colors = gradient(
|
||||
gradient = CoverGradient.SKY,
|
||||
alpha = 0.3f
|
||||
)
|
||||
),
|
||||
shape = RoundedCornerShape(20.dp)
|
||||
)
|
||||
}
|
||||
|
||||
else -> Modifier
|
||||
}
|
||||
)
|
||||
.clickable {
|
||||
onCardClicked()
|
||||
}
|
||||
) {
|
||||
SpaceIconView(
|
||||
icon = icon,
|
||||
onSpaceIconClick = {
|
||||
onCardClicked()
|
||||
},
|
||||
mainSize = 64.dp,
|
||||
modifier = Modifier
|
||||
.padding(start = 16.dp)
|
||||
.align(Alignment.CenterStart)
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
top = 24.dp,
|
||||
start = 96.dp,
|
||||
end = 16.dp
|
||||
)
|
||||
) {
|
||||
Text(
|
||||
text = title.ifEmpty { stringResource(id = R.string.untitled) },
|
||||
style = BodySemiBold,
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Text(
|
||||
text = subtitle,
|
||||
style = Relations3,
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
modifier = Modifier.alpha(0.6f)
|
||||
)
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterEnd)
|
||||
.padding(end = 16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
if (unreadMentionCount > 0) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
color = colorResource(R.color.glyph_active),
|
||||
shape = CircleShape
|
||||
)
|
||||
.size(18.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(R.drawable.ic_chat_widget_mention),
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
}
|
||||
|
||||
if (unreadMessageCount > 0) {
|
||||
val shape = if (unreadMentionCount > 9) {
|
||||
CircleShape
|
||||
} else {
|
||||
RoundedCornerShape(100.dp)
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(18.dp)
|
||||
.background(
|
||||
color = colorResource(R.color.glyph_active),
|
||||
shape = shape
|
||||
)
|
||||
.padding(horizontal = 5.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
text = unreadMessageCount.toString(),
|
||||
style = Caption1Regular,
|
||||
color = colorResource(id = R.color.text_white),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun VaultSpaceAddCard(
|
||||
onCreateSpaceClicked: () -> Unit
|
||||
|
@ -593,43 +447,32 @@ fun VaultScreenToolbarScrolledPreview() {
|
|||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, name = "Light Mode")
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO, name = "Dark Mode")
|
||||
fun VaultSpaceCardPreview() {
|
||||
VaultSpaceCard(
|
||||
title = "B&O Museum",
|
||||
subtitle = "Private space",
|
||||
onCardClicked = {},
|
||||
wallpaper = Wallpaper.Default,
|
||||
icon = SpaceIconView.Placeholder(),
|
||||
unreadMentionCount = 1,
|
||||
unreadMessageCount = 9
|
||||
)
|
||||
}
|
||||
//@Composable
|
||||
//@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, name = "Light Mode")
|
||||
//@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO, name = "Dark Mode")
|
||||
//fun VaultScreenPreview() {
|
||||
// VaultScreen(
|
||||
// spaces = buildList {
|
||||
// add(
|
||||
// VaultSpaceView(
|
||||
// space = ObjectWrapper.SpaceView(
|
||||
// mapOf(
|
||||
// Relations.NAME to "B&O Museum",
|
||||
// Relations.SPACE_ACCESS_TYPE to SpaceAccessType.SHARED.code.toDouble()
|
||||
// )
|
||||
// ),
|
||||
// icon = SpaceIconView.Placeholder()
|
||||
// )
|
||||
// )
|
||||
// },
|
||||
// onSpaceClicked = {},
|
||||
// onCreateSpaceClicked = {},
|
||||
// onSettingsClicked = {},
|
||||
// onOrderChanged = {},
|
||||
// profile = AccountProfile.Idle
|
||||
// )
|
||||
//}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, name = "Light Mode")
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO, name = "Dark Mode")
|
||||
fun VaultScreenPreview() {
|
||||
VaultScreen(
|
||||
spaces = buildList {
|
||||
add(
|
||||
VaultSpaceView(
|
||||
space = ObjectWrapper.SpaceView(
|
||||
mapOf(
|
||||
Relations.NAME to "B&O Museum",
|
||||
Relations.SPACE_ACCESS_TYPE to SpaceAccessType.SHARED.code.toDouble()
|
||||
)
|
||||
),
|
||||
icon = SpaceIconView.Placeholder()
|
||||
)
|
||||
)
|
||||
},
|
||||
onSpaceClicked = {},
|
||||
onCreateSpaceClicked = {},
|
||||
onSettingsClicked = {},
|
||||
onOrderChanged = {},
|
||||
profile = AccountProfile.Idle
|
||||
)
|
||||
}
|
||||
const val TYPE_CHAT = "chat"
|
||||
const val TYPE_SPACE = "space"
|
||||
const val TYPE_LOADING = "loading"
|
|
@ -0,0 +1,14 @@
|
|||
package com.anytypeio.anytype.ui.vault
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import com.anytypeio.anytype.presentation.spaces.SpaceIconView
|
||||
|
||||
@Composable
|
||||
fun VaultSpaceCard(
|
||||
title: String,
|
||||
subtitle: String,
|
||||
onCardClicked: () -> Unit,
|
||||
icon: SpaceIconView,
|
||||
) {
|
||||
//todo next pr
|
||||
}
|
|
@ -348,4 +348,12 @@ val HeadlineTitleSemibold =
|
|||
fontSize = 28.sp,
|
||||
lineHeight = 32.sp,
|
||||
letterSpacing = (-0.017).em
|
||||
)
|
||||
)
|
||||
|
||||
val chatPreviewTextStyle = TextStyle(
|
||||
fontFamily = fontInterRegular,
|
||||
fontWeight = FontWeight.W500,
|
||||
fontSize = 15.sp,
|
||||
lineHeight = 20.sp,
|
||||
letterSpacing = (-0.014).em
|
||||
)
|
|
@ -8,6 +8,7 @@ import com.anytypeio.anytype.core_models.TimeInSeconds
|
|||
import com.anytypeio.anytype.domain.misc.DateProvider
|
||||
import com.anytypeio.anytype.domain.misc.DateType
|
||||
import com.anytypeio.anytype.domain.misc.LocaleProvider
|
||||
import com.anytypeio.anytype.domain.resources.StringResourceProvider
|
||||
import java.text.DateFormat
|
||||
import java.text.SimpleDateFormat
|
||||
import java.time.Instant
|
||||
|
@ -24,7 +25,8 @@ import timber.log.Timber
|
|||
class DateProviderImpl @Inject constructor(
|
||||
private val defaultZoneId: ZoneId,
|
||||
private val localeProvider: LocaleProvider,
|
||||
private val appDefaultDateFormatProvider: AppDefaultDateFormatProvider
|
||||
private val appDefaultDateFormatProvider: AppDefaultDateFormatProvider,
|
||||
private val stringResourceProvider: StringResourceProvider
|
||||
) : DateProvider {
|
||||
|
||||
private val defaultDateFormat get() = appDefaultDateFormatProvider.provide()
|
||||
|
@ -148,6 +150,7 @@ class DateProviderImpl @Inject constructor(
|
|||
try {
|
||||
val locale = localeProvider.locale()
|
||||
val formatter = SimpleDateFormat(pattern, locale)
|
||||
formatter.timeZone = java.util.TimeZone.getTimeZone(defaultZoneId)
|
||||
return formatter.format(Date(timestamp))
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "Error formatting timestamp to date string")
|
||||
|
@ -256,6 +259,38 @@ class DateProviderImpl @Inject constructor(
|
|||
// Check if the year is within the desired range
|
||||
return year in yearRange.first()..yearRange.last()
|
||||
}
|
||||
|
||||
override fun getChatPreviewDate(
|
||||
timeInSeconds: TimeInSeconds,
|
||||
timeStyle: Int
|
||||
): String {
|
||||
val dateType = calculateDateType(timeInSeconds)
|
||||
val timestamp = timeInSeconds * 1000 // Convert seconds to milliseconds
|
||||
|
||||
return when (dateType) {
|
||||
DateType.TODAY -> {
|
||||
// Show time in HH:mm format
|
||||
formatToDateString(timestamp, "HH:mm")
|
||||
}
|
||||
DateType.YESTERDAY -> {
|
||||
// Show "Yesterday" localized
|
||||
stringResourceProvider.getYesterday()
|
||||
}
|
||||
else -> {
|
||||
// Check if it's current year
|
||||
val currentYear = getLocalDateOfTime(System.currentTimeMillis()).year
|
||||
val messageYear = getLocalDateOfTime(timestamp).year
|
||||
|
||||
if (currentYear == messageYear) {
|
||||
// Show "MMM d" format (e.g., "Apr 12")
|
||||
formatToDateString(timestamp, "MMM d")
|
||||
} else {
|
||||
// Show "MMM d, yyyy" format (e.g., "Apr 12, 2024")
|
||||
formatToDateString(timestamp, "MMM d, yyyy")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -5,8 +5,11 @@ import com.anytypeio.anytype.device.providers.AppDefaultDateFormatProviderImpl
|
|||
import com.anytypeio.anytype.device.providers.DateProviderImpl
|
||||
import com.anytypeio.anytype.domain.misc.DateProvider
|
||||
import com.anytypeio.anytype.domain.misc.LocaleProvider
|
||||
import com.anytypeio.anytype.domain.resources.StringResourceProvider
|
||||
import com.anytypeio.anytype.domain.vault.ObserveVaultSettings
|
||||
import java.time.ZoneId
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneOffset
|
||||
import java.util.Locale
|
||||
import kotlinx.coroutines.test.StandardTestDispatcher
|
||||
import kotlinx.coroutines.test.runTest
|
||||
|
@ -27,6 +30,9 @@ class DateProviderImplTest {
|
|||
@Mock
|
||||
lateinit var observeVaultSettings: ObserveVaultSettings
|
||||
|
||||
@Mock
|
||||
lateinit var stringResourceProvider: StringResourceProvider
|
||||
|
||||
lateinit var dateProviderImpl: DateProvider
|
||||
|
||||
lateinit var appDefaultDateFormatProvider: AppDefaultDateFormatProvider
|
||||
|
@ -36,7 +42,8 @@ class DateProviderImplTest {
|
|||
MockitoAnnotations.openMocks(this)
|
||||
appDefaultDateFormatProvider = AppDefaultDateFormatProviderImpl(localeProvider)
|
||||
Mockito.`when`(localeProvider.locale()).thenReturn(Locale.getDefault())
|
||||
Mockito.`when`(localeProvider.language()).thenReturn("en")
|
||||
Mockito.`when`(localeProvider.language()).thenReturn(Locale.getDefault().language)
|
||||
Mockito.`when`(stringResourceProvider.getYesterday()).thenReturn("Yesterday")
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -78,6 +85,7 @@ class DateProviderImplTest {
|
|||
defaultZoneId = zoneId,
|
||||
localeProvider = localeProvider,
|
||||
appDefaultDateFormatProvider = appDefaultDateFormatProvider,
|
||||
stringResourceProvider = stringResourceProvider
|
||||
)
|
||||
val startOfDayInLocalZone =
|
||||
dateProviderImpl.adjustFromStartOfDayInUserTimeZoneToUTC(
|
||||
|
@ -127,6 +135,7 @@ class DateProviderImplTest {
|
|||
defaultZoneId = zoneId,
|
||||
localeProvider = localeProvider,
|
||||
appDefaultDateFormatProvider = appDefaultDateFormatProvider,
|
||||
stringResourceProvider = stringResourceProvider
|
||||
)
|
||||
val startOfDayInLocalZone =
|
||||
dateProviderImpl.adjustFromStartOfDayInUserTimeZoneToUTC(utcTimestamp * 1000)
|
||||
|
@ -173,6 +182,7 @@ class DateProviderImplTest {
|
|||
defaultZoneId = zoneId,
|
||||
localeProvider = localeProvider,
|
||||
appDefaultDateFormatProvider = appDefaultDateFormatProvider,
|
||||
stringResourceProvider = stringResourceProvider
|
||||
)
|
||||
val startOfDayInLocalZone =
|
||||
dateProviderImpl.adjustFromStartOfDayInUserTimeZoneToUTC(utcTimestamp * 1000)
|
||||
|
@ -218,6 +228,7 @@ class DateProviderImplTest {
|
|||
defaultZoneId = zoneId,
|
||||
localeProvider = localeProvider,
|
||||
appDefaultDateFormatProvider = appDefaultDateFormatProvider,
|
||||
stringResourceProvider = stringResourceProvider
|
||||
)
|
||||
val startOfDayInLocalZone =
|
||||
dateProviderImpl.adjustFromStartOfDayInUserTimeZoneToUTC(utcTimestamp * 1000)
|
||||
|
|
|
@ -55,6 +55,15 @@ interface ChatPreviewContainer {
|
|||
.scan(initial = initial) { previews, events ->
|
||||
events.fold(previews) { state, event ->
|
||||
when (event) {
|
||||
is Event.Command.Chats.Add -> {
|
||||
state.map { preview ->
|
||||
if (preview.chat == event.context) {
|
||||
preview.copy(message = event.message)
|
||||
} else {
|
||||
preview
|
||||
}
|
||||
}
|
||||
}
|
||||
is Event.Command.Chats.Update -> {
|
||||
state.map { preview ->
|
||||
if (preview.chat == event.context && preview.message?.id == event.id) {
|
||||
|
|
|
@ -30,6 +30,10 @@ interface DateProvider {
|
|||
fun isSameMinute(timestamp1: Long, timestamp2: Long): Boolean
|
||||
fun getLocalDateOfTime(epochMilli: Long): LocalDate
|
||||
fun isTimestampWithinYearRange(timeStampInMillis: Long, yearRange: IntRange): Boolean
|
||||
fun getChatPreviewDate(
|
||||
timeInSeconds: TimeInSeconds,
|
||||
timeStyle: Int = DEFAULT_DATE_FORMAT_STYLE
|
||||
): String
|
||||
}
|
||||
|
||||
interface DateTypeNameProvider {
|
||||
|
|
|
@ -2,6 +2,7 @@ package com.anytypeio.anytype.domain.resources
|
|||
|
||||
import com.anytypeio.anytype.core_models.RelationFormat
|
||||
import com.anytypeio.anytype.core_models.RelativeDate
|
||||
import com.anytypeio.anytype.core_models.multiplayer.SpaceAccessType
|
||||
|
||||
interface StringResourceProvider {
|
||||
fun getRelativeDateName(relativeDate: RelativeDate): String
|
||||
|
@ -11,4 +12,6 @@ interface StringResourceProvider {
|
|||
fun getPropertiesFormatPrettyString(format: RelationFormat): String
|
||||
fun getDefaultSpaceName(): String
|
||||
fun getAttachmentText(): String
|
||||
fun getSpaceAccessTypeName(accessType: SpaceAccessType?): String
|
||||
fun getYesterday(): String
|
||||
}
|
|
@ -1,8 +1,11 @@
|
|||
package com.anytypeio.anytype.presentation.util
|
||||
|
||||
import android.content.Context
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.anytypeio.anytype.core_models.RelationFormat
|
||||
import com.anytypeio.anytype.core_models.RelativeDate
|
||||
import com.anytypeio.anytype.core_models.ext.EMPTY_STRING_VALUE
|
||||
import com.anytypeio.anytype.core_models.multiplayer.SpaceAccessType
|
||||
import com.anytypeio.anytype.domain.resources.StringResourceProvider
|
||||
import com.anytypeio.anytype.presentation.R
|
||||
import javax.inject.Inject
|
||||
|
@ -59,4 +62,17 @@ class StringResourceProviderImpl @Inject constructor(private val context: Contex
|
|||
override fun getAttachmentText(): String {
|
||||
return context.getString(R.string.attachment)
|
||||
}
|
||||
|
||||
override fun getSpaceAccessTypeName(accessType: SpaceAccessType?): String {
|
||||
return when (accessType) {
|
||||
SpaceAccessType.PRIVATE -> context.getString(R.string.space_type_private_space)
|
||||
SpaceAccessType.DEFAULT -> context.getString(R.string.space_type_default_space)
|
||||
SpaceAccessType.SHARED -> context.getString(R.string.space_type_shared_space)
|
||||
null -> EMPTY_STRING_VALUE
|
||||
}
|
||||
}
|
||||
|
||||
override fun getYesterday(): String {
|
||||
return context.getString(R.string.yesterday)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package com.anytypeio.anytype.presentation.vault
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.anytypeio.anytype.analytics.base.Analytics
|
||||
import com.anytypeio.anytype.domain.chats.ChatPreviewContainer
|
||||
import com.anytypeio.anytype.domain.deeplink.PendingIntentStore
|
||||
import com.anytypeio.anytype.domain.misc.AppActionManager
|
||||
import com.anytypeio.anytype.domain.misc.DateProvider
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.domain.multiplayer.SpaceInviteResolver
|
||||
import com.anytypeio.anytype.domain.multiplayer.SpaceViewSubscriptionContainer
|
||||
import com.anytypeio.anytype.domain.resources.StringResourceProvider
|
||||
import com.anytypeio.anytype.domain.search.ProfileSubscriptionManager
|
||||
import com.anytypeio.anytype.domain.spaces.SaveCurrentSpace
|
||||
import com.anytypeio.anytype.domain.vault.ObserveVaultSettings
|
||||
import com.anytypeio.anytype.domain.vault.SetVaultSpaceOrder
|
||||
import com.anytypeio.anytype.domain.workspace.SpaceManager
|
||||
import com.anytypeio.anytype.presentation.navigation.DeepLinkToObjectDelegate
|
||||
import javax.inject.Inject
|
||||
|
||||
class VaultViewModelFactory @Inject constructor(
|
||||
private val spaceViewSubscriptionContainer: SpaceViewSubscriptionContainer,
|
||||
private val urlBuilder: UrlBuilder,
|
||||
private val spaceManager: SpaceManager,
|
||||
private val saveCurrentSpace: SaveCurrentSpace,
|
||||
private val setVaultSpaceOrder: SetVaultSpaceOrder,
|
||||
private val observeVaultSettings: ObserveVaultSettings,
|
||||
private val analytics: Analytics,
|
||||
private val deepLinkToObjectDelegate: DeepLinkToObjectDelegate,
|
||||
private val appActionManager: AppActionManager,
|
||||
private val spaceInviteResolver: SpaceInviteResolver,
|
||||
private val profileContainer: ProfileSubscriptionManager,
|
||||
private val chatPreviewContainer: ChatPreviewContainer,
|
||||
private val pendingIntentStore: PendingIntentStore,
|
||||
private val stringResourceProvider: StringResourceProvider,
|
||||
private val dateProvider: DateProvider
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(
|
||||
modelClass: Class<T>
|
||||
) = VaultViewModel(
|
||||
spaceViewSubscriptionContainer = spaceViewSubscriptionContainer,
|
||||
urlBuilder = urlBuilder,
|
||||
spaceManager = spaceManager,
|
||||
saveCurrentSpace = saveCurrentSpace,
|
||||
setVaultSpaceOrder = setVaultSpaceOrder,
|
||||
observeVaultSettings = observeVaultSettings,
|
||||
analytics = analytics,
|
||||
deepLinkToObjectDelegate = deepLinkToObjectDelegate,
|
||||
appActionManager = appActionManager,
|
||||
spaceInviteResolver = spaceInviteResolver,
|
||||
profileContainer = profileContainer,
|
||||
chatPreviewContainer = chatPreviewContainer,
|
||||
pendingIntentStore = pendingIntentStore,
|
||||
stringResourceProvider = stringResourceProvider,
|
||||
dateProvider = dateProvider
|
||||
) as T
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
package com.anytypeio.anytype.presentation.vault
|
||||
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.ObjectWrapper
|
||||
import com.anytypeio.anytype.core_models.primitives.Space
|
||||
import com.anytypeio.anytype.presentation.spaces.SpaceIconView
|
||||
|
||||
sealed class VaultSpaceView {
|
||||
|
||||
abstract val space: ObjectWrapper.SpaceView
|
||||
abstract val icon: SpaceIconView
|
||||
|
||||
data class Loading(
|
||||
override val space: ObjectWrapper.SpaceView,
|
||||
override val icon: SpaceIconView
|
||||
) : VaultSpaceView()
|
||||
|
||||
data class Space(
|
||||
override val space: ObjectWrapper.SpaceView,
|
||||
override val icon: SpaceIconView,
|
||||
val accessType: String
|
||||
) : VaultSpaceView()
|
||||
|
||||
data class Chat(
|
||||
override val space: ObjectWrapper.SpaceView,
|
||||
override val icon: SpaceIconView,
|
||||
val unreadMessageCount: Int = 0,
|
||||
val unreadMentionCount: Int = 0,
|
||||
val chatMessage: com.anytypeio.anytype.core_models.chats.Chat.Message.Content? = null,
|
||||
val chatPreview: com.anytypeio.anytype.core_models.chats.Chat.Preview? = null,
|
||||
val previewText: String? = null,
|
||||
val creatorName: String? = null,
|
||||
val messageText: String? = null,
|
||||
val messageTime: String? = null
|
||||
) : VaultSpaceView()
|
||||
}
|
||||
|
||||
sealed class VaultCommand {
|
||||
data class EnterSpaceHomeScreen(val space: Space) : VaultCommand()
|
||||
data class EnterSpaceLevelChat(val space: Space, val chat: Id) : VaultCommand()
|
||||
data object CreateNewSpace : VaultCommand()
|
||||
data object CreateChat : VaultCommand()
|
||||
data object OpenProfileSettings : VaultCommand()
|
||||
|
||||
sealed class Deeplink : VaultCommand() {
|
||||
data object DeepLinkToObjectNotWorking : Deeplink()
|
||||
data class Invite(val link: String) : Deeplink()
|
||||
data class GalleryInstallation(
|
||||
val deepLinkType: String,
|
||||
val deepLinkSource: String
|
||||
) : Deeplink()
|
||||
|
||||
data class MembershipScreen(val tierId: String?) : Deeplink()
|
||||
}
|
||||
}
|
||||
|
||||
sealed class VaultNavigation {
|
||||
data class OpenChat(val ctx: Id, val space: Id) : VaultNavigation()
|
||||
data class OpenObject(val ctx: Id, val space: Id) : VaultNavigation()
|
||||
data class OpenSet(val ctx: Id, val space: Id, val view: Id?) : VaultNavigation()
|
||||
data class OpenDateObject(val ctx: Id, val space: Id) : VaultNavigation()
|
||||
data class OpenParticipant(val ctx: Id, val space: Id) : VaultNavigation()
|
||||
data class OpenType(val target: Id, val space: Id) : VaultNavigation()
|
||||
data class ShowError(val message: String) : VaultNavigation()
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
package com.anytypeio.anytype.presentation.vault
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.anytypeio.anytype.analytics.base.Analytics
|
||||
import com.anytypeio.anytype.analytics.base.EventsDictionary
|
||||
|
@ -10,47 +9,48 @@ import com.anytypeio.anytype.analytics.base.sendEvent
|
|||
import com.anytypeio.anytype.analytics.props.Props
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.ObjectWrapper
|
||||
import com.anytypeio.anytype.core_models.Wallpaper
|
||||
import com.anytypeio.anytype.core_models.Relations
|
||||
import com.anytypeio.anytype.core_models.chats.Chat
|
||||
import com.anytypeio.anytype.core_models.multiplayer.SpaceUxType
|
||||
import com.anytypeio.anytype.core_models.primitives.Space
|
||||
import com.anytypeio.anytype.core_models.primitives.SpaceId
|
||||
import com.anytypeio.anytype.core_models.settings.VaultSettings
|
||||
import com.anytypeio.anytype.domain.base.fold
|
||||
import com.anytypeio.anytype.domain.chats.ChatPreviewContainer
|
||||
import com.anytypeio.anytype.domain.deeplink.PendingIntentStore
|
||||
import com.anytypeio.anytype.domain.misc.AppActionManager
|
||||
import com.anytypeio.anytype.domain.misc.DateProvider
|
||||
import com.anytypeio.anytype.domain.misc.DeepLinkResolver
|
||||
import com.anytypeio.anytype.domain.misc.UrlBuilder
|
||||
import com.anytypeio.anytype.domain.multiplayer.SpaceInviteResolver
|
||||
import com.anytypeio.anytype.domain.multiplayer.SpaceViewSubscriptionContainer
|
||||
import com.anytypeio.anytype.domain.resources.StringResourceProvider
|
||||
import com.anytypeio.anytype.domain.search.ProfileSubscriptionManager
|
||||
import com.anytypeio.anytype.domain.spaces.SaveCurrentSpace
|
||||
import com.anytypeio.anytype.domain.vault.GetVaultSettings
|
||||
import com.anytypeio.anytype.domain.vault.ObserveVaultSettings
|
||||
import com.anytypeio.anytype.domain.vault.SetVaultSettings
|
||||
import com.anytypeio.anytype.domain.vault.SetVaultSpaceOrder
|
||||
import com.anytypeio.anytype.domain.wallpaper.GetSpaceWallpapers
|
||||
import com.anytypeio.anytype.domain.workspace.SpaceManager
|
||||
import com.anytypeio.anytype.presentation.BuildConfig
|
||||
import com.anytypeio.anytype.presentation.confgs.ChatConfig
|
||||
import com.anytypeio.anytype.presentation.home.OpenObjectNavigation
|
||||
import com.anytypeio.anytype.presentation.home.navigation
|
||||
import com.anytypeio.anytype.presentation.navigation.DeepLinkToObjectDelegate
|
||||
import com.anytypeio.anytype.presentation.navigation.NavigationViewModel
|
||||
import com.anytypeio.anytype.presentation.profile.AccountProfile
|
||||
import com.anytypeio.anytype.presentation.profile.profileIcon
|
||||
import com.anytypeio.anytype.presentation.spaces.SpaceGradientProvider
|
||||
import com.anytypeio.anytype.presentation.spaces.SpaceIconView
|
||||
import com.anytypeio.anytype.presentation.spaces.spaceIcon
|
||||
import com.anytypeio.anytype.presentation.vault.VaultViewModel.Navigation.*
|
||||
import javax.inject.Inject
|
||||
import com.anytypeio.anytype.presentation.vault.VaultNavigation.OpenChat
|
||||
import com.anytypeio.anytype.presentation.vault.VaultNavigation.OpenDateObject
|
||||
import com.anytypeio.anytype.presentation.vault.VaultNavigation.OpenObject
|
||||
import com.anytypeio.anytype.presentation.vault.VaultNavigation.OpenParticipant
|
||||
import com.anytypeio.anytype.presentation.vault.VaultNavigation.OpenSet
|
||||
import com.anytypeio.anytype.presentation.vault.VaultNavigation.OpenType
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.debounce
|
||||
import kotlinx.coroutines.flow.emitAll
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onCompletion
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
|
@ -61,7 +61,6 @@ import timber.log.Timber
|
|||
class VaultViewModel(
|
||||
private val spaceViewSubscriptionContainer: SpaceViewSubscriptionContainer,
|
||||
private val urlBuilder: UrlBuilder,
|
||||
private val getSpaceWallpapers: GetSpaceWallpapers,
|
||||
private val spaceManager: SpaceManager,
|
||||
private val saveCurrentSpace: SaveCurrentSpace,
|
||||
private val observeVaultSettings: ObserveVaultSettings,
|
||||
|
@ -72,11 +71,15 @@ class VaultViewModel(
|
|||
private val spaceInviteResolver: SpaceInviteResolver,
|
||||
private val profileContainer: ProfileSubscriptionManager,
|
||||
private val chatPreviewContainer: ChatPreviewContainer,
|
||||
private val pendingIntentStore: PendingIntentStore
|
||||
) : NavigationViewModel<VaultViewModel.Navigation>(), DeepLinkToObjectDelegate by deepLinkToObjectDelegate {
|
||||
private val pendingIntentStore: PendingIntentStore,
|
||||
private val stringResourceProvider: StringResourceProvider,
|
||||
private val dateProvider: DateProvider
|
||||
) : ViewModel(),
|
||||
DeepLinkToObjectDelegate by deepLinkToObjectDelegate {
|
||||
|
||||
val spaces = MutableStateFlow<List<VaultSpaceView>>(emptyList())
|
||||
val commands = MutableSharedFlow<Command>(replay = 0)
|
||||
val commands = MutableSharedFlow<VaultCommand>(replay = 0)
|
||||
val navigations = MutableSharedFlow<VaultNavigation>(replay = 0)
|
||||
val showChooseSpaceType = MutableStateFlow(false)
|
||||
|
||||
val profileView = profileContainer.observe().map { obj ->
|
||||
|
@ -93,57 +96,131 @@ class VaultViewModel(
|
|||
init {
|
||||
Timber.i("VaultViewModel, init")
|
||||
viewModelScope.launch {
|
||||
val wallpapers = getSpaceWallpapers.async(Unit).getOrNull() ?: emptyMap()
|
||||
combine(
|
||||
spaceViewSubscriptionContainer
|
||||
.observe()
|
||||
.take(1)
|
||||
.onCompletion {
|
||||
emitAll(
|
||||
spaceViewSubscriptionContainer
|
||||
.observe()
|
||||
.debounce(SPACE_VAULT_DEBOUNCE_DURATION)
|
||||
spaceViewSubscriptionContainer.observe()
|
||||
)
|
||||
},
|
||||
observeVaultSettings.flow(),
|
||||
chatPreviewContainer.observePreviews()
|
||||
) { spaces, settings, chatPreviews ->
|
||||
spaces
|
||||
.filter { space -> (space.isActive || space.isLoading) }
|
||||
.map { space ->
|
||||
val chatPreview = space.targetSpaceId?.let { spaceId ->
|
||||
chatPreviews.find { it.space.id == spaceId }
|
||||
}
|
||||
|
||||
VaultSpaceView(
|
||||
space = space,
|
||||
icon = space.spaceIcon(
|
||||
builder = urlBuilder,
|
||||
spaceGradientProvider = SpaceGradientProvider.Default
|
||||
),
|
||||
wallpaper = wallpapers.getOrDefault(
|
||||
key = space.targetSpaceId,
|
||||
defaultValue = Wallpaper.Default
|
||||
),
|
||||
unreadMessageCount = chatPreview?.state?.unreadMessages?.counter ?: 0,
|
||||
unreadMentionCount = chatPreview?.state?.unreadMentions?.counter ?: 0
|
||||
)
|
||||
}.sortedBy { space ->
|
||||
val idx = settings.orderOfSpaces.indexOf(
|
||||
space.space.id
|
||||
)
|
||||
if (idx == -1) {
|
||||
Int.MIN_VALUE
|
||||
} else {
|
||||
idx
|
||||
}
|
||||
}
|
||||
}.collect {
|
||||
spaces.value = it
|
||||
) { spacesFromFlow, settings, chatPreviews ->
|
||||
transformToVaultSpaceViews(spacesFromFlow, settings, chatPreviews)
|
||||
}.collect { resultingSpaceViews ->
|
||||
spaces.value = resultingSpaceViews
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun transformToVaultSpaceViews(
|
||||
spacesFromFlow: List<ObjectWrapper.SpaceView>,
|
||||
settings: VaultSettings,
|
||||
chatPreviews: List<Chat.Preview>
|
||||
): List<VaultSpaceView> {
|
||||
return spacesFromFlow
|
||||
.filter { space -> (space.isActive || space.isLoading) }
|
||||
.map { space ->
|
||||
val chatPreview = space.targetSpaceId?.let { spaceId ->
|
||||
chatPreviews.find { it.space.id == spaceId }
|
||||
}
|
||||
mapToVaultSpaceViewItem(space, chatPreview)
|
||||
}.sortedBy { spaceView ->
|
||||
val idx = settings.orderOfSpaces.indexOf(
|
||||
spaceView.space.id
|
||||
)
|
||||
if (idx == -1) {
|
||||
Int.MIN_VALUE
|
||||
} else {
|
||||
idx
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun mapToVaultSpaceViewItem(
|
||||
space: ObjectWrapper.SpaceView,
|
||||
chatPreview: Chat.Preview?
|
||||
): VaultSpaceView {
|
||||
return when {
|
||||
space.isLoading -> createLoadingView(space)
|
||||
chatPreview != null -> createChatView(space, chatPreview)
|
||||
else -> createStandardSpaceView(space)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createLoadingView(
|
||||
space: ObjectWrapper.SpaceView
|
||||
): VaultSpaceView.Loading {
|
||||
Timber.d("Space ${space.id} is loading")
|
||||
return VaultSpaceView.Loading(
|
||||
space = space,
|
||||
icon = space.spaceIcon(
|
||||
builder = urlBuilder,
|
||||
spaceGradientProvider = SpaceGradientProvider.Default
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun createChatView(
|
||||
space: ObjectWrapper.SpaceView,
|
||||
chatPreview: Chat.Preview
|
||||
): VaultSpaceView.Chat {
|
||||
val creator = chatPreview.message?.creator ?: ""
|
||||
val messageText = chatPreview.message?.content?.text
|
||||
|
||||
val creatorName = if (creator.isNotEmpty()) {
|
||||
val creatorObj = chatPreview.dependencies.find {
|
||||
it.getSingleValue<String>(
|
||||
Relations.IDENTITY
|
||||
) == creator
|
||||
}
|
||||
creatorObj?.name ?: "Unknown"
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
val previewText = if (creatorName != null && messageText != null) {
|
||||
"$creatorName: $messageText"
|
||||
} else {
|
||||
messageText
|
||||
}
|
||||
|
||||
val messageTime = chatPreview.message?.createdAt?.let { timeInSeconds ->
|
||||
if (timeInSeconds > 0) {
|
||||
dateProvider.getChatPreviewDate(timeInSeconds = timeInSeconds)
|
||||
} else null
|
||||
}
|
||||
|
||||
return VaultSpaceView.Chat(
|
||||
space = space,
|
||||
icon = space.spaceIcon(
|
||||
builder = urlBuilder,
|
||||
spaceGradientProvider = SpaceGradientProvider.Default
|
||||
),
|
||||
chatPreview = chatPreview,
|
||||
previewText = previewText,
|
||||
creatorName = creatorName,
|
||||
messageText = messageText,
|
||||
messageTime = messageTime
|
||||
)
|
||||
}
|
||||
|
||||
private fun createStandardSpaceView(
|
||||
space: ObjectWrapper.SpaceView
|
||||
): VaultSpaceView.Space {
|
||||
return VaultSpaceView.Space(
|
||||
space = space,
|
||||
icon = space.spaceIcon(
|
||||
builder = urlBuilder,
|
||||
spaceGradientProvider = SpaceGradientProvider.Default
|
||||
),
|
||||
accessType = stringResourceProvider
|
||||
.getSpaceAccessTypeName(accessType = space.spaceAccessType)
|
||||
)
|
||||
}
|
||||
|
||||
fun onSpaceClicked(view: VaultSpaceView) {
|
||||
Timber.i("onSpaceClicked")
|
||||
viewModelScope.launch {
|
||||
|
@ -170,7 +247,7 @@ class VaultViewModel(
|
|||
|
||||
fun onSettingsClicked() {
|
||||
viewModelScope.launch {
|
||||
commands.emit(Command.OpenProfileSettings)
|
||||
commands.emit(VaultCommand.OpenProfileSettings)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -189,14 +266,14 @@ class VaultViewModel(
|
|||
fun onCreateSpaceClicked() {
|
||||
viewModelScope.launch {
|
||||
showChooseSpaceType.value = false
|
||||
commands.emit(Command.CreateNewSpace)
|
||||
commands.emit(VaultCommand.CreateNewSpace)
|
||||
}
|
||||
}
|
||||
|
||||
fun onCreateChatClicked() {
|
||||
viewModelScope.launch {
|
||||
viewModelScope.launch {
|
||||
showChooseSpaceType.value = false
|
||||
commands.emit(Command.CreateChat)
|
||||
commands.emit(VaultCommand.CreateChat)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -222,7 +299,7 @@ class VaultViewModel(
|
|||
when (deeplink) {
|
||||
is DeepLinkResolver.Action.Import.Experience -> {
|
||||
commands.emit(
|
||||
Command.Deeplink.GalleryInstallation(
|
||||
VaultCommand.Deeplink.GalleryInstallation(
|
||||
deepLinkType = deeplink.type,
|
||||
deepLinkSource = deeplink.source
|
||||
)
|
||||
|
@ -231,25 +308,27 @@ class VaultViewModel(
|
|||
|
||||
is DeepLinkResolver.Action.Invite -> {
|
||||
delay(1000)
|
||||
commands.emit(Command.Deeplink.Invite(deeplink.link))
|
||||
commands.emit(VaultCommand.Deeplink.Invite(deeplink.link))
|
||||
}
|
||||
|
||||
is DeepLinkResolver.Action.Unknown -> {
|
||||
if (BuildConfig.DEBUG) {
|
||||
sendToast("Could not resolve deeplink")
|
||||
//sendToast("Could not resolve deeplink")
|
||||
}
|
||||
}
|
||||
|
||||
is DeepLinkResolver.Action.DeepLinkToObject -> {
|
||||
onDeepLinkToObjectAwait(
|
||||
obj = deeplink.obj,
|
||||
space = deeplink.space,
|
||||
switchSpaceIfObjectFound = true
|
||||
).collect { result ->
|
||||
when(result) {
|
||||
when (result) {
|
||||
is DeepLinkToObjectDelegate.Result.Error -> {
|
||||
val link = deeplink.invite
|
||||
if (link != null) {
|
||||
commands.emit(
|
||||
Command.Deeplink.Invite(
|
||||
VaultCommand.Deeplink.Invite(
|
||||
link = spaceInviteResolver.createInviteLink(
|
||||
contentId = link.cid,
|
||||
encryptionKey = link.key
|
||||
|
@ -257,22 +336,25 @@ class VaultViewModel(
|
|||
)
|
||||
)
|
||||
} else {
|
||||
commands.emit(Command.Deeplink.DeepLinkToObjectNotWorking)
|
||||
commands.emit(VaultCommand.Deeplink.DeepLinkToObjectNotWorking)
|
||||
}
|
||||
}
|
||||
|
||||
is DeepLinkToObjectDelegate.Result.Success -> {
|
||||
proceedWithNavigation(result.obj.navigation())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is DeepLinkResolver.Action.DeepLinkToMembership -> {
|
||||
commands.emit(
|
||||
Command.Deeplink.MembershipScreen(
|
||||
VaultCommand.Deeplink.MembershipScreen(
|
||||
tierId = deeplink.tierId
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
else -> {
|
||||
Timber.d("No deep link")
|
||||
}
|
||||
|
@ -288,7 +370,7 @@ class VaultViewModel(
|
|||
delay(1000) // Simulate some delay
|
||||
pendingIntentStore.getDeepLinkInvite()?.let { deeplink ->
|
||||
Timber.d("Processing pending deeplink: $deeplink")
|
||||
commands.emit(Command.Deeplink.Invite(deeplink))
|
||||
commands.emit(VaultCommand.Deeplink.Invite(deeplink))
|
||||
pendingIntentStore.clearDeepLinkInvite()
|
||||
}
|
||||
}
|
||||
|
@ -306,16 +388,19 @@ class VaultViewModel(
|
|||
Timber.e(it, "Error while saving current space on vault screen")
|
||||
},
|
||||
onSuccess = {
|
||||
if (spaceUxType == SpaceUxType.CHAT && chat != null && ChatConfig.isChatAllowed(space = targetSpace)) {
|
||||
if (spaceUxType == SpaceUxType.CHAT && chat != null && ChatConfig.isChatAllowed(
|
||||
space = targetSpace
|
||||
)
|
||||
) {
|
||||
commands.emit(
|
||||
Command.EnterSpaceLevelChat(
|
||||
VaultCommand.EnterSpaceLevelChat(
|
||||
space = Space(targetSpace),
|
||||
chat = chat
|
||||
)
|
||||
)
|
||||
} else {
|
||||
commands.emit(
|
||||
Command.EnterSpaceHomeScreen(
|
||||
VaultCommand.EnterSpaceHomeScreen(
|
||||
space = Space(targetSpace)
|
||||
)
|
||||
)
|
||||
|
@ -325,137 +410,69 @@ class VaultViewModel(
|
|||
}
|
||||
|
||||
private fun proceedWithNavigation(navigation: OpenObjectNavigation) {
|
||||
when(navigation) {
|
||||
val nav = when (navigation) {
|
||||
is OpenObjectNavigation.OpenDataView -> {
|
||||
navigate(
|
||||
OpenSet(
|
||||
ctx = navigation.target,
|
||||
space = navigation.space,
|
||||
view = null
|
||||
)
|
||||
OpenSet(
|
||||
ctx = navigation.target,
|
||||
space = navigation.space,
|
||||
view = null
|
||||
)
|
||||
}
|
||||
|
||||
is OpenObjectNavigation.OpenEditor -> {
|
||||
navigate(
|
||||
OpenObject(
|
||||
ctx = navigation.target,
|
||||
space = navigation.space
|
||||
)
|
||||
|
||||
OpenObject(
|
||||
ctx = navigation.target,
|
||||
space = navigation.space
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
is OpenObjectNavigation.OpenChat -> {
|
||||
navigate(
|
||||
OpenChat(
|
||||
ctx = navigation.target,
|
||||
space = navigation.space
|
||||
)
|
||||
OpenChat(
|
||||
ctx = navigation.target,
|
||||
space = navigation.space
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
is OpenObjectNavigation.UnexpectedLayoutError -> {
|
||||
sendToast("Unexpected layout: ${navigation.layout}")
|
||||
VaultNavigation.ShowError("Unexpected layout: ${navigation.layout}")
|
||||
}
|
||||
|
||||
OpenObjectNavigation.NonValidObject -> {
|
||||
sendToast("Object id is missing")
|
||||
VaultNavigation.ShowError("Object id is missing")
|
||||
}
|
||||
|
||||
is OpenObjectNavigation.OpenDateObject -> {
|
||||
navigate(
|
||||
OpenDateObject(
|
||||
ctx = navigation.target,
|
||||
space = navigation.space
|
||||
)
|
||||
OpenDateObject(
|
||||
ctx = navigation.target,
|
||||
space = navigation.space
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
is OpenObjectNavigation.OpenParticipant -> {
|
||||
navigate(
|
||||
OpenParticipant(
|
||||
ctx = navigation.target,
|
||||
space = navigation.space
|
||||
)
|
||||
OpenParticipant(
|
||||
ctx = navigation.target,
|
||||
space = navigation.space
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
is OpenObjectNavigation.OpenType -> {
|
||||
navigate(
|
||||
OpenType(
|
||||
target = navigation.target,
|
||||
space = navigation.space
|
||||
)
|
||||
OpenType(
|
||||
target = navigation.target,
|
||||
space = navigation.space
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Factory @Inject constructor(
|
||||
private val spaceViewSubscriptionContainer: SpaceViewSubscriptionContainer,
|
||||
private val getSpaceWallpapers: GetSpaceWallpapers,
|
||||
private val urlBuilder: UrlBuilder,
|
||||
private val spaceManager: SpaceManager,
|
||||
private val saveCurrentSpace: SaveCurrentSpace,
|
||||
private val setVaultSpaceOrder: SetVaultSpaceOrder,
|
||||
private val observeVaultSettings: ObserveVaultSettings,
|
||||
private val analytics: Analytics,
|
||||
private val deepLinkToObjectDelegate: DeepLinkToObjectDelegate,
|
||||
private val appActionManager: AppActionManager,
|
||||
private val spaceInviteResolver: SpaceInviteResolver,
|
||||
private val profileContainer: ProfileSubscriptionManager,
|
||||
private val chatPreviewContainer: ChatPreviewContainer,
|
||||
private val pendingIntentStore: PendingIntentStore
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(
|
||||
modelClass: Class<T>
|
||||
) = VaultViewModel(
|
||||
spaceViewSubscriptionContainer = spaceViewSubscriptionContainer,
|
||||
getSpaceWallpapers = getSpaceWallpapers,
|
||||
urlBuilder = urlBuilder,
|
||||
spaceManager = spaceManager,
|
||||
saveCurrentSpace = saveCurrentSpace,
|
||||
setVaultSpaceOrder = setVaultSpaceOrder,
|
||||
observeVaultSettings = observeVaultSettings,
|
||||
analytics = analytics,
|
||||
deepLinkToObjectDelegate = deepLinkToObjectDelegate,
|
||||
appActionManager = appActionManager,
|
||||
spaceInviteResolver = spaceInviteResolver,
|
||||
profileContainer = profileContainer,
|
||||
chatPreviewContainer = chatPreviewContainer,
|
||||
pendingIntentStore = pendingIntentStore
|
||||
) as T
|
||||
}
|
||||
|
||||
data class VaultSpaceView(
|
||||
val space: ObjectWrapper.SpaceView,
|
||||
val icon: SpaceIconView,
|
||||
val wallpaper: Wallpaper = Wallpaper.Default,
|
||||
val unreadMessageCount: Int = 0,
|
||||
val unreadMentionCount: Int = 0,
|
||||
)
|
||||
|
||||
sealed class Command {
|
||||
data class EnterSpaceHomeScreen(val space: Space): Command()
|
||||
data class EnterSpaceLevelChat(val space: Space, val chat: Id): Command()
|
||||
data object CreateNewSpace: Command()
|
||||
data object CreateChat: Command()
|
||||
data object OpenProfileSettings: Command()
|
||||
|
||||
sealed class Deeplink : Command() {
|
||||
data object DeepLinkToObjectNotWorking: Deeplink()
|
||||
data class Invite(val link: String) : Deeplink()
|
||||
data class GalleryInstallation(
|
||||
val deepLinkType: String,
|
||||
val deepLinkSource: String
|
||||
) : Deeplink()
|
||||
data class MembershipScreen(val tierId: String?) : Deeplink()
|
||||
viewModelScope.launch {
|
||||
Timber.d("Proceeding with navigation: $nav")
|
||||
navigations.emit(nav)
|
||||
}
|
||||
}
|
||||
|
||||
sealed class Navigation {
|
||||
data class OpenChat(val ctx: Id, val space: Id) : Navigation()
|
||||
data class OpenObject(val ctx: Id, val space: Id) : Navigation()
|
||||
data class OpenSet(val ctx: Id, val space: Id, val view: Id?) : Navigation()
|
||||
data class OpenDateObject(val ctx: Id, val space: Id) : Navigation()
|
||||
data class OpenParticipant(val ctx: Id, val space: Id) : Navigation()
|
||||
data class OpenType(val target: Id, val space: Id) : Navigation()
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val SPACE_VAULT_DEBOUNCE_DURATION = 300L
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue