Release 0.1.8 (#139)

* pagination in chat fixed
* other fixes and improvements

* fixed visual bug in progress bar in chat history

* Refactor: Enhance conversations and friends features

-   In `ConversationsScreen`, removed `isNeedToScrollToTop` and `onScrolledToTop`, and refactored toolbar container color logic. Added `NoItemsView` for empty conversation lists.
-   In `MainGraph`, added `onMessageClicked` for navigation to message history.
-   In `ApiEvent`, introduced `parseOrNull` for handling unknown event types.
-   In `ConversationsViewModel`, removed `scrollToTop` logic and refactored error handling.
-   In `FriendsViewModel`, refactored error handling and introduced `onErrorConsumed` and `handleError`.
-   In `FriendItem`, added an icon button to initiate sending a message to a friend.
-   In `strings.xml`, added or updated strings for session expiration, log out, refreshing, and empty friend lists.
-   In `RootScreen`, added `onMessageClicked` for navigating to messages.
-   In `FriendsList`, added `onMessageClicked` for handling message clicks.
-   In `MainScreen`, removed unused `MutableSharedFlow`.
-   In `FriendsScreen`, added support for showing errors, added `onMessageClicked`, and replaced `hazeChild` with `hazeEffect` and `hazeSource`.
-   In `FriendsNavigation`, added `onMessageClicked` for handling message clicks.
-   In `ConversationsNavigation`, removed the unused `scrollToTopFlow` parameter.
-   In `ErrorView`, added text alignment.
-   In `NoItemsView`, added support for a button and custom text.
-   In `LongPollUpdatesParser`, replaced try-catch with `parseOrNull`.

* Chat creation feature (#138)

* - read indicator, edit status and time for message in messages history

* message sending status
This commit is contained in:
2025-03-23 09:22:41 +03:00
committed by GitHub
parent 30e132d418
commit b2879d8756
81 changed files with 1687 additions and 458 deletions
@@ -11,10 +11,7 @@ import dev.meloda.fast.common.extensions.createTimerFlow
import dev.meloda.fast.common.extensions.findWithIndex
import dev.meloda.fast.common.extensions.listenValue
import dev.meloda.fast.common.extensions.setValue
import dev.meloda.fast.conversations.model.ConversationOption
import dev.meloda.fast.conversations.model.ConversationsScreenState
import dev.meloda.fast.conversations.model.ConversationsShowOptions
import dev.meloda.fast.conversations.model.UiConversation
import dev.meloda.fast.conversations.util.asPresentation
import dev.meloda.fast.conversations.util.extractAvatar
import dev.meloda.fast.data.State
@@ -29,10 +26,11 @@ import dev.meloda.fast.model.InteractionType
import dev.meloda.fast.model.LongPollEvent
import dev.meloda.fast.model.api.domain.VkConversation
import dev.meloda.fast.network.VkErrorCode
import dev.meloda.fast.ui.model.api.ConversationOption
import dev.meloda.fast.ui.model.api.ConversationsShowOptions
import dev.meloda.fast.ui.model.api.UiConversation
import dev.meloda.fast.ui.util.ImmutableList
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
@@ -40,7 +38,6 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlin.coroutines.cancellation.CancellationException
interface ConversationsViewModel {
@@ -49,7 +46,6 @@ interface ConversationsViewModel {
val baseError: StateFlow<BaseError?>
val currentOffset: StateFlow<Int>
val canPaginate: StateFlow<Boolean>
val scrollToTop: StateFlow<Boolean>
fun onPaginationConditionsMet()
@@ -70,10 +66,6 @@ interface ConversationsViewModel {
fun setScrollIndex(index: Int)
fun setScrollOffset(offset: Int)
fun setScrollToTopFlow(scrollToTopFlow: Flow<Int>)
fun onScrolledToTop()
}
class ConversationsViewModelImpl(
@@ -91,12 +83,8 @@ class ConversationsViewModelImpl(
override val baseError = MutableStateFlow<BaseError?>(null)
override val currentOffset = MutableStateFlow(0)
override val canPaginate = MutableStateFlow(false)
override val scrollToTop = MutableStateFlow(false)
// TODO: 22-Dec-24, Danil Nikolaev: rewrite
private val useContactNames = {
userSettings.useContactNames.value
}
private val useContactNames: Boolean get() = userSettings.useContactNames.value
override fun onPaginationConditionsMet() {
currentOffset.update { screenState.value.conversations.size }
@@ -134,7 +122,7 @@ class ConversationsViewModelImpl(
}
override fun onRefresh() {
baseError.setValue { null }
onErrorConsumed()
loadConversations(offset = 0)
}
@@ -177,11 +165,11 @@ class ConversationsViewModelImpl(
conversations = old.conversations.map { item ->
item.copy(
isExpanded =
if (item.id == conversation.id) {
!item.isExpanded
} else {
false
},
if (item.id == conversation.id) {
!item.isExpanded
} else {
false
},
options = ImmutableList.copyOf(options)
)
}
@@ -200,7 +188,10 @@ class ConversationsViewModelImpl(
onPinDialogDismissed()
}
override fun onOptionClicked(conversation: UiConversation, option: ConversationOption) {
override fun onOptionClicked(
conversation: UiConversation,
option: ConversationOption
) {
when (option) {
ConversationOption.Delete -> {
emitShowOptions { old ->
@@ -237,20 +228,6 @@ class ConversationsViewModelImpl(
screenState.setValue { old -> old.copy(scrollOffset = offset) }
}
override fun setScrollToTopFlow(scrollToTopFlow: Flow<Int>) {
scrollToTopFlow.listenValue(viewModelScope) { index ->
if (index == 1) {
scrollToTop.emit(true)
}
}
}
override fun onScrolledToTop() {
viewModelScope.launch(Dispatchers.Main) {
scrollToTop.emit(false)
}
}
private fun hideOptions(conversationId: Int) {
screenState.setValue { old ->
old.copy(
@@ -345,21 +322,25 @@ class ConversationsViewModelImpl(
}
}
}
State.Error.ConnectionError -> {
baseError.setValue {
BaseError.SimpleError(message = "Connection error")
}
}
State.Error.InternalError -> {
baseError.setValue {
BaseError.SimpleError(message = "Internal error")
}
}
State.Error.UnknownError -> {
baseError.setValue {
BaseError.SimpleError(message = "Unknown error")
}
}
else -> Unit
}
}
@@ -383,7 +364,7 @@ class ConversationsViewModelImpl(
conversations = newConversations.map {
it.asPresentation(
resources = resources,
useContactName = useContactNames()
useContactName = useContactNames
)
}
)
@@ -447,7 +428,7 @@ class ConversationsViewModelImpl(
conversations = newConversations.map {
it.asPresentation(
resources = resources,
useContactName = useContactNames()
useContactName = useContactNames
)
}
)
@@ -498,7 +479,7 @@ class ConversationsViewModelImpl(
conversations = newConversations.map {
it.asPresentation(
resources = resources,
useContactName = useContactNames()
useContactName = useContactNames
)
}
)
@@ -527,7 +508,7 @@ class ConversationsViewModelImpl(
conversations = newConversations.map {
it.asPresentation(
resources = resources,
useContactName = useContactNames()
useContactName = useContactNames
)
}
)
@@ -557,7 +538,7 @@ class ConversationsViewModelImpl(
conversations = newConversations.map {
it.asPresentation(
resources = resources,
useContactName = useContactNames()
useContactName = useContactNames
)
}
)
@@ -586,7 +567,7 @@ class ConversationsViewModelImpl(
conversations = newConversations.map {
it.asPresentation(
resources = resources,
useContactName = useContactNames()
useContactName = useContactNames
)
}
)
@@ -632,7 +613,7 @@ class ConversationsViewModelImpl(
old.copy(conversations = newConversations.map {
it.asPresentation(
resources = resources,
useContactName = useContactNames()
useContactName = useContactNames
)
})
}
@@ -673,7 +654,7 @@ class ConversationsViewModelImpl(
conversations = newConversations.map {
it.asPresentation(
resources = resources,
useContactName = useContactNames()
useContactName = useContactNames
)
}
)
@@ -724,7 +705,7 @@ class ConversationsViewModelImpl(
conversations = newConversations.map {
it.asPresentation(
resources = resources,
useContactName = useContactNames()
useContactName = useContactNames
)
}
)
@@ -758,7 +739,7 @@ class ConversationsViewModelImpl(
conversations = newConversations.map {
it.asPresentation(
resources = resources,
useContactName = useContactNames()
useContactName = useContactNames
)
}
)
@@ -1,20 +0,0 @@
package dev.meloda.fast.conversations.model
enum class ActionState {
PHANTOM, CALL_IN_PROGRESS, NONE;
// TODO: 11/04/2024, Danil Nikolaev: implement
fun getResourceId(): Int {
return -1
}
companion object {
fun parse(isPhantom: Boolean, isCallInProgress: Boolean): ActionState {
return when {
isPhantom -> PHANTOM
isCallInProgress -> CALL_IN_PROGRESS
else -> NONE
}
}
}
}
@@ -1,31 +0,0 @@
package dev.meloda.fast.conversations.model
import dev.meloda.fast.common.model.UiImage
import dev.meloda.fast.common.model.UiText
import dev.meloda.fast.ui.R as UiR
sealed class ConversationOption(
val title: UiText,
val icon: UiImage
) {
data object MarkAsRead : ConversationOption(
title = UiText.Resource(UiR.string.action_mark_as_read),
icon = UiImage.Resource(UiR.drawable.round_done_all_24)
)
data object Pin : ConversationOption(
title = UiText.Resource(UiR.string.action_pin),
icon = UiImage.Resource(UiR.drawable.pin_outline_24)
)
data object Unpin : ConversationOption(
title = UiText.Resource(UiR.string.action_unpin),
icon = UiImage.Resource(UiR.drawable.pin_off_outline_24)
)
data object Delete : ConversationOption(
title = UiText.Resource(UiR.string.action_delete),
icon = UiImage.Resource(UiR.drawable.round_delete_outline_24)
)
}
@@ -1,6 +1,8 @@
package dev.meloda.fast.conversations.model
import androidx.compose.runtime.Immutable
import dev.meloda.fast.ui.model.api.ConversationsShowOptions
import dev.meloda.fast.ui.model.api.UiConversation
@Immutable
data class ConversationsScreenState(
@@ -1,14 +0,0 @@
package dev.meloda.fast.conversations.model
data class ConversationsShowOptions(
val showDeleteDialog: Int?,
val showPinDialog: UiConversation?
) {
companion object {
val EMPTY: ConversationsShowOptions = ConversationsShowOptions(
showDeleteDialog = null,
showPinDialog = null
)
}
}
@@ -1,31 +0,0 @@
package dev.meloda.fast.conversations.model
import androidx.compose.runtime.Immutable
import androidx.compose.ui.text.AnnotatedString
import dev.meloda.fast.common.model.UiImage
import dev.meloda.fast.model.api.PeerType
import dev.meloda.fast.model.api.domain.VkMessage
import dev.meloda.fast.ui.util.ImmutableList
@Immutable
data class UiConversation(
val id: Int,
val lastMessageId: Int?,
val avatar: UiImage?,
val title: String,
val unreadCount: String?,
val date: String,
val message: AnnotatedString,
val attachmentImage: UiImage?,
val isPinned: Boolean,
val actionImageId: Int,
val isBirthday: Boolean,
val isUnread: Boolean,
val isAccount: Boolean,
val isOnline: Boolean,
val lastMessage: VkMessage?,
val peerType: PeerType,
val interactionText: String?,
val isExpanded: Boolean,
val options: ImmutableList<ConversationOption>,
)
@@ -8,7 +8,6 @@ import dev.meloda.fast.conversations.ConversationsViewModelImpl
import dev.meloda.fast.conversations.presentation.ConversationsRoute
import dev.meloda.fast.model.BaseError
import dev.meloda.fast.ui.extensions.sharedViewModel
import kotlinx.coroutines.flow.Flow
import kotlinx.serialization.Serializable
@Serializable
@@ -18,18 +17,18 @@ fun NavGraphBuilder.conversationsScreen(
onError: (BaseError) -> Unit,
onConversationItemClicked: (id: Int) -> Unit,
onPhotoClicked: (url: String) -> Unit,
scrollToTopFlow: Flow<Int>,
onCreateChatClicked: () -> Unit,
navController: NavController,
) {
composable<Conversations> {
val viewModel: ConversationsViewModel =
it.sharedViewModel<ConversationsViewModelImpl>(navController = navController)
viewModel.setScrollToTopFlow(scrollToTopFlow)
ConversationsRoute(
onError = onError,
onConversationItemClicked = onConversationItemClicked,
onConversationPhotoClicked = onPhotoClicked,
onCreateChatButtonClicked = onCreateChatClicked,
viewModel = viewModel
)
}
@@ -48,11 +48,11 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import coil.compose.AsyncImage
import dev.meloda.fast.conversations.model.ConversationOption
import dev.meloda.fast.conversations.model.UiConversation
import dev.meloda.fast.ui.basic.ContentAlpha
import dev.meloda.fast.ui.basic.LocalContentAlpha
import dev.meloda.fast.ui.components.DotsFlashing
import dev.meloda.fast.ui.model.api.ConversationOption
import dev.meloda.fast.ui.model.api.UiConversation
import dev.meloda.fast.ui.util.getImage
import dev.meloda.fast.ui.util.getResourcePainter
import dev.meloda.fast.ui.util.getString
@@ -256,7 +256,7 @@ fun ConversationItem(
Row {
if (conversation.interactionText != null) {
Text(
text = conversation.interactionText,
text = conversation.interactionText.orEmpty(),
color = MaterialTheme.colorScheme.primary
)
Spacer(modifier = Modifier.width(4.dp))
@@ -5,7 +5,6 @@ import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.items
@@ -23,10 +22,10 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import dev.meloda.fast.conversations.model.ConversationOption
import dev.meloda.fast.conversations.model.ConversationsScreenState
import dev.meloda.fast.conversations.model.UiConversation
import dev.meloda.fast.data.UserConfig
import dev.meloda.fast.ui.model.api.ConversationOption
import dev.meloda.fast.ui.model.api.UiConversation
import dev.meloda.fast.ui.theme.LocalBottomPadding
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -83,8 +82,7 @@ fun ConversationsList(
Column(
modifier = Modifier
.fillMaxWidth()
.animateItem(fadeInSpec = null, fadeOutSpec = null)
.navigationBarsPadding(),
.animateItem(fadeInSpec = null, fadeOutSpec = null),
horizontalAlignment = Alignment.CenterHorizontally
) {
if (screenState.isPaginating) {
@@ -107,11 +105,9 @@ fun ConversationsList(
)
}
}
}
}
item {
Spacer(modifier = Modifier.height(bottomPadding))
Spacer(modifier = Modifier.height(8.dp))
}
}
}
}
@@ -2,7 +2,6 @@ package dev.meloda.fast.conversations.presentation
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
@@ -48,12 +47,10 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
@@ -62,28 +59,26 @@ import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.core.view.HapticFeedbackConstantsCompat
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import dev.chrisbanes.haze.haze
import dev.chrisbanes.haze.hazeChild
import dev.chrisbanes.haze.hazeEffect
import dev.chrisbanes.haze.hazeSource
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
import dev.chrisbanes.haze.materials.HazeMaterials
import dev.meloda.fast.conversations.ConversationsViewModel
import dev.meloda.fast.conversations.model.ConversationOption
import dev.meloda.fast.conversations.model.ConversationsScreenState
import dev.meloda.fast.conversations.model.UiConversation
import dev.meloda.fast.datastore.AppSettings
import dev.meloda.fast.model.BaseError
import dev.meloda.fast.ui.components.ErrorView
import dev.meloda.fast.ui.components.FullScreenLoader
import dev.meloda.fast.ui.components.MaterialDialog
import dev.meloda.fast.ui.components.NoItemsView
import dev.meloda.fast.ui.model.api.ConversationOption
import dev.meloda.fast.ui.model.api.UiConversation
import dev.meloda.fast.ui.theme.LocalBottomPadding
import dev.meloda.fast.ui.theme.LocalHazeState
import dev.meloda.fast.ui.theme.LocalThemeConfig
import dev.meloda.fast.ui.util.isScrollingUp
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.launch
import dev.meloda.fast.ui.R as UiR
@Composable
@@ -91,12 +86,12 @@ fun ConversationsRoute(
onError: (BaseError) -> Unit,
onConversationItemClicked: (conversationId: Int) -> Unit,
onConversationPhotoClicked: (url: String) -> Unit,
onCreateChatButtonClicked: () -> Unit,
viewModel: ConversationsViewModel
) {
val screenState by viewModel.screenState.collectAsStateWithLifecycle()
val baseError by viewModel.baseError.collectAsStateWithLifecycle()
val canPaginate by viewModel.canPaginate.collectAsStateWithLifecycle()
val isNeedToScrollToTop by viewModel.scrollToTop.collectAsStateWithLifecycle()
ConversationsScreen(
screenState = screenState,
@@ -113,10 +108,9 @@ fun ConversationsRoute(
onRefreshDropdownItemClicked = viewModel::onRefresh,
onRefresh = viewModel::onRefresh,
onConversationPhotoClicked = onConversationPhotoClicked,
onCreateChatButtonClicked = onCreateChatButtonClicked,
setScrollIndex = viewModel::setScrollIndex,
setScrollOffset = viewModel::setScrollOffset,
isNeedToScrollToTop = isNeedToScrollToTop,
onScrolledToTop = viewModel::onScrolledToTop
setScrollOffset = viewModel::setScrollOffset
)
HandleDialogs(
@@ -134,7 +128,7 @@ fun ConversationsScreen(
screenState: ConversationsScreenState = ConversationsScreenState.EMPTY,
baseError: BaseError? = null,
canPaginate: Boolean = false,
onSessionExpiredLogOutButtonClicked: () -> Unit,
onSessionExpiredLogOutButtonClicked: () -> Unit = {},
onConversationItemClicked: (conversationId: Int) -> Unit = {},
onConversationItemLongClicked: (conversation: UiConversation) -> Unit = {},
onOptionClicked: (UiConversation, ConversationOption) -> Unit = { _, _ -> },
@@ -142,10 +136,9 @@ fun ConversationsScreen(
onRefreshDropdownItemClicked: () -> Unit = {},
onRefresh: () -> Unit = {},
onConversationPhotoClicked: (url: String) -> Unit = {},
onCreateChatButtonClicked: () -> Unit = {},
setScrollIndex: (Int) -> Unit = {},
setScrollOffset: (Int) -> Unit = {},
isNeedToScrollToTop: Boolean = false,
onScrolledToTop: () -> Unit = {}
setScrollOffset: (Int) -> Unit = {}
) {
val view = LocalView.current
val currentTheme = LocalThemeConfig.current
@@ -159,14 +152,6 @@ fun ConversationsScreen(
initialFirstVisibleItemScrollOffset = screenState.scrollOffset
)
LaunchedEffect(isNeedToScrollToTop) {
if (isNeedToScrollToTop) {
listState.scrollToItem(0)
onScrolledToTop()
}
}
LaunchedEffect(listState) {
snapshotFlow { listState.firstVisibleItemIndex }
.debounce(500L)
@@ -207,10 +192,10 @@ fun ConversationsScreen(
val toolbarContainerColor by animateColorAsState(
targetValue =
if (currentTheme.enableBlur || !listState.canScrollBackward)
MaterialTheme.colorScheme.surface
else
MaterialTheme.colorScheme.surfaceColorAtElevation(3.dp),
if (currentTheme.enableBlur || !listState.canScrollBackward)
MaterialTheme.colorScheme.surface
else
MaterialTheme.colorScheme.surfaceColorAtElevation(3.dp),
label = "toolbarColorAlpha",
animationSpec = tween(durationMillis = 50)
)
@@ -275,7 +260,7 @@ fun ConversationsScreen(
modifier = Modifier
.then(
if (currentTheme.enableBlur) {
Modifier.hazeChild(
Modifier.hazeEffect(
state = hazeState,
style = HazeMaterials.thick()
)
@@ -296,37 +281,13 @@ fun ConversationsScreen(
}
},
floatingActionButton = {
val scope = rememberCoroutineScope()
val rotation = remember { Animatable(0f) }
Column {
AnimatedVisibility(
visible = listState.isScrollingUp(),
enter = slideIn { IntOffset(0, 600) } + fadeIn(tween(200)),
exit = slideOut { IntOffset(0, 600) } + fadeOut(tween(200))
) {
FloatingActionButton(
onClick = {
if (AppSettings.General.enableHaptic) {
view.performHapticFeedback(HapticFeedbackConstantsCompat.REJECT)
}
scope.launch {
for (i in 20 downTo 0 step 4) {
rotation.animateTo(
targetValue = i.toFloat(),
animationSpec = tween(50)
)
if (i > 0) {
rotation.animateTo(
targetValue = -i.toFloat(),
animationSpec = tween(50)
)
}
}
}
},
modifier = Modifier.rotate(rotation.value)
) {
FloatingActionButton(onClick = onCreateChatButtonClicked) {
Icon(
painter = painterResource(id = UiR.drawable.ic_baseline_create_24),
contentDescription = "Add chat button"
@@ -343,8 +304,8 @@ fun ConversationsScreen(
when (baseError) {
is BaseError.SessionExpired -> {
ErrorView(
text = "Session expired",
buttonText = "Log out",
text = stringResource(UiR.string.session_expired),
buttonText = stringResource(UiR.string.action_log_out),
onButtonClick = onSessionExpiredLogOutButtonClicked
)
}
@@ -352,7 +313,7 @@ fun ConversationsScreen(
is BaseError.SimpleError -> {
ErrorView(
text = baseError.message,
buttonText = "Try again",
buttonText = stringResource(UiR.string.try_again),
onButtonClick = onRefresh
)
}
@@ -390,7 +351,7 @@ fun ConversationsScreen(
state = listState,
maxLines = maxLines,
modifier = if (currentTheme.enableBlur) {
Modifier.haze(state = hazeState)
Modifier.hazeSource(state = hazeState)
} else {
Modifier
}.fillMaxSize(),
@@ -398,6 +359,13 @@ fun ConversationsScreen(
padding = padding,
onPhotoClicked = onConversationPhotoClicked
)
if (screenState.conversations.isEmpty()) {
NoItemsView(
buttonText = stringResource(UiR.string.action_refresh),
onButtonClick = onRefresh
)
}
}
}
}
@@ -422,9 +390,7 @@ fun HandleDialogs(
)
}
if (showOptions.showPinDialog != null) {
val conversation = showOptions.showPinDialog
showOptions.showPinDialog?.let { conversation ->
MaterialDialog(
onDismissRequest = viewModel::onPinDialogDismissed,
title = stringResource(
@@ -14,8 +14,6 @@ import dev.meloda.fast.common.model.UiImage
import dev.meloda.fast.common.model.UiText
import dev.meloda.fast.common.model.parseString
import dev.meloda.fast.common.util.TimeUtils
import dev.meloda.fast.conversations.model.ActionState
import dev.meloda.fast.conversations.model.UiConversation
import dev.meloda.fast.data.UserConfig
import dev.meloda.fast.data.VkMemoryCache
import dev.meloda.fast.model.InteractionType
@@ -24,6 +22,8 @@ import dev.meloda.fast.model.api.data.AttachmentType
import dev.meloda.fast.model.api.domain.VkAttachment
import dev.meloda.fast.model.api.domain.VkConversation
import dev.meloda.fast.model.api.domain.VkMessage
import dev.meloda.fast.ui.model.api.ActionState
import dev.meloda.fast.ui.model.api.UiConversation
import dev.meloda.fast.ui.util.ImmutableList
import java.util.Calendar
import java.util.Locale