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`.
This commit is contained in:
+1
-24
@@ -30,9 +30,7 @@ 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.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,7 +83,6 @@ 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 = {
|
||||
@@ -134,7 +125,7 @@ class ConversationsViewModelImpl(
|
||||
}
|
||||
|
||||
override fun onRefresh() {
|
||||
baseError.setValue { null }
|
||||
onErrorConsumed()
|
||||
loadConversations(offset = 0)
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
-3
@@ -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,13 +17,11 @@ fun NavGraphBuilder.conversationsScreen(
|
||||
onError: (BaseError) -> Unit,
|
||||
onConversationItemClicked: (id: Int) -> Unit,
|
||||
onPhotoClicked: (url: String) -> Unit,
|
||||
scrollToTopFlow: Flow<Int>,
|
||||
navController: NavController,
|
||||
) {
|
||||
composable<Conversations> {
|
||||
val viewModel: ConversationsViewModel =
|
||||
it.sharedViewModel<ConversationsViewModelImpl>(navController = navController)
|
||||
viewModel.setScrollToTopFlow(scrollToTopFlow)
|
||||
|
||||
ConversationsRoute(
|
||||
onError = onError,
|
||||
|
||||
+17
-22
@@ -77,6 +77,7 @@ 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.theme.LocalBottomPadding
|
||||
import dev.meloda.fast.ui.theme.LocalHazeState
|
||||
import dev.meloda.fast.ui.theme.LocalThemeConfig
|
||||
@@ -96,7 +97,6 @@ fun ConversationsRoute(
|
||||
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,
|
||||
@@ -114,9 +114,7 @@ fun ConversationsRoute(
|
||||
onRefresh = viewModel::onRefresh,
|
||||
onConversationPhotoClicked = onConversationPhotoClicked,
|
||||
setScrollIndex = viewModel::setScrollIndex,
|
||||
setScrollOffset = viewModel::setScrollOffset,
|
||||
isNeedToScrollToTop = isNeedToScrollToTop,
|
||||
onScrolledToTop = viewModel::onScrolledToTop
|
||||
setScrollOffset = viewModel::setScrollOffset
|
||||
)
|
||||
|
||||
HandleDialogs(
|
||||
@@ -143,9 +141,7 @@ fun ConversationsScreen(
|
||||
onRefresh: () -> Unit = {},
|
||||
onConversationPhotoClicked: (url: String) -> 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 +155,6 @@ fun ConversationsScreen(
|
||||
initialFirstVisibleItemScrollOffset = screenState.scrollOffset
|
||||
)
|
||||
|
||||
LaunchedEffect(isNeedToScrollToTop) {
|
||||
if (isNeedToScrollToTop) {
|
||||
listState.scrollToItem(0)
|
||||
onScrolledToTop()
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(listState) {
|
||||
snapshotFlow { listState.firstVisibleItemIndex }
|
||||
.debounce(500L)
|
||||
@@ -207,10 +195,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)
|
||||
)
|
||||
@@ -343,8 +331,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 +340,7 @@ fun ConversationsScreen(
|
||||
is BaseError.SimpleError -> {
|
||||
ErrorView(
|
||||
text = baseError.message,
|
||||
buttonText = "Try again",
|
||||
buttonText = stringResource(UiR.string.try_again),
|
||||
onButtonClick = onRefresh
|
||||
)
|
||||
}
|
||||
@@ -398,6 +386,13 @@ fun ConversationsScreen(
|
||||
padding = padding,
|
||||
onPhotoClicked = onConversationPhotoClicked
|
||||
)
|
||||
|
||||
if (screenState.conversations.isEmpty()) {
|
||||
NoItemsView(
|
||||
buttonText = stringResource(UiR.string.action_refresh),
|
||||
onButtonClick = onRefresh
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user