diff --git a/core/ui/src/main/res/values-ru/strings.xml b/core/ui/src/main/res/values-ru/strings.xml
index 9a99ec89..37cf84d2 100644
--- a/core/ui/src/main/res/values-ru/strings.xml
+++ b/core/ui/src/main/res/values-ru/strings.xml
@@ -286,4 +286,7 @@
Н
Д
Сейчас
+
+ Вы действительно хотите создать чат «%s»?
+ Вы действительно хотите создать чат «%s» только с собой?
diff --git a/core/ui/src/main/res/values/strings.xml b/core/ui/src/main/res/values/strings.xml
index 6b824562..c25e95db 100644
--- a/core/ui/src/main/res/values/strings.xml
+++ b/core/ui/src/main/res/values/strings.xml
@@ -363,4 +363,7 @@
W
D
Now
+
+ Are you sure you want to create chat «%s»?
+ Are you sure you want to create chat «%s» only with yourself?
diff --git a/feature/createchat/src/main/kotlin/dev/meloda/fast/conversations/CreateChatViewModel.kt b/feature/createchat/src/main/kotlin/dev/meloda/fast/conversations/CreateChatViewModel.kt
index b4a43fde..6692cc60 100644
--- a/feature/createchat/src/main/kotlin/dev/meloda/fast/conversations/CreateChatViewModel.kt
+++ b/feature/createchat/src/main/kotlin/dev/meloda/fast/conversations/CreateChatViewModel.kt
@@ -17,74 +17,67 @@ import dev.meloda.fast.domain.GetLocalUserByIdUseCase
import dev.meloda.fast.domain.MessagesUseCase
import dev.meloda.fast.domain.util.asPresentation
import dev.meloda.fast.model.BaseError
+import dev.meloda.fast.model.api.domain.VkUser
import dev.meloda.fast.network.VkErrorCode
import dev.meloda.fast.ui.model.api.UiFriend
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
-interface CreateChatViewModel {
-
- val screenState: StateFlow
- val baseError: StateFlow
- val currentOffset: StateFlow
- val canPaginate: StateFlow
-
- val isChatCreated: StateFlow
-
- fun onPaginationConditionsMet()
- fun onRefresh()
- fun onErrorConsumed()
-
- fun toggleFriendSelection(userId: Long)
-
- fun onTitleTextInputChanged(newTitle: String)
-
- fun onCreateChatButtonClicked()
-
- fun onNavigatedBack()
-}
-
-class CreateChatViewModelImpl(
+class CreateChatViewModel(
private val friendsUseCase: FriendsUseCase,
private val messagesUseCase: MessagesUseCase,
private val imageLoader: ImageLoader,
private val applicationContext: Context,
private val getLocalUserByIdUseCase: GetLocalUserByIdUseCase,
private val userSettings: UserSettings
-) : CreateChatViewModel, ViewModel() {
+) : ViewModel() {
- override val screenState = MutableStateFlow(CreateChatScreenState.EMPTY)
- override val baseError = MutableStateFlow(null)
- override val currentOffset = MutableStateFlow(0)
- override val canPaginate = MutableStateFlow(false)
+ private val _screenState = MutableStateFlow(CreateChatScreenState.EMPTY)
+ val screenState: StateFlow = _screenState.asStateFlow()
- override val isChatCreated = MutableStateFlow(null)
+ private val _baseError = MutableStateFlow(null)
+ val baseError: StateFlow = _baseError.asStateFlow()
+
+ private val currentOffset = MutableStateFlow(0)
+
+ private val _canPaginate = MutableStateFlow(false)
+ val canPaginate: StateFlow = _canPaginate.asStateFlow()
+
+ private val _isChatCreated = MutableStateFlow(null)
+ val isChatCreated: StateFlow = _isChatCreated.asStateFlow()
+
+ private val _finalChatTitle = MutableStateFlow("")
+ val finalChatTitle: StateFlow = _finalChatTitle.asStateFlow()
private val useContactNames: Boolean = userSettings.useContactNames.value
+ private var accountUser: VkUser? = null
+
init {
- loadFriends()
+ fetchAccountUser()
+ fetchUsers()
}
- override fun onPaginationConditionsMet() {
+ fun onPaginationConditionsMet() {
currentOffset.update { screenState.value.friends.size }
- loadFriends()
+ fetchUsers()
}
- override fun onRefresh() {
+ fun onRefresh() {
onErrorConsumed()
- loadFriends(offset = 0)
+ fetchUsers(offset = 0)
}
- override fun onErrorConsumed() {
- baseError.setValue { null }
+ fun onErrorConsumed() {
+ _baseError.setValue { null }
}
- override fun toggleFriendSelection(userId: Long) {
+ fun toggleFriendSelection(userId: Long) {
val newSelectionList = screenState.value.selectedFriendsIds.toMutableList()
if (newSelectionList.contains(userId)) {
@@ -93,26 +86,69 @@ class CreateChatViewModelImpl(
newSelectionList.add(userId)
}
- screenState.setValue { old ->
+ _screenState.setValue { old ->
old.copy(selectedFriendsIds = newSelectionList)
}
+
+ refreshFinalTitle()
+ }
+
+ fun onTitleTextInputChanged(newTitle: String) {
+ _screenState.setValue { old -> old.copy(chatTitle = newTitle) }
+
+ refreshFinalTitle()
+ }
+
+ fun onCreateChatButtonClicked() {
+ _screenState.setValue { old -> old.copy(showConfirmDialog = true) }
+ }
+
+ fun onNavigatedBack() {
+ viewModelScope.launch(Dispatchers.Main) {
+ _isChatCreated.emit(null)
+ }
}
- override fun onTitleTextInputChanged(newTitle: String) {
- screenState.setValue { old -> old.copy(chatTitle = newTitle) }
+ fun onConfirmDialogDismissed() {
+ _screenState.setValue { old -> old.copy(showConfirmDialog = false) }
}
- override fun onCreateChatButtonClicked() {
+ fun onConfirmDialogConfirmed() {
+ _screenState.setValue { old -> old.copy(showConfirmDialog = false) }
createChat()
}
- override fun onNavigatedBack() {
- viewModelScope.launch(Dispatchers.Main) {
- isChatCreated.emit(null)
+ private fun fetchAccountUser() {
+ viewModelScope.launch {
+ accountUser = getLocalUserByIdUseCase.proceed(UserConfig.userId)
+ if (accountUser != null) {
+ _finalChatTitle.setValue { accountUser?.firstName.orEmpty() }
+ }
}
}
- private fun loadFriends(
+ private fun refreshFinalTitle() {
+ if (screenState.value.chatTitle.trim().isNotEmpty()) {
+ _finalChatTitle.setValue { screenState.value.chatTitle.trim() }
+ } else {
+ val accountAsFriend = accountUser?.asPresentation(useContactNames)
+
+ val accountList = accountAsFriend?.let(::listOf) ?: emptyList()
+
+ val selectedFriends = screenState.value.selectedFriendsIds
+ .take(3)
+ .takeIf { it.isNotEmpty() }
+ ?.mapNotNull { userId -> screenState.value.friends.find { it.userId == userId } }
+
+ val finalTitle =
+ (accountList + selectedFriends.orEmpty()).joinToString(transform = UiFriend::firstName)
+ .plus(if (screenState.value.selectedFriendsIds.size > 3) ", ..." else "")
+
+ _finalChatTitle.setValue { finalTitle }
+ }
+ }
+
+ private fun fetchUsers(
offset: Int = currentOffset.value
) {
friendsUseCase.getFriends(count = LOAD_COUNT, offset = offset)
@@ -121,13 +157,13 @@ class CreateChatViewModelImpl(
error = ::handleError,
success = { response ->
val itemsCountSufficient = response.size == LOAD_COUNT
- canPaginate.setValue { itemsCountSufficient }
+ _canPaginate.setValue { itemsCountSufficient }
val paginationExhausted = !itemsCountSufficient &&
screenState.value.friends.isNotEmpty()
val imagesToPreload =
- response.mapNotNull { it.photo100.takeIf { !it.isNullOrEmpty() } }
+ response.mapNotNull { it.photo100.takeIf { p -> !p.isNullOrEmpty() } }
imagesToPreload.forEach { url ->
imageLoader.enqueue(
@@ -147,11 +183,11 @@ class CreateChatViewModelImpl(
isPaginationExhausted = paginationExhausted
)
if (offset == 0) {
- screenState.setValue {
+ _screenState.setValue {
newState.copy(friends = loadedFriends)
}
} else {
- screenState.setValue {
+ _screenState.setValue {
newState.copy(
friends = newState.friends.plus(loadedFriends)
)
@@ -160,7 +196,7 @@ class CreateChatViewModelImpl(
}
)
- screenState.setValue { old ->
+ _screenState.setValue { old ->
old.copy(
isLoading = offset == 0 && state.isLoading(),
isPaginating = offset > 0 && state.isLoading()
@@ -171,27 +207,19 @@ class CreateChatViewModelImpl(
private fun createChat() {
viewModelScope.launch {
- val title = screenState.value.chatTitle.takeUnless(String::isBlank)
-
- val accountAsFriend =
- getLocalUserByIdUseCase.proceed(UserConfig.userId)?.asPresentation(useContactNames)
-
- val accountList = accountAsFriend?.let(::listOf) ?: emptyList()
-
val selectedFriends = screenState.value.selectedFriendsIds
.takeIf { it.isNotEmpty() }
?.mapNotNull { userId -> screenState.value.friends.find { it.userId == userId } }
messagesUseCase.createChat(
userIds = selectedFriends?.map { it.userId },
- title = title
- ?: (accountList + selectedFriends.orEmpty()).joinToString(transform = UiFriend::firstName)
+ title = finalChatTitle.value
).listenValue(viewModelScope) { state ->
state.processState(
error = ::handleError,
success = { response ->
withContext(Dispatchers.Main) {
- isChatCreated.emit(2_000_000_000 + response)
+ _isChatCreated.emit(2_000_000_000 + response)
}
}
)
@@ -204,11 +232,11 @@ class CreateChatViewModelImpl(
is State.Error.ApiError -> {
when (error.errorCode) {
VkErrorCode.USER_AUTHORIZATION_FAILED -> {
- baseError.setValue { BaseError.SessionExpired }
+ _baseError.setValue { BaseError.SessionExpired }
}
else -> {
- baseError.setValue {
+ _baseError.setValue {
BaseError.SimpleError(message = error.errorMessage)
}
}
@@ -216,19 +244,19 @@ class CreateChatViewModelImpl(
}
State.Error.ConnectionError -> {
- baseError.setValue {
+ _baseError.setValue {
BaseError.SimpleError(message = "Connection error")
}
}
State.Error.InternalError -> {
- baseError.setValue {
+ _baseError.setValue {
BaseError.SimpleError(message = "Internal error")
}
}
State.Error.UnknownError -> {
- baseError.setValue {
+ _baseError.setValue {
BaseError.SimpleError(message = "Unknown error")
}
}
diff --git a/feature/createchat/src/main/kotlin/dev/meloda/fast/conversations/di/CreateChatModule.kt b/feature/createchat/src/main/kotlin/dev/meloda/fast/conversations/di/CreateChatModule.kt
index 2b9ec1b5..e57b7382 100644
--- a/feature/createchat/src/main/kotlin/dev/meloda/fast/conversations/di/CreateChatModule.kt
+++ b/feature/createchat/src/main/kotlin/dev/meloda/fast/conversations/di/CreateChatModule.kt
@@ -1,9 +1,9 @@
package dev.meloda.fast.conversations.di
-import dev.meloda.fast.conversations.CreateChatViewModelImpl
+import dev.meloda.fast.conversations.CreateChatViewModel
import org.koin.core.module.dsl.viewModelOf
import org.koin.dsl.module
val createChatModule = module {
- viewModelOf(::CreateChatViewModelImpl)
+ viewModelOf(::CreateChatViewModel)
}
diff --git a/feature/createchat/src/main/kotlin/dev/meloda/fast/conversations/model/CreateChatScreenState.kt b/feature/createchat/src/main/kotlin/dev/meloda/fast/conversations/model/CreateChatScreenState.kt
index 10a6591c..878ef734 100644
--- a/feature/createchat/src/main/kotlin/dev/meloda/fast/conversations/model/CreateChatScreenState.kt
+++ b/feature/createchat/src/main/kotlin/dev/meloda/fast/conversations/model/CreateChatScreenState.kt
@@ -10,7 +10,8 @@ data class CreateChatScreenState(
val isPaginationExhausted: Boolean,
val friends: List,
val selectedFriendsIds: List,
- val chatTitle: String
+ val chatTitle: String,
+ val showConfirmDialog: Boolean
) {
companion object {
val EMPTY: CreateChatScreenState = CreateChatScreenState(
@@ -19,7 +20,8 @@ data class CreateChatScreenState(
isPaginationExhausted = false,
friends = emptyList(),
selectedFriendsIds = emptyList(),
- chatTitle = ""
+ chatTitle = "",
+ showConfirmDialog = false
)
}
}
diff --git a/feature/createchat/src/main/kotlin/dev/meloda/fast/conversations/navigation/CreateChatNavigation.kt b/feature/createchat/src/main/kotlin/dev/meloda/fast/conversations/navigation/CreateChatNavigation.kt
index 81ef6ef0..2ea96c69 100644
--- a/feature/createchat/src/main/kotlin/dev/meloda/fast/conversations/navigation/CreateChatNavigation.kt
+++ b/feature/createchat/src/main/kotlin/dev/meloda/fast/conversations/navigation/CreateChatNavigation.kt
@@ -6,7 +6,6 @@ import androidx.navigation.NavController
import androidx.navigation.NavGraphBuilder
import androidx.navigation.compose.composable
import dev.meloda.fast.conversations.CreateChatViewModel
-import dev.meloda.fast.conversations.CreateChatViewModelImpl
import dev.meloda.fast.conversations.presentation.CreateChatRoute
import kotlinx.serialization.Serializable
import org.koin.compose.viewmodel.koinViewModel
@@ -20,7 +19,7 @@ fun NavGraphBuilder.createChatScreen(
) {
composable {
val context = LocalContext.current
- val viewModel: CreateChatViewModel = koinViewModel(
+ val viewModel: CreateChatViewModel = koinViewModel(
viewModelStoreOwner = context as AppCompatActivity
)
diff --git a/feature/createchat/src/main/kotlin/dev/meloda/fast/conversations/presentation/CreateChatScreen.kt b/feature/createchat/src/main/kotlin/dev/meloda/fast/conversations/presentation/CreateChatScreen.kt
index 9906e058..931dc713 100644
--- a/feature/createchat/src/main/kotlin/dev/meloda/fast/conversations/presentation/CreateChatScreen.kt
+++ b/feature/createchat/src/main/kotlin/dev/meloda/fast/conversations/presentation/CreateChatScreen.kt
@@ -64,6 +64,7 @@ import dev.meloda.fast.model.BaseError
import dev.meloda.fast.ui.R
import dev.meloda.fast.ui.components.FullScreenContainedLoader
import dev.meloda.fast.ui.components.IconButton
+import dev.meloda.fast.ui.components.MaterialDialog
import dev.meloda.fast.ui.components.NoItemsView
import dev.meloda.fast.ui.components.VkErrorView
import dev.meloda.fast.ui.theme.LocalHazeState
@@ -89,6 +90,27 @@ fun CreateChatRoute(
}
}
+ if (screenState.showConfirmDialog) {
+ MaterialDialog(
+ onDismissRequest = viewModel::onConfirmDialogDismissed,
+ title = stringResource(R.string.confirm),
+ text = when {
+ screenState.selectedFriendsIds.isEmpty() -> stringResource(
+ R.string.confirm_chat_create_empty_with_title,
+ viewModel.finalChatTitle.value
+ )
+
+ else -> stringResource(
+ R.string.confirm_chat_create_with_title,
+ viewModel.finalChatTitle.value
+ )
+ },
+ confirmAction = viewModel::onConfirmDialogConfirmed,
+ confirmText = stringResource(R.string.action_create),
+ cancelText = stringResource(R.string.cancel)
+ )
+ }
+
CreateChatScreen(
screenState = screenState,
baseError = baseError,