forked from melod1n/fast-messenger
twoFa -> validation naming; fixes for preview for screens (separating view model from ui); some improvements & fixes
This commit is contained in:
+21
-65
@@ -4,6 +4,7 @@ import android.content.SharedPreferences
|
||||
import android.content.res.Resources
|
||||
import android.util.Log
|
||||
import androidx.core.content.edit
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.conena.nanokt.collections.indexOfFirstOrNull
|
||||
@@ -20,11 +21,10 @@ import com.meloda.app.fast.data.api.messages.MessagesUseCase
|
||||
import com.meloda.app.fast.data.processState
|
||||
import com.meloda.app.fast.datastore.SettingsKeys
|
||||
import com.meloda.app.fast.messageshistory.model.ActionMode
|
||||
import com.meloda.app.fast.messageshistory.model.MessagesHistoryArguments
|
||||
import com.meloda.app.fast.messageshistory.model.MessagesHistoryScreenState
|
||||
import com.meloda.app.fast.messageshistory.navigation.MessagesHistory
|
||||
import com.meloda.app.fast.messageshistory.util.asPresentation
|
||||
import com.meloda.app.fast.messageshistory.util.extractAvatar
|
||||
import com.meloda.app.fast.messageshistory.util.extractShowName
|
||||
import com.meloda.app.fast.messageshistory.util.extractTitle
|
||||
import com.meloda.app.fast.model.BaseError
|
||||
import com.meloda.app.fast.model.LongPollEvent
|
||||
@@ -48,17 +48,14 @@ interface MessagesHistoryViewModel {
|
||||
|
||||
val canPaginate: StateFlow<Boolean>
|
||||
|
||||
fun onRefresh()
|
||||
fun onAttachmentButtonClicked()
|
||||
fun onInputChanged(newText: String)
|
||||
fun onMessageInputChanged(newText: String)
|
||||
fun onEmojiButtonClicked()
|
||||
fun onActionButtonClicked()
|
||||
fun onTopAppBarMenuClicked(id: Int)
|
||||
fun setArguments(arguments: MessagesHistoryArguments)
|
||||
|
||||
fun onMetPaginationCondition()
|
||||
fun onShowDatesClicked(showDates: Boolean)
|
||||
fun onShowNamesClicked(showNames: Boolean)
|
||||
fun onEnableAnimationsClicked(enableAnimations: Boolean)
|
||||
fun onPaginationConditionsMet()
|
||||
fun onToggleAnimationsDropdownItemClicked(enableAnimations: Boolean)
|
||||
}
|
||||
|
||||
class MessagesHistoryViewModelImpl(
|
||||
@@ -67,6 +64,7 @@ class MessagesHistoryViewModelImpl(
|
||||
private val preferences: SharedPreferences,
|
||||
private val resources: Resources,
|
||||
updatesParser: LongPollUpdatesParser,
|
||||
savedStateHandle: SavedStateHandle
|
||||
) : MessagesHistoryViewModel, ViewModel() {
|
||||
|
||||
override val screenState = MutableStateFlow(MessagesHistoryScreenState.EMPTY)
|
||||
@@ -85,17 +83,26 @@ class MessagesHistoryViewModelImpl(
|
||||
private val sendingMessages: MutableList<VkMessage> = mutableListOf()
|
||||
|
||||
init {
|
||||
val arguments = MessagesHistory.from(savedStateHandle).arguments
|
||||
|
||||
screenState.setValue { old -> old.copy(conversationId = arguments.conversationId) }
|
||||
loadMessagesHistory()
|
||||
|
||||
updatesParser.onNewMessage(::handleNewMessage)
|
||||
updatesParser.onMessageEdited(::handleEditedMessage)
|
||||
updatesParser.onMessageIncomingRead(::handleReadIncomingEvent)
|
||||
updatesParser.onMessageOutgoingRead(::handleReadOutgoingEvent)
|
||||
}
|
||||
|
||||
override fun onRefresh() {
|
||||
loadMessagesHistory(offset = 0)
|
||||
}
|
||||
|
||||
override fun onAttachmentButtonClicked() {
|
||||
|
||||
}
|
||||
|
||||
override fun onInputChanged(newText: String) {
|
||||
override fun onMessageInputChanged(newText: String) {
|
||||
screenState.setValue { old ->
|
||||
old.copy(
|
||||
message = newText,
|
||||
@@ -131,58 +138,12 @@ class MessagesHistoryViewModelImpl(
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTopAppBarMenuClicked(id: Int) {
|
||||
when (id) {
|
||||
0 -> loadMessagesHistory(0)
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
override fun setArguments(arguments: MessagesHistoryArguments) {
|
||||
if (arguments.conversationId == screenState.value.conversationId) return
|
||||
|
||||
screenState.setValue { old -> old.copy(conversationId = arguments.conversationId) }
|
||||
loadMessagesHistory()
|
||||
}
|
||||
|
||||
override fun onMetPaginationCondition() {
|
||||
override fun onPaginationConditionsMet() {
|
||||
currentOffset.update { screenState.value.messages.size }
|
||||
loadMessagesHistory()
|
||||
}
|
||||
|
||||
override fun onShowDatesClicked(showDates: Boolean) {
|
||||
preferences.edit { putBoolean(SettingsKeys.KEY_SHOW_DATE_UNDER_BUBBLES, showDates) }
|
||||
|
||||
screenState.setValue { old ->
|
||||
old.copy(
|
||||
messages = old.messages.map { message ->
|
||||
message.copy(showDate = showDates)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onShowNamesClicked(showNames: Boolean) {
|
||||
preferences.edit { putBoolean(SettingsKeys.KEY_SHOW_NAME_IN_BUBBLES, showNames) }
|
||||
|
||||
screenState.setValue { old ->
|
||||
old.copy(
|
||||
messages = old.messages.map { message ->
|
||||
message.copy(
|
||||
showName = if (showNames) {
|
||||
val index = messages.value.indexOfFirst { it.id == message.id }
|
||||
val domainMessage = messages.value[index]
|
||||
val prevMessage = messages.value.getOrNull(index + 1)
|
||||
|
||||
domainMessage.extractShowName(prevMessage)
|
||||
} else false
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onEnableAnimationsClicked(enableAnimations: Boolean) {
|
||||
override fun onToggleAnimationsDropdownItemClicked(enableAnimations: Boolean) {
|
||||
preferences.edit {
|
||||
putBoolean(
|
||||
SettingsKeys.KEY_ENABLE_ANIMATIONS_IN_MESSAGES,
|
||||
@@ -285,15 +246,10 @@ class MessagesHistoryViewModelImpl(
|
||||
messagesUseCase.storeMessages(messages)
|
||||
conversationsUseCase.storeConversations(conversations)
|
||||
|
||||
val showDate =
|
||||
preferences.getBoolean(SettingsKeys.KEY_SHOW_DATE_UNDER_BUBBLES, false)
|
||||
val showName =
|
||||
preferences.getBoolean(SettingsKeys.KEY_SHOW_NAME_IN_BUBBLES, false)
|
||||
|
||||
val loadedMessages = fullMessages.mapIndexed { index, message ->
|
||||
message.asPresentation(
|
||||
showDate = showDate,
|
||||
showName = showName,
|
||||
showDate = false,
|
||||
showName = false,
|
||||
prevMessage = messages.getOrNull(index + 1),
|
||||
nextMessage = messages.getOrNull(index - 1),
|
||||
)
|
||||
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
package com.meloda.app.fast.messageshistory.navigation
|
||||
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.toRoute
|
||||
import com.meloda.app.fast.common.customNavType
|
||||
import com.meloda.app.fast.messageshistory.model.MessagesHistoryArguments
|
||||
import com.meloda.app.fast.messageshistory.presentation.MessagesHistoryRoute
|
||||
import com.meloda.app.fast.model.BaseError
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlin.reflect.typeOf
|
||||
|
||||
@Serializable
|
||||
data class MessagesHistory(val arguments: MessagesHistoryArguments) {
|
||||
|
||||
companion object {
|
||||
val typeMap =
|
||||
mapOf(typeOf<MessagesHistoryArguments>() to customNavType<MessagesHistoryArguments>())
|
||||
|
||||
fun from(savedStateHandle: SavedStateHandle) =
|
||||
savedStateHandle.toRoute<MessagesHistory>(typeMap)
|
||||
}
|
||||
}
|
||||
|
||||
fun NavGraphBuilder.messagesHistoryScreen(
|
||||
onError: (BaseError) -> Unit,
|
||||
onBack: () -> Unit,
|
||||
onChatMaterialsDropdownItemClicked: (peerId: Int, conversationMessageId: Int) -> Unit
|
||||
) {
|
||||
composable<MessagesHistory>(typeMap = MessagesHistory.typeMap) {
|
||||
MessagesHistoryRoute(
|
||||
onError = onError,
|
||||
onBack = onBack,
|
||||
onChatMaterialsDropdownItemClicked = onChatMaterialsDropdownItemClicked,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun NavController.navigateToMessagesHistory(conversationId: Int) {
|
||||
this.navigate(MessagesHistory(MessagesHistoryArguments(conversationId)))
|
||||
}
|
||||
-64
@@ -1,64 +0,0 @@
|
||||
package com.meloda.app.fast.messageshistory.navigation
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.core.os.BundleCompat
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavType
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.toRoute
|
||||
import com.meloda.app.fast.messageshistory.MessagesHistoryViewModel
|
||||
import com.meloda.app.fast.messageshistory.MessagesHistoryViewModelImpl
|
||||
import com.meloda.app.fast.messageshistory.model.MessagesHistoryArguments
|
||||
import com.meloda.app.fast.messageshistory.presentation.MessagesHistoryScreen
|
||||
import com.meloda.app.fast.model.BaseError
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.koin.androidx.compose.koinViewModel
|
||||
import kotlin.reflect.typeOf
|
||||
|
||||
@Serializable
|
||||
data class MessagesHistory(val arguments: MessagesHistoryArguments)
|
||||
|
||||
val MessagesHistoryNavType = object : NavType<MessagesHistoryArguments>(isNullableAllowed = false) {
|
||||
override fun get(bundle: Bundle, key: String): MessagesHistoryArguments? =
|
||||
BundleCompat.getParcelable(bundle, key, MessagesHistoryArguments::class.java)
|
||||
|
||||
override fun parseValue(value: String): MessagesHistoryArguments = Json.decodeFromString(value)
|
||||
|
||||
override fun serializeAsValue(value: MessagesHistoryArguments): String =
|
||||
Json.encodeToString(value)
|
||||
|
||||
override fun put(bundle: Bundle, key: String, value: MessagesHistoryArguments) {
|
||||
bundle.putParcelable(key, value)
|
||||
}
|
||||
|
||||
override val name: String = "MessagesHistoryArguments"
|
||||
}
|
||||
|
||||
fun NavGraphBuilder.messagesHistoryRoute(
|
||||
onError: (BaseError) -> Unit,
|
||||
onBack: () -> Unit,
|
||||
onNavigateToChatAttachments: (peerId: Int, conversationMessageId: Int) -> Unit
|
||||
) {
|
||||
composable<MessagesHistory>(
|
||||
typeMap = mapOf(typeOf<MessagesHistoryArguments>() to MessagesHistoryNavType)
|
||||
) { backStackEntry ->
|
||||
val arguments: MessagesHistory = backStackEntry.toRoute()
|
||||
|
||||
val viewModel: MessagesHistoryViewModel = koinViewModel<MessagesHistoryViewModelImpl>()
|
||||
viewModel.setArguments(arguments.arguments)
|
||||
|
||||
MessagesHistoryScreen(
|
||||
onError = onError,
|
||||
onBack = onBack,
|
||||
onNavigateToChatMaterials = onNavigateToChatAttachments,
|
||||
viewModel = viewModel
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun NavController.navigateToMessagesHistory(conversationId: Int) {
|
||||
this.navigate(MessagesHistory(MessagesHistoryArguments(conversationId)))
|
||||
}
|
||||
+49
-58
@@ -71,6 +71,7 @@ import com.meloda.app.fast.designsystem.LocalTheme
|
||||
import com.meloda.app.fast.messageshistory.MessagesHistoryViewModel
|
||||
import com.meloda.app.fast.messageshistory.MessagesHistoryViewModelImpl
|
||||
import com.meloda.app.fast.messageshistory.model.ActionMode
|
||||
import com.meloda.app.fast.messageshistory.model.MessagesHistoryScreenState
|
||||
import com.meloda.app.fast.model.BaseError
|
||||
import dev.chrisbanes.haze.HazeState
|
||||
import dev.chrisbanes.haze.hazeChild
|
||||
@@ -81,6 +82,32 @@ import org.koin.androidx.compose.koinViewModel
|
||||
import org.koin.compose.koinInject
|
||||
import com.meloda.app.fast.designsystem.R as UiR
|
||||
|
||||
@Composable
|
||||
fun MessagesHistoryRoute(
|
||||
onError: (BaseError) -> Unit,
|
||||
onBack: () -> Unit,
|
||||
onChatMaterialsDropdownItemClicked: (peerId: Int, conversationMessageId: Int) -> Unit,
|
||||
viewModel: MessagesHistoryViewModel = koinViewModel<MessagesHistoryViewModelImpl>()
|
||||
) {
|
||||
val screenState by viewModel.screenState.collectAsStateWithLifecycle()
|
||||
val baseError by viewModel.baseError.collectAsStateWithLifecycle()
|
||||
val canPaginate by viewModel.canPaginate.collectAsStateWithLifecycle()
|
||||
|
||||
MessagesHistoryScreen(
|
||||
screenState = screenState,
|
||||
baseError = baseError,
|
||||
canPaginate = canPaginate,
|
||||
onBack = onBack,
|
||||
onChatMaterialsDropdownItemClicked = onChatMaterialsDropdownItemClicked,
|
||||
onRefreshDropdownItemClicked = viewModel::onRefresh,
|
||||
onToggleAnimationsDropdownItemClicked = viewModel::onToggleAnimationsDropdownItemClicked,
|
||||
onPaginationConditionsMet = viewModel::onPaginationConditionsMet,
|
||||
onMessageInputChanged = viewModel::onMessageInputChanged,
|
||||
onAttachmentButtonClicked = viewModel::onAttachmentButtonClicked,
|
||||
onActionButtonClicked = viewModel::onActionButtonClicked
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(
|
||||
ExperimentalMaterial3Api::class,
|
||||
ExperimentalHazeMaterialsApi::class,
|
||||
@@ -88,21 +115,23 @@ import com.meloda.app.fast.designsystem.R as UiR
|
||||
)
|
||||
@Composable
|
||||
fun MessagesHistoryScreen(
|
||||
onError: (BaseError) -> Unit,
|
||||
onBack: () -> Unit,
|
||||
onNavigateToChatMaterials: (peerId: Int, conversationMessageId: Int) -> Unit,
|
||||
viewModel: MessagesHistoryViewModel = koinViewModel<MessagesHistoryViewModelImpl>()
|
||||
screenState: MessagesHistoryScreenState = MessagesHistoryScreenState.EMPTY,
|
||||
baseError: BaseError? = null,
|
||||
canPaginate: Boolean = false,
|
||||
onBack: () -> Unit = {},
|
||||
onChatMaterialsDropdownItemClicked: (peerId: Int, conversationMessageId: Int) -> Unit = { _, _ -> },
|
||||
onRefreshDropdownItemClicked: () -> Unit = {},
|
||||
onToggleAnimationsDropdownItemClicked: (Boolean) -> Unit = {},
|
||||
onPaginationConditionsMet: () -> Unit = {},
|
||||
onMessageInputChanged: (String) -> Unit = {},
|
||||
onAttachmentButtonClicked: () -> Unit = {},
|
||||
onActionButtonClicked: () -> Unit = {}
|
||||
) {
|
||||
val view = LocalView.current
|
||||
|
||||
val preferences: SharedPreferences = koinInject()
|
||||
val currentTheme = LocalTheme.current
|
||||
|
||||
val screenState by viewModel.screenState.collectAsStateWithLifecycle()
|
||||
val canPaginate by viewModel.canPaginate.collectAsStateWithLifecycle()
|
||||
|
||||
val messages = screenState.messages
|
||||
|
||||
val listState = rememberLazyListState()
|
||||
|
||||
val paginationConditionMet by remember {
|
||||
@@ -115,7 +144,7 @@ fun MessagesHistoryScreen(
|
||||
|
||||
LaunchedEffect(paginationConditionMet) {
|
||||
if (paginationConditionMet && !screenState.isPaginating) {
|
||||
viewModel.onMetPaginationCondition()
|
||||
onPaginationConditionsMet()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,24 +154,6 @@ fun MessagesHistoryScreen(
|
||||
|
||||
val hazeSate = remember { HazeState() }
|
||||
|
||||
var datesShown by remember {
|
||||
mutableStateOf(
|
||||
preferences.getBoolean(
|
||||
SettingsKeys.KEY_SHOW_DATE_UNDER_BUBBLES,
|
||||
false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
var namesShown by remember {
|
||||
mutableStateOf(
|
||||
preferences.getBoolean(
|
||||
SettingsKeys.KEY_SHOW_NAME_IN_BUBBLES,
|
||||
false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
var animationsEnabled by remember {
|
||||
mutableStateOf(
|
||||
preferences.getBoolean(
|
||||
@@ -217,7 +228,8 @@ fun MessagesHistoryScreen(
|
||||
dropDownMenuExpanded = false
|
||||
|
||||
// TODO: 11/07/2024, Danil Nikolaev: to VM
|
||||
onNavigateToChatMaterials(
|
||||
|
||||
onChatMaterialsDropdownItemClicked(
|
||||
screenState.conversationId,
|
||||
screenState.messages.first().conversationMessageId
|
||||
)
|
||||
@@ -228,7 +240,7 @@ fun MessagesHistoryScreen(
|
||||
)
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
viewModel.onTopAppBarMenuClicked(0)
|
||||
onRefreshDropdownItemClicked()
|
||||
dropDownMenuExpanded = false
|
||||
},
|
||||
text = {
|
||||
@@ -248,27 +260,6 @@ fun MessagesHistoryScreen(
|
||||
)
|
||||
) {
|
||||
HorizontalDivider()
|
||||
DropdownMenuItem(
|
||||
text = {
|
||||
Text(text = if (datesShown) "Hide dates" else "Show dates")
|
||||
},
|
||||
onClick = {
|
||||
dropDownMenuExpanded = false
|
||||
datesShown = !datesShown
|
||||
viewModel.onShowDatesClicked(datesShown)
|
||||
}
|
||||
)
|
||||
|
||||
DropdownMenuItem(
|
||||
text = {
|
||||
Text(text = if (namesShown) "Hide names" else "Show names")
|
||||
},
|
||||
onClick = {
|
||||
dropDownMenuExpanded = false
|
||||
namesShown = !namesShown
|
||||
viewModel.onShowNamesClicked(namesShown)
|
||||
}
|
||||
)
|
||||
|
||||
DropdownMenuItem(
|
||||
text = {
|
||||
@@ -277,14 +268,14 @@ fun MessagesHistoryScreen(
|
||||
onClick = {
|
||||
dropDownMenuExpanded = false
|
||||
animationsEnabled = !animationsEnabled
|
||||
viewModel.onEnableAnimationsClicked(animationsEnabled)
|
||||
onToggleAnimationsDropdownItemClicked(animationsEnabled)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
if (screenState.isLoading && messages.isNotEmpty()) {
|
||||
if (screenState.isLoading && screenState.messages.isNotEmpty()) {
|
||||
LinearProgressIndicator(modifier = Modifier.fillMaxWidth())
|
||||
}
|
||||
}
|
||||
@@ -300,7 +291,7 @@ fun MessagesHistoryScreen(
|
||||
MessagesList(
|
||||
hazeState = hazeSate,
|
||||
listState = listState,
|
||||
immutableMessages = ImmutableList.copyOf(messages),
|
||||
immutableMessages = ImmutableList.copyOf(screenState.messages),
|
||||
isPaginating = screenState.isPaginating,
|
||||
enableAnimations = animationsEnabled
|
||||
)
|
||||
@@ -372,7 +363,7 @@ fun MessagesHistoryScreen(
|
||||
TextField(
|
||||
modifier = Modifier.weight(1f),
|
||||
value = screenState.message,
|
||||
onValueChange = viewModel::onInputChanged,
|
||||
onValueChange = onMessageInputChanged,
|
||||
colors = TextFieldDefaults.colors(
|
||||
unfocusedContainerColor = Color.Transparent,
|
||||
focusedContainerColor = Color.Transparent,
|
||||
@@ -382,7 +373,7 @@ fun MessagesHistoryScreen(
|
||||
placeholder = { Text(text = stringResource(id = UiR.string.message_input_hint)) }
|
||||
)
|
||||
|
||||
IconButton(onClick = viewModel::onAttachmentButtonClicked) {
|
||||
IconButton(onClick = onAttachmentButtonClicked) {
|
||||
Icon(
|
||||
painter = painterResource(id = UiR.drawable.round_attach_file_24),
|
||||
contentDescription = "Add attachment button",
|
||||
@@ -414,7 +405,7 @@ fun MessagesHistoryScreen(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
viewModel.onActionButtonClicked()
|
||||
onActionButtonClicked()
|
||||
}
|
||||
},
|
||||
modifier = Modifier.rotate(rotation.value)
|
||||
@@ -445,7 +436,7 @@ fun MessagesHistoryScreen(
|
||||
}
|
||||
}
|
||||
|
||||
if (screenState.isLoading && messages.isEmpty()) {
|
||||
if (screenState.isLoading && screenState.messages.isEmpty()) {
|
||||
CircularProgressIndicator(modifier = Modifier.align(Alignment.Center))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user