forked from melod1n/fast-messenger
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:
@@ -24,6 +24,7 @@ fun NavGraphBuilder.mainScreen(
|
|||||||
onSettingsButtonClicked: () -> Unit,
|
onSettingsButtonClicked: () -> Unit,
|
||||||
onConversationClicked: (conversationId: Int) -> Unit,
|
onConversationClicked: (conversationId: Int) -> Unit,
|
||||||
onPhotoClicked: (url: String) -> Unit,
|
onPhotoClicked: (url: String) -> Unit,
|
||||||
|
onMessageClicked: (userId: Int) -> Unit,
|
||||||
viewModel: MainViewModel
|
viewModel: MainViewModel
|
||||||
) {
|
) {
|
||||||
val navigationItems = ImmutableList.of(
|
val navigationItems = ImmutableList.of(
|
||||||
@@ -54,6 +55,7 @@ fun NavGraphBuilder.mainScreen(
|
|||||||
onSettingsButtonClicked = onSettingsButtonClicked,
|
onSettingsButtonClicked = onSettingsButtonClicked,
|
||||||
onConversationItemClicked = onConversationClicked,
|
onConversationItemClicked = onConversationClicked,
|
||||||
onPhotoClicked = onPhotoClicked,
|
onPhotoClicked = onPhotoClicked,
|
||||||
|
onMessageClicked = onMessageClicked,
|
||||||
viewModel = viewModel
|
viewModel = viewModel
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,8 +47,6 @@ import dev.meloda.fast.ui.theme.LocalBottomPadding
|
|||||||
import dev.meloda.fast.ui.theme.LocalHazeState
|
import dev.meloda.fast.ui.theme.LocalHazeState
|
||||||
import dev.meloda.fast.ui.theme.LocalThemeConfig
|
import dev.meloda.fast.ui.theme.LocalThemeConfig
|
||||||
import dev.meloda.fast.ui.util.ImmutableList
|
import dev.meloda.fast.ui.util.ImmutableList
|
||||||
import kotlinx.coroutines.channels.BufferOverflow
|
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
|
||||||
|
|
||||||
@OptIn(ExperimentalHazeMaterialsApi::class)
|
@OptIn(ExperimentalHazeMaterialsApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
@@ -58,6 +56,7 @@ fun MainScreen(
|
|||||||
onSettingsButtonClicked: () -> Unit = {},
|
onSettingsButtonClicked: () -> Unit = {},
|
||||||
onConversationItemClicked: (conversationId: Int) -> Unit = {},
|
onConversationItemClicked: (conversationId: Int) -> Unit = {},
|
||||||
onPhotoClicked: (url: String) -> Unit = {},
|
onPhotoClicked: (url: String) -> Unit = {},
|
||||||
|
onMessageClicked: (userId: Int) -> Unit = {},
|
||||||
viewModel: MainViewModel
|
viewModel: MainViewModel
|
||||||
) {
|
) {
|
||||||
val currentTheme = LocalThemeConfig.current
|
val currentTheme = LocalThemeConfig.current
|
||||||
@@ -70,14 +69,6 @@ fun MainScreen(
|
|||||||
mutableIntStateOf(1)
|
mutableIntStateOf(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
val sharedFlow = remember {
|
|
||||||
MutableSharedFlow<Int>(
|
|
||||||
replay = 0,
|
|
||||||
extraBufferCapacity = 1,
|
|
||||||
onBufferOverflow = BufferOverflow.DROP_OLDEST
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
bottomBar = {
|
bottomBar = {
|
||||||
NavigationBar(
|
NavigationBar(
|
||||||
@@ -108,8 +99,6 @@ fun MainScreen(
|
|||||||
inclusive = true
|
inclusive = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
sharedFlow.tryEmit(index)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
icon = {
|
icon = {
|
||||||
@@ -176,13 +165,13 @@ fun MainScreen(
|
|||||||
friendsScreen(
|
friendsScreen(
|
||||||
onError = onError,
|
onError = onError,
|
||||||
navController = navController,
|
navController = navController,
|
||||||
onPhotoClicked = onPhotoClicked
|
onPhotoClicked = onPhotoClicked,
|
||||||
|
onMessageClicked = onMessageClicked
|
||||||
)
|
)
|
||||||
conversationsScreen(
|
conversationsScreen(
|
||||||
onError = onError,
|
onError = onError,
|
||||||
onConversationItemClicked = onConversationItemClicked,
|
onConversationItemClicked = onConversationItemClicked,
|
||||||
onPhotoClicked = onPhotoClicked,
|
onPhotoClicked = onPhotoClicked,
|
||||||
scrollToTopFlow = sharedFlow,
|
|
||||||
navController = navController,
|
navController = navController,
|
||||||
)
|
)
|
||||||
profileScreen(
|
profileScreen(
|
||||||
|
|||||||
@@ -124,6 +124,7 @@ fun RootScreen(
|
|||||||
onSettingsButtonClicked = navController::navigateToSettings,
|
onSettingsButtonClicked = navController::navigateToSettings,
|
||||||
onConversationClicked = navController::navigateToMessagesHistory,
|
onConversationClicked = navController::navigateToMessagesHistory,
|
||||||
onPhotoClicked = { url -> navController.navigateToPhotoView(listOf(url)) },
|
onPhotoClicked = { url -> navController.navigateToPhotoView(listOf(url)) },
|
||||||
|
onMessageClicked = navController::navigateToMessagesHistory,
|
||||||
viewModel = viewModel
|
viewModel = viewModel
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -41,15 +41,9 @@ class LongPollUpdatesParser(
|
|||||||
fun parseNextUpdate(event: List<Any>) {
|
fun parseNextUpdate(event: List<Any>) {
|
||||||
val eventId = event.first().asInt()
|
val eventId = event.first().asInt()
|
||||||
|
|
||||||
val eventType: ApiEvent = try {
|
when (val eventType = ApiEvent.parseOrNull(eventId)) {
|
||||||
ApiEvent.parse(eventId)
|
null -> Log.d("LongPollUpdatesParser", "parseNextUpdate: unknownEvent: $event")
|
||||||
} catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
Log.d("LongPollUpdatesParser", "parseNextUpdate: unknownEvent: $event")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
when (eventType) {
|
|
||||||
ApiEvent.MESSAGE_SET_FLAGS -> parseMessageSetFlags(eventType, event)
|
ApiEvent.MESSAGE_SET_FLAGS -> parseMessageSetFlags(eventType, event)
|
||||||
ApiEvent.MESSAGE_CLEAR_FLAGS -> parseMessageClearFlags(eventType, event)
|
ApiEvent.MESSAGE_CLEAR_FLAGS -> parseMessageClearFlags(eventType, event)
|
||||||
ApiEvent.MESSAGE_NEW -> parseMessageNew(eventType, event)
|
ApiEvent.MESSAGE_NEW -> parseMessageNew(eventType, event)
|
||||||
|
|||||||
@@ -18,5 +18,6 @@ enum class ApiEvent(val value: Int) {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun parse(value: Int) = entries.first { it.value == value }
|
fun parse(value: Int) = entries.first { it.value == value }
|
||||||
|
fun parseOrNull(value: Int) = entries.firstOrNull { it.value == value }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import androidx.compose.material3.Text
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
@@ -31,7 +32,8 @@ fun ErrorView(
|
|||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = text,
|
text = text,
|
||||||
style = MaterialTheme.typography.titleLarge
|
style = MaterialTheme.typography.titleLarge,
|
||||||
|
textAlign = TextAlign.Center
|
||||||
)
|
)
|
||||||
|
|
||||||
buttonText?.let {
|
buttonText?.let {
|
||||||
|
|||||||
@@ -1,29 +1,51 @@
|
|||||||
package dev.meloda.fast.ui.components
|
package dev.meloda.fast.ui.components
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
import dev.meloda.fast.ui.R
|
import dev.meloda.fast.ui.R
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun NoItemsView(
|
fun NoItemsView(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
customText: String? = null
|
customText: String? = null,
|
||||||
|
buttonText: String? = null,
|
||||||
|
onButtonClick: (() -> Unit)? = null,
|
||||||
) {
|
) {
|
||||||
Box(
|
Column(
|
||||||
modifier = modifier.fillMaxSize(),
|
modifier = modifier
|
||||||
contentAlignment = Alignment.Center
|
.fillMaxSize()
|
||||||
|
.padding(horizontal = 16.dp),
|
||||||
|
verticalArrangement = Arrangement.Center,
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = customText ?: stringResource(id = R.string.no_items),
|
text = customText ?: stringResource(R.string.no_items),
|
||||||
style = MaterialTheme.typography.titleLarge
|
style = MaterialTheme.typography.titleLarge,
|
||||||
|
textAlign = TextAlign.Center
|
||||||
)
|
)
|
||||||
|
|
||||||
|
buttonText?.let {
|
||||||
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
|
Button(
|
||||||
|
onClick = { onButtonClick?.invoke() }
|
||||||
|
) {
|
||||||
|
Text(text = buttonText)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,6 +53,7 @@ fun NoItemsView(
|
|||||||
@Composable
|
@Composable
|
||||||
private fun NoItemsViewPreview() {
|
private fun NoItemsViewPreview() {
|
||||||
NoItemsView(
|
NoItemsView(
|
||||||
customText = "Nothing here..."
|
customText = "Nothing here...",
|
||||||
|
buttonText = "Refresh"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -128,7 +128,7 @@
|
|||||||
<string name="post_type_community">Запись сообщества</string>
|
<string name="post_type_community">Запись сообщества</string>
|
||||||
<string name="post_type_user">Запись пользователя</string>
|
<string name="post_type_user">Запись пользователя</string>
|
||||||
<string name="post_type_unknown">Запись на стене</string>
|
<string name="post_type_unknown">Запись на стене</string>
|
||||||
<string name="log_out">Выйти</string>
|
<string name="action_log_out">Выйти</string>
|
||||||
<string name="confirm">Подтверждение</string>
|
<string name="confirm">Подтверждение</string>
|
||||||
<string name="message_attachment_story_your_story">Ваша история</string>
|
<string name="message_attachment_story_your_story">Ваша история</string>
|
||||||
<string name="settings_dynamic_colors">Динамические цвета</string>
|
<string name="settings_dynamic_colors">Динамические цвета</string>
|
||||||
@@ -212,4 +212,7 @@
|
|||||||
<string name="captcha_exit_warning">Вы уверены? Процесс ввода капчи будет отменён</string>
|
<string name="captcha_exit_warning">Вы уверены? Процесс ввода капчи будет отменён</string>
|
||||||
<string name="validation_exit_warning">Вы уверены? Процесс ввода кода-подтверждения будет отменён</string>
|
<string name="validation_exit_warning">Вы уверены? Процесс ввода кода-подтверждения будет отменён</string>
|
||||||
<string name="action_authorize">Авторизоваться</string>
|
<string name="action_authorize">Авторизоваться</string>
|
||||||
|
<string name="no_online_friends">Никого в сети</string>
|
||||||
|
<string name="try_again">Попробовать ещё раз</string>
|
||||||
|
<string name="session_expired">Срок действия сессии истёк</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -119,7 +119,7 @@
|
|||||||
<string name="post_type_community">Community post</string>
|
<string name="post_type_community">Community post</string>
|
||||||
<string name="post_type_user">User post</string>
|
<string name="post_type_user">User post</string>
|
||||||
<string name="post_type_unknown">Post</string>
|
<string name="post_type_unknown">Post</string>
|
||||||
<string name="log_out">Log out</string>
|
<string name="action_log_out">Log out</string>
|
||||||
<string name="confirm">Confirmation</string>
|
<string name="confirm">Confirmation</string>
|
||||||
<string name="sign_out_confirm">Signing out will delete all data related to this account from this device. Continue?</string>
|
<string name="sign_out_confirm">Signing out will delete all data related to this account from this device. Continue?</string>
|
||||||
<string name="yes">Yes</string>
|
<string name="yes">Yes</string>
|
||||||
@@ -276,6 +276,8 @@
|
|||||||
<string name="warning_confirmation">Confirmation</string>
|
<string name="warning_confirmation">Confirmation</string>
|
||||||
<string name="captcha_exit_warning">Are you sure? Captcha process will be cancelled</string>
|
<string name="captcha_exit_warning">Are you sure? Captcha process will be cancelled</string>
|
||||||
<string name="validation_exit_warning">Are you sure? Validation process will be cancelled</string>
|
<string name="validation_exit_warning">Are you sure? Validation process will be cancelled</string>
|
||||||
<string name="settings_general_enable_pull_to_refresh_title">Enable pull to refresh</string>
|
|
||||||
<string name="action_authorize">Authorize</string>
|
<string name="action_authorize">Authorize</string>
|
||||||
|
<string name="no_online_friends">No one is online</string>
|
||||||
|
<string name="try_again">Try again</string>
|
||||||
|
<string name="session_expired">Session expired</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
+1
-24
@@ -30,9 +30,7 @@ import dev.meloda.fast.model.LongPollEvent
|
|||||||
import dev.meloda.fast.model.api.domain.VkConversation
|
import dev.meloda.fast.model.api.domain.VkConversation
|
||||||
import dev.meloda.fast.network.VkErrorCode
|
import dev.meloda.fast.network.VkErrorCode
|
||||||
import dev.meloda.fast.ui.util.ImmutableList
|
import dev.meloda.fast.ui.util.ImmutableList
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
@@ -40,7 +38,6 @@ import kotlinx.coroutines.flow.launchIn
|
|||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlin.coroutines.cancellation.CancellationException
|
import kotlin.coroutines.cancellation.CancellationException
|
||||||
|
|
||||||
interface ConversationsViewModel {
|
interface ConversationsViewModel {
|
||||||
@@ -49,7 +46,6 @@ interface ConversationsViewModel {
|
|||||||
val baseError: StateFlow<BaseError?>
|
val baseError: StateFlow<BaseError?>
|
||||||
val currentOffset: StateFlow<Int>
|
val currentOffset: StateFlow<Int>
|
||||||
val canPaginate: StateFlow<Boolean>
|
val canPaginate: StateFlow<Boolean>
|
||||||
val scrollToTop: StateFlow<Boolean>
|
|
||||||
|
|
||||||
fun onPaginationConditionsMet()
|
fun onPaginationConditionsMet()
|
||||||
|
|
||||||
@@ -70,10 +66,6 @@ interface ConversationsViewModel {
|
|||||||
|
|
||||||
fun setScrollIndex(index: Int)
|
fun setScrollIndex(index: Int)
|
||||||
fun setScrollOffset(offset: Int)
|
fun setScrollOffset(offset: Int)
|
||||||
|
|
||||||
|
|
||||||
fun setScrollToTopFlow(scrollToTopFlow: Flow<Int>)
|
|
||||||
fun onScrolledToTop()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class ConversationsViewModelImpl(
|
class ConversationsViewModelImpl(
|
||||||
@@ -91,7 +83,6 @@ class ConversationsViewModelImpl(
|
|||||||
override val baseError = MutableStateFlow<BaseError?>(null)
|
override val baseError = MutableStateFlow<BaseError?>(null)
|
||||||
override val currentOffset = MutableStateFlow(0)
|
override val currentOffset = MutableStateFlow(0)
|
||||||
override val canPaginate = MutableStateFlow(false)
|
override val canPaginate = MutableStateFlow(false)
|
||||||
override val scrollToTop = MutableStateFlow(false)
|
|
||||||
|
|
||||||
// TODO: 22-Dec-24, Danil Nikolaev: rewrite
|
// TODO: 22-Dec-24, Danil Nikolaev: rewrite
|
||||||
private val useContactNames = {
|
private val useContactNames = {
|
||||||
@@ -134,7 +125,7 @@ class ConversationsViewModelImpl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onRefresh() {
|
override fun onRefresh() {
|
||||||
baseError.setValue { null }
|
onErrorConsumed()
|
||||||
loadConversations(offset = 0)
|
loadConversations(offset = 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,20 +228,6 @@ class ConversationsViewModelImpl(
|
|||||||
screenState.setValue { old -> old.copy(scrollOffset = offset) }
|
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) {
|
private fun hideOptions(conversationId: Int) {
|
||||||
screenState.setValue { old ->
|
screenState.setValue { old ->
|
||||||
old.copy(
|
old.copy(
|
||||||
|
|||||||
-3
@@ -8,7 +8,6 @@ import dev.meloda.fast.conversations.ConversationsViewModelImpl
|
|||||||
import dev.meloda.fast.conversations.presentation.ConversationsRoute
|
import dev.meloda.fast.conversations.presentation.ConversationsRoute
|
||||||
import dev.meloda.fast.model.BaseError
|
import dev.meloda.fast.model.BaseError
|
||||||
import dev.meloda.fast.ui.extensions.sharedViewModel
|
import dev.meloda.fast.ui.extensions.sharedViewModel
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@@ -18,13 +17,11 @@ fun NavGraphBuilder.conversationsScreen(
|
|||||||
onError: (BaseError) -> Unit,
|
onError: (BaseError) -> Unit,
|
||||||
onConversationItemClicked: (id: Int) -> Unit,
|
onConversationItemClicked: (id: Int) -> Unit,
|
||||||
onPhotoClicked: (url: String) -> Unit,
|
onPhotoClicked: (url: String) -> Unit,
|
||||||
scrollToTopFlow: Flow<Int>,
|
|
||||||
navController: NavController,
|
navController: NavController,
|
||||||
) {
|
) {
|
||||||
composable<Conversations> {
|
composable<Conversations> {
|
||||||
val viewModel: ConversationsViewModel =
|
val viewModel: ConversationsViewModel =
|
||||||
it.sharedViewModel<ConversationsViewModelImpl>(navController = navController)
|
it.sharedViewModel<ConversationsViewModelImpl>(navController = navController)
|
||||||
viewModel.setScrollToTopFlow(scrollToTopFlow)
|
|
||||||
|
|
||||||
ConversationsRoute(
|
ConversationsRoute(
|
||||||
onError = onError,
|
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.ErrorView
|
||||||
import dev.meloda.fast.ui.components.FullScreenLoader
|
import dev.meloda.fast.ui.components.FullScreenLoader
|
||||||
import dev.meloda.fast.ui.components.MaterialDialog
|
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.LocalBottomPadding
|
||||||
import dev.meloda.fast.ui.theme.LocalHazeState
|
import dev.meloda.fast.ui.theme.LocalHazeState
|
||||||
import dev.meloda.fast.ui.theme.LocalThemeConfig
|
import dev.meloda.fast.ui.theme.LocalThemeConfig
|
||||||
@@ -96,7 +97,6 @@ fun ConversationsRoute(
|
|||||||
val screenState by viewModel.screenState.collectAsStateWithLifecycle()
|
val screenState by viewModel.screenState.collectAsStateWithLifecycle()
|
||||||
val baseError by viewModel.baseError.collectAsStateWithLifecycle()
|
val baseError by viewModel.baseError.collectAsStateWithLifecycle()
|
||||||
val canPaginate by viewModel.canPaginate.collectAsStateWithLifecycle()
|
val canPaginate by viewModel.canPaginate.collectAsStateWithLifecycle()
|
||||||
val isNeedToScrollToTop by viewModel.scrollToTop.collectAsStateWithLifecycle()
|
|
||||||
|
|
||||||
ConversationsScreen(
|
ConversationsScreen(
|
||||||
screenState = screenState,
|
screenState = screenState,
|
||||||
@@ -114,9 +114,7 @@ fun ConversationsRoute(
|
|||||||
onRefresh = viewModel::onRefresh,
|
onRefresh = viewModel::onRefresh,
|
||||||
onConversationPhotoClicked = onConversationPhotoClicked,
|
onConversationPhotoClicked = onConversationPhotoClicked,
|
||||||
setScrollIndex = viewModel::setScrollIndex,
|
setScrollIndex = viewModel::setScrollIndex,
|
||||||
setScrollOffset = viewModel::setScrollOffset,
|
setScrollOffset = viewModel::setScrollOffset
|
||||||
isNeedToScrollToTop = isNeedToScrollToTop,
|
|
||||||
onScrolledToTop = viewModel::onScrolledToTop
|
|
||||||
)
|
)
|
||||||
|
|
||||||
HandleDialogs(
|
HandleDialogs(
|
||||||
@@ -143,9 +141,7 @@ fun ConversationsScreen(
|
|||||||
onRefresh: () -> Unit = {},
|
onRefresh: () -> Unit = {},
|
||||||
onConversationPhotoClicked: (url: String) -> Unit = {},
|
onConversationPhotoClicked: (url: String) -> Unit = {},
|
||||||
setScrollIndex: (Int) -> Unit = {},
|
setScrollIndex: (Int) -> Unit = {},
|
||||||
setScrollOffset: (Int) -> Unit = {},
|
setScrollOffset: (Int) -> Unit = {}
|
||||||
isNeedToScrollToTop: Boolean = false,
|
|
||||||
onScrolledToTop: () -> Unit = {}
|
|
||||||
) {
|
) {
|
||||||
val view = LocalView.current
|
val view = LocalView.current
|
||||||
val currentTheme = LocalThemeConfig.current
|
val currentTheme = LocalThemeConfig.current
|
||||||
@@ -159,14 +155,6 @@ fun ConversationsScreen(
|
|||||||
initialFirstVisibleItemScrollOffset = screenState.scrollOffset
|
initialFirstVisibleItemScrollOffset = screenState.scrollOffset
|
||||||
)
|
)
|
||||||
|
|
||||||
LaunchedEffect(isNeedToScrollToTop) {
|
|
||||||
if (isNeedToScrollToTop) {
|
|
||||||
listState.scrollToItem(0)
|
|
||||||
onScrolledToTop()
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LaunchedEffect(listState) {
|
LaunchedEffect(listState) {
|
||||||
snapshotFlow { listState.firstVisibleItemIndex }
|
snapshotFlow { listState.firstVisibleItemIndex }
|
||||||
.debounce(500L)
|
.debounce(500L)
|
||||||
@@ -207,10 +195,10 @@ fun ConversationsScreen(
|
|||||||
|
|
||||||
val toolbarContainerColor by animateColorAsState(
|
val toolbarContainerColor by animateColorAsState(
|
||||||
targetValue =
|
targetValue =
|
||||||
if (currentTheme.enableBlur || !listState.canScrollBackward)
|
if (currentTheme.enableBlur || !listState.canScrollBackward)
|
||||||
MaterialTheme.colorScheme.surface
|
MaterialTheme.colorScheme.surface
|
||||||
else
|
else
|
||||||
MaterialTheme.colorScheme.surfaceColorAtElevation(3.dp),
|
MaterialTheme.colorScheme.surfaceColorAtElevation(3.dp),
|
||||||
label = "toolbarColorAlpha",
|
label = "toolbarColorAlpha",
|
||||||
animationSpec = tween(durationMillis = 50)
|
animationSpec = tween(durationMillis = 50)
|
||||||
)
|
)
|
||||||
@@ -343,8 +331,8 @@ fun ConversationsScreen(
|
|||||||
when (baseError) {
|
when (baseError) {
|
||||||
is BaseError.SessionExpired -> {
|
is BaseError.SessionExpired -> {
|
||||||
ErrorView(
|
ErrorView(
|
||||||
text = "Session expired",
|
text = stringResource(UiR.string.session_expired),
|
||||||
buttonText = "Log out",
|
buttonText = stringResource(UiR.string.action_log_out),
|
||||||
onButtonClick = onSessionExpiredLogOutButtonClicked
|
onButtonClick = onSessionExpiredLogOutButtonClicked
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -352,7 +340,7 @@ fun ConversationsScreen(
|
|||||||
is BaseError.SimpleError -> {
|
is BaseError.SimpleError -> {
|
||||||
ErrorView(
|
ErrorView(
|
||||||
text = baseError.message,
|
text = baseError.message,
|
||||||
buttonText = "Try again",
|
buttonText = stringResource(UiR.string.try_again),
|
||||||
onButtonClick = onRefresh
|
onButtonClick = onRefresh
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -398,6 +386,13 @@ fun ConversationsScreen(
|
|||||||
padding = padding,
|
padding = padding,
|
||||||
onPhotoClicked = onConversationPhotoClicked
|
onPhotoClicked = onConversationPhotoClicked
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (screenState.conversations.isEmpty()) {
|
||||||
|
NoItemsView(
|
||||||
|
buttonText = stringResource(UiR.string.action_refresh),
|
||||||
|
onButtonClick = onRefresh
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ class FriendsViewModelImpl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onRefresh() {
|
override fun onRefresh() {
|
||||||
|
onErrorConsumed()
|
||||||
loadFriends(offset = 0)
|
loadFriends(offset = 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,32 +100,12 @@ class FriendsViewModelImpl(
|
|||||||
friendsUseCase.getOnlineFriends(null, null)
|
friendsUseCase.getOnlineFriends(null, null)
|
||||||
.listenValue(viewModelScope) { state ->
|
.listenValue(viewModelScope) { state ->
|
||||||
state.processState(
|
state.processState(
|
||||||
error = { error ->
|
error = ::handleError,
|
||||||
if (error is State.Error.ApiError) {
|
|
||||||
when (error.errorCode) {
|
|
||||||
VkErrorCode.USER_AUTHORIZATION_FAILED -> {
|
|
||||||
baseError.setValue { BaseError.SessionExpired }
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> Unit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
success = { userIds ->
|
success = { userIds ->
|
||||||
loadUsersByIdsUseCase(userIds = userIds)
|
loadUsersByIdsUseCase(userIds = userIds)
|
||||||
.listenValue(viewModelScope) { state ->
|
.listenValue(viewModelScope) { state ->
|
||||||
state.processState(
|
state.processState(
|
||||||
error = { error ->
|
error = ::handleError,
|
||||||
if (error is State.Error.ApiError) {
|
|
||||||
when (error.errorCode) {
|
|
||||||
VkErrorCode.USER_AUTHORIZATION_FAILED -> {
|
|
||||||
baseError.setValue { BaseError.SessionExpired }
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> Unit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
success = { onlineFriends ->
|
success = { onlineFriends ->
|
||||||
screenState.setValue { old ->
|
screenState.setValue { old ->
|
||||||
old.copy(
|
old.copy(
|
||||||
@@ -142,17 +123,7 @@ class FriendsViewModelImpl(
|
|||||||
friendsUseCase.getFriends(count = LOAD_COUNT, offset = offset)
|
friendsUseCase.getFriends(count = LOAD_COUNT, offset = offset)
|
||||||
.listenValue(viewModelScope) { state ->
|
.listenValue(viewModelScope) { state ->
|
||||||
state.processState(
|
state.processState(
|
||||||
error = { error ->
|
error = ::handleError,
|
||||||
if (error is State.Error.ApiError) {
|
|
||||||
when (error.errorCode) {
|
|
||||||
VkErrorCode.USER_AUTHORIZATION_FAILED -> {
|
|
||||||
baseError.setValue { BaseError.SessionExpired }
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> Unit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
success = { response ->
|
success = { response ->
|
||||||
val itemsCountSufficient = response.size == LOAD_COUNT
|
val itemsCountSufficient = response.size == LOAD_COUNT
|
||||||
canPaginate.setValue { itemsCountSufficient }
|
canPaginate.setValue { itemsCountSufficient }
|
||||||
@@ -197,6 +168,40 @@ class FriendsViewModelImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleError(error: State.Error) {
|
||||||
|
when (error) {
|
||||||
|
is State.Error.ApiError -> {
|
||||||
|
when (error.errorCode) {
|
||||||
|
VkErrorCode.USER_AUTHORIZATION_FAILED -> {
|
||||||
|
baseError.setValue { BaseError.SessionExpired }
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
baseError.setValue {
|
||||||
|
BaseError.SimpleError(message = error.errorMessage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun updateFriendsNames(useContactNames: Boolean) {
|
private fun updateFriendsNames(useContactNames: Boolean) {
|
||||||
val friends = friends.value
|
val friends = friends.value
|
||||||
if (friends.isEmpty()) return
|
if (friends.isEmpty()) return
|
||||||
|
|||||||
+4
-2
@@ -16,7 +16,8 @@ object Friends
|
|||||||
fun NavGraphBuilder.friendsScreen(
|
fun NavGraphBuilder.friendsScreen(
|
||||||
onError: (BaseError) -> Unit,
|
onError: (BaseError) -> Unit,
|
||||||
navController: NavController,
|
navController: NavController,
|
||||||
onPhotoClicked: (url: String) -> Unit
|
onPhotoClicked: (url: String) -> Unit,
|
||||||
|
onMessageClicked: (userId: Int) -> Unit
|
||||||
) {
|
) {
|
||||||
composable<Friends> {
|
composable<Friends> {
|
||||||
val viewModel: FriendsViewModel =
|
val viewModel: FriendsViewModel =
|
||||||
@@ -25,7 +26,8 @@ fun NavGraphBuilder.friendsScreen(
|
|||||||
FriendsRoute(
|
FriendsRoute(
|
||||||
onError = onError,
|
onError = onError,
|
||||||
viewModel = viewModel,
|
viewModel = viewModel,
|
||||||
onPhotoClicked = onPhotoClicked
|
onPhotoClicked = onPhotoClicked,
|
||||||
|
onMessageClicked = onMessageClicked
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.rounded.MailOutline
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@@ -31,7 +35,8 @@ fun FriendItem(
|
|||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
friend: UiFriend,
|
friend: UiFriend,
|
||||||
maxLines: Int,
|
maxLines: Int,
|
||||||
onPhotoClicked: (url: String) -> Unit
|
onPhotoClicked: (url: String) -> Unit,
|
||||||
|
onMessageClicked: (userId: Int) -> Unit
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = modifier.fillMaxWidth(),
|
modifier = modifier.fillMaxWidth(),
|
||||||
@@ -92,9 +97,24 @@ fun FriendItem(
|
|||||||
text = friend.title,
|
text = friend.title,
|
||||||
minLines = 1,
|
minLines = 1,
|
||||||
maxLines = maxLines,
|
maxLines = maxLines,
|
||||||
style = MaterialTheme.typography.headlineSmall.copy(fontSize = 20.sp)
|
style = MaterialTheme.typography.headlineSmall.copy(fontSize = 20.sp),
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(modifier = Modifier.width(16.dp))
|
Spacer(modifier = Modifier.width(16.dp))
|
||||||
|
|
||||||
|
IconButton(
|
||||||
|
onClick = {
|
||||||
|
onMessageClicked(friend.userId)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Rounded.MailOutline,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = MaterialTheme.colorScheme.primary
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.width(16.dp))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ fun FriendsList(
|
|||||||
maxLines: Int,
|
maxLines: Int,
|
||||||
padding: PaddingValues,
|
padding: PaddingValues,
|
||||||
onPhotoClicked: (url: String) -> Unit,
|
onPhotoClicked: (url: String) -> Unit,
|
||||||
|
onMessageClicked: (userId: Int) -> Unit,
|
||||||
setCanScrollBackward: (Boolean) -> Unit
|
setCanScrollBackward: (Boolean) -> Unit
|
||||||
) {
|
) {
|
||||||
LaunchedEffect(listState) {
|
LaunchedEffect(listState) {
|
||||||
@@ -66,7 +67,8 @@ fun FriendsList(
|
|||||||
FriendItem(
|
FriendItem(
|
||||||
friend = friend,
|
friend = friend,
|
||||||
maxLines = maxLines,
|
maxLines = maxLines,
|
||||||
onPhotoClicked = onPhotoClicked
|
onPhotoClicked = onPhotoClicked,
|
||||||
|
onMessageClicked = onMessageClicked
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|||||||
+37
-20
@@ -48,8 +48,8 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import coil.imageLoader
|
import coil.imageLoader
|
||||||
import coil.request.ImageRequest
|
import coil.request.ImageRequest
|
||||||
import dev.chrisbanes.haze.haze
|
import dev.chrisbanes.haze.hazeEffect
|
||||||
import dev.chrisbanes.haze.hazeChild
|
import dev.chrisbanes.haze.hazeSource
|
||||||
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
|
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
|
||||||
import dev.chrisbanes.haze.materials.HazeMaterials
|
import dev.chrisbanes.haze.materials.HazeMaterials
|
||||||
import dev.meloda.fast.friends.FriendsViewModel
|
import dev.meloda.fast.friends.FriendsViewModel
|
||||||
@@ -72,6 +72,7 @@ import dev.meloda.fast.ui.R as UiR
|
|||||||
fun FriendsRoute(
|
fun FriendsRoute(
|
||||||
onError: (BaseError) -> Unit,
|
onError: (BaseError) -> Unit,
|
||||||
onPhotoClicked: (url: String) -> Unit,
|
onPhotoClicked: (url: String) -> Unit,
|
||||||
|
onMessageClicked: (userId: Int) -> Unit,
|
||||||
viewModel: FriendsViewModel = koinViewModel<FriendsViewModelImpl>()
|
viewModel: FriendsViewModel = koinViewModel<FriendsViewModelImpl>()
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
@@ -99,11 +100,12 @@ fun FriendsRoute(
|
|||||||
onPaginationConditionsMet = viewModel::onPaginationConditionsMet,
|
onPaginationConditionsMet = viewModel::onPaginationConditionsMet,
|
||||||
onRefresh = viewModel::onRefresh,
|
onRefresh = viewModel::onRefresh,
|
||||||
onPhotoClicked = onPhotoClicked,
|
onPhotoClicked = onPhotoClicked,
|
||||||
|
onMessageClicked = onMessageClicked,
|
||||||
setSelectedTabIndex = viewModel::onTabSelected,
|
setSelectedTabIndex = viewModel::onTabSelected,
|
||||||
setScrollIndex = viewModel::setScrollIndex,
|
setScrollIndex = viewModel::setScrollIndex,
|
||||||
setScrollOffset = viewModel::setScrollOffset,
|
setScrollOffset = viewModel::setScrollOffset,
|
||||||
setScrollIndexOnline = viewModel::setScrollIndexOnline,
|
setScrollIndexOnline = viewModel::setScrollIndexOnline,
|
||||||
setScrollOffsetOnline = viewModel::setScrollOffsetOnline,
|
setScrollOffsetOnline = viewModel::setScrollOffsetOnline
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,11 +122,12 @@ fun FriendsScreen(
|
|||||||
onPaginationConditionsMet: () -> Unit = {},
|
onPaginationConditionsMet: () -> Unit = {},
|
||||||
onRefresh: () -> Unit = {},
|
onRefresh: () -> Unit = {},
|
||||||
onPhotoClicked: (url: String) -> Unit = {},
|
onPhotoClicked: (url: String) -> Unit = {},
|
||||||
|
onMessageClicked: (userId: Int) -> Unit = {},
|
||||||
setSelectedTabIndex: (Int) -> Unit = {},
|
setSelectedTabIndex: (Int) -> Unit = {},
|
||||||
setScrollIndex: (Int) -> Unit = {},
|
setScrollIndex: (Int) -> Unit = {},
|
||||||
setScrollOffset: (Int) -> Unit = {},
|
setScrollOffset: (Int) -> Unit = {},
|
||||||
setScrollIndexOnline: (Int) -> Unit = {},
|
setScrollIndexOnline: (Int) -> Unit = {},
|
||||||
setScrollOffsetOnline: (Int) -> Unit = {},
|
setScrollOffsetOnline: (Int) -> Unit = {}
|
||||||
) {
|
) {
|
||||||
val currentTheme = LocalThemeConfig.current
|
val currentTheme = LocalThemeConfig.current
|
||||||
|
|
||||||
@@ -231,7 +234,7 @@ fun FriendsScreen(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.then(
|
.then(
|
||||||
if (currentTheme.enableBlur) {
|
if (currentTheme.enableBlur) {
|
||||||
Modifier.hazeChild(
|
Modifier.hazeEffect(
|
||||||
state = hazeState,
|
state = hazeState,
|
||||||
style = HazeMaterials.thick()
|
style = HazeMaterials.thick()
|
||||||
)
|
)
|
||||||
@@ -281,12 +284,24 @@ fun FriendsScreen(
|
|||||||
}
|
}
|
||||||
) { padding ->
|
) { padding ->
|
||||||
when {
|
when {
|
||||||
baseError is BaseError.SessionExpired -> {
|
baseError != null -> {
|
||||||
ErrorView(
|
when (baseError) {
|
||||||
text = "Session expired",
|
is BaseError.SessionExpired -> {
|
||||||
buttonText = "Log out",
|
ErrorView(
|
||||||
onButtonClick = onSessionExpiredLogOutButtonClicked
|
text = stringResource(UiR.string.session_expired),
|
||||||
)
|
buttonText = stringResource(UiR.string.action_log_out),
|
||||||
|
onButtonClick = onSessionExpiredLogOutButtonClicked
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
is BaseError.SimpleError -> {
|
||||||
|
ErrorView(
|
||||||
|
text = baseError.message,
|
||||||
|
buttonText = stringResource(UiR.string.try_again),
|
||||||
|
onButtonClick = onRefresh
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
screenState.isLoading && screenState.friends.isEmpty() -> FullScreenLoader()
|
screenState.isLoading && screenState.friends.isEmpty() -> FullScreenLoader()
|
||||||
@@ -333,15 +348,17 @@ fun FriendsScreen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
val friendsToDisplay = if (index == 0) {
|
val friendsToDisplay = remember(index) {
|
||||||
screenState.friends
|
if (index == 0) {
|
||||||
} else {
|
screenState.friends
|
||||||
screenState.onlineFriends
|
} else {
|
||||||
|
screenState.onlineFriends
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FriendsList(
|
FriendsList(
|
||||||
modifier = if (currentTheme.enableBlur) {
|
modifier = if (currentTheme.enableBlur) {
|
||||||
Modifier.haze(state = hazeState)
|
Modifier.hazeSource(state = hazeState)
|
||||||
} else {
|
} else {
|
||||||
Modifier
|
Modifier
|
||||||
}.fillMaxSize(),
|
}.fillMaxSize(),
|
||||||
@@ -351,6 +368,7 @@ fun FriendsScreen(
|
|||||||
maxLines = maxLines,
|
maxLines = maxLines,
|
||||||
padding = padding,
|
padding = padding,
|
||||||
onPhotoClicked = onPhotoClicked,
|
onPhotoClicked = onPhotoClicked,
|
||||||
|
onMessageClicked = onMessageClicked,
|
||||||
setCanScrollBackward = { can ->
|
setCanScrollBackward = { can ->
|
||||||
canScrollBackward = can
|
canScrollBackward = can
|
||||||
}
|
}
|
||||||
@@ -358,10 +376,9 @@ fun FriendsScreen(
|
|||||||
|
|
||||||
if (friendsToDisplay.isEmpty()) {
|
if (friendsToDisplay.isEmpty()) {
|
||||||
NoItemsView(
|
NoItemsView(
|
||||||
modifier = Modifier
|
customText = if (index == 1) stringResource(UiR.string.no_online_friends) else null,
|
||||||
.padding(padding.calculateTopPadding())
|
buttonText = stringResource(UiR.string.action_refresh),
|
||||||
.padding(top = 16.dp),
|
onButtonClick = onRefresh
|
||||||
customText = "No${if (index == 1) " online" else ""} friends :("
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user