ability to enable/disable pull to refresh

This commit is contained in:
2024-07-16 00:15:15 +03:00
parent 789283fcff
commit 46d3fe63fa
16 changed files with 155 additions and 104 deletions
@@ -54,7 +54,6 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.res.stringResource
@@ -65,6 +64,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.meloda.app.fast.chatmaterials.ChatMaterialsViewModel
import com.meloda.app.fast.chatmaterials.ChatMaterialsViewModelImpl
import com.meloda.app.fast.chatmaterials.model.ChatMaterialsScreenState
import com.meloda.app.fast.datastore.UserSettings
import com.meloda.app.fast.ui.R
import com.meloda.app.fast.ui.theme.LocalTheme
import dev.chrisbanes.haze.HazeState
@@ -73,16 +73,22 @@ import dev.chrisbanes.haze.hazeChild
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
import dev.chrisbanes.haze.materials.HazeMaterials
import org.koin.androidx.compose.koinViewModel
import org.koin.compose.koinInject
@Composable
fun ChatMaterialsRoute(
onBack: () -> Unit,
viewModel: ChatMaterialsViewModel = koinViewModel<ChatMaterialsViewModelImpl>()
) {
val userSettings: UserSettings = koinInject()
val enablePullToRefresh by userSettings.enablePullToRefresh.collectAsStateWithLifecycle()
val screenState by viewModel.screenState.collectAsStateWithLifecycle()
ChatMaterialsScreen(
screenState = screenState,
enablePullToRefresh = enablePullToRefresh,
onBack = onBack,
onTypeChanged = viewModel::onTypeChanged,
onRefreshDropdownItemClicked = viewModel::onRefresh,
@@ -97,7 +103,8 @@ fun ChatMaterialsRoute(
)
@Composable
fun ChatMaterialsScreen(
screenState: ChatMaterialsScreenState,
screenState: ChatMaterialsScreenState = ChatMaterialsScreenState.EMPTY,
enablePullToRefresh: Boolean = false,
onBack: () -> Unit = {},
onTypeChanged: (String) -> Unit = {},
onRefreshDropdownItemClicked: () -> Unit = {},
@@ -168,12 +175,6 @@ fun ChatMaterialsScreen(
)
)
val pullToRefreshAlpha by animateFloatAsState(
targetValue = if (!canScrollBackward) 1f else 0f,
label = "pullToRefreshAlpha",
animationSpec = tween(durationMillis = 50)
)
val pullToRefreshState = rememberPullToRefreshState()
Scaffold(
@@ -286,7 +287,11 @@ fun ChatMaterialsScreen(
.fillMaxSize()
.padding(start = padding.calculateStartPadding(LayoutDirection.Ltr))
.padding(end = padding.calculateEndPadding(LayoutDirection.Ltr))
.nestedScroll(pullToRefreshState.nestedScrollConnection)
.then(
if (enablePullToRefresh) {
Modifier.nestedScroll(pullToRefreshState.nestedScrollConnection)
} else Modifier
)
) {
if (checkedTypeIndex in listOf(0, 1)) {
LazyVerticalGrid(
@@ -350,26 +355,27 @@ fun ChatMaterialsScreen(
}
}
if (pullToRefreshState.isRefreshing) {
LaunchedEffect(true) {
onRefresh()
if (enablePullToRefresh) {
if (pullToRefreshState.isRefreshing) {
LaunchedEffect(true) {
onRefresh()
}
}
}
LaunchedEffect(screenState.isLoading) {
if (!screenState.isLoading) {
pullToRefreshState.endRefresh()
LaunchedEffect(screenState.isLoading) {
if (!screenState.isLoading) {
pullToRefreshState.endRefresh()
}
}
}
PullToRefreshContainer(
state = pullToRefreshState,
modifier = Modifier
.alpha(pullToRefreshAlpha)
.align(Alignment.TopCenter)
.padding(top = padding.calculateTopPadding()),
contentColor = MaterialTheme.colorScheme.primary
)
PullToRefreshContainer(
state = pullToRefreshState,
modifier = Modifier
.align(Alignment.TopCenter)
.padding(top = padding.calculateTopPadding()),
contentColor = MaterialTheme.colorScheme.primary
)
}
}
}
}
@@ -51,7 +51,6 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
@@ -72,6 +71,7 @@ import com.meloda.app.fast.conversations.ConversationsViewModelImpl
import com.meloda.app.fast.conversations.model.ConversationOption
import com.meloda.app.fast.conversations.model.ConversationsScreenState
import com.meloda.app.fast.conversations.model.UiConversation
import com.meloda.app.fast.datastore.UserSettings
import com.meloda.app.fast.model.BaseError
import com.meloda.app.fast.ui.components.ErrorView
import com.meloda.app.fast.ui.components.FullScreenLoader
@@ -86,6 +86,7 @@ import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
import dev.chrisbanes.haze.materials.HazeMaterials
import kotlinx.coroutines.launch
import org.koin.androidx.compose.koinViewModel
import org.koin.compose.koinInject
import com.meloda.app.fast.ui.R as UiR
@Composable
@@ -96,6 +97,10 @@ fun ConversationsRoute(
) {
val context = LocalContext.current
val userSettings: UserSettings = koinInject()
val enablePullToRefresh by userSettings.enablePullToRefresh.collectAsStateWithLifecycle()
val screenState by viewModel.screenState.collectAsStateWithLifecycle()
val baseError by viewModel.baseError.collectAsStateWithLifecycle()
val canPaginate by viewModel.canPaginate.collectAsStateWithLifecycle()
@@ -115,6 +120,7 @@ fun ConversationsRoute(
screenState = screenState,
baseError = baseError,
canPaginate = canPaginate,
enablePullToRefresh = enablePullToRefresh,
onSessionExpiredLogOutButtonClicked = { onError(BaseError.SessionExpired) },
onConversationItemClicked = { id ->
onConversationItemClicked(id)
@@ -143,6 +149,7 @@ fun ConversationsScreen(
screenState: ConversationsScreenState = ConversationsScreenState.EMPTY,
baseError: BaseError? = null,
canPaginate: Boolean = false,
enablePullToRefresh: Boolean = false,
onSessionExpiredLogOutButtonClicked: () -> Unit,
onConversationItemClicked: (conversationId: Int) -> Unit = {},
onConversationItemLongClicked: (conversation: UiConversation) -> Unit = {},
@@ -155,7 +162,7 @@ fun ConversationsScreen(
val currentTheme = LocalTheme.current
val maxLines by remember(currentTheme) {
mutableIntStateOf(if (currentTheme.multiline) 2 else 1)
mutableIntStateOf(if (currentTheme.isMultiline) 2 else 1)
}
val listState = rememberLazyListState()
@@ -196,12 +203,6 @@ fun ConversationsScreen(
animationSpec = tween(durationMillis = 50)
)
val pullToRefreshAlpha by animateFloatAsState(
targetValue = if (!listState.canScrollBackward) 1f else 0f,
label = "pullToRefreshAlpha",
animationSpec = tween(durationMillis = 50)
)
Scaffold(
modifier = Modifier.fillMaxSize(),
contentWindowInsets = WindowInsets.statusBars,
@@ -342,7 +343,11 @@ fun ConversationsScreen(
.padding(start = padding.calculateStartPadding(LayoutDirection.Ltr))
.padding(end = padding.calculateEndPadding(LayoutDirection.Ltr))
.padding(bottom = padding.calculateBottomPadding())
.nestedScroll(pullToRefreshState.nestedScrollConnection)
.then(
if (enablePullToRefresh) {
Modifier.nestedScroll(pullToRefreshState.nestedScrollConnection)
} else Modifier
)
) {
ConversationsListComposable(
onConversationsClick = onConversationItemClicked,
@@ -362,26 +367,27 @@ fun ConversationsScreen(
padding = padding
)
if (pullToRefreshState.isRefreshing) {
LaunchedEffect(true) {
onRefresh()
if (enablePullToRefresh) {
if (pullToRefreshState.isRefreshing) {
LaunchedEffect(true) {
onRefresh()
}
}
}
LaunchedEffect(screenState.isLoading) {
if (!screenState.isLoading) {
pullToRefreshState.endRefresh()
LaunchedEffect(screenState.isLoading) {
if (!screenState.isLoading) {
pullToRefreshState.endRefresh()
}
}
}
PullToRefreshContainer(
state = pullToRefreshState,
modifier = Modifier
.alpha(pullToRefreshAlpha)
.align(Alignment.TopCenter)
.padding(top = padding.calculateTopPadding()),
contentColor = MaterialTheme.colorScheme.primary
)
PullToRefreshContainer(
state = pullToRefreshState,
modifier = Modifier
.align(Alignment.TopCenter)
.padding(top = padding.calculateTopPadding()),
contentColor = MaterialTheme.colorScheme.primary
)
}
}
}
}
@@ -39,7 +39,6 @@ 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.alpha
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
@@ -50,6 +49,7 @@ import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import coil.imageLoader
import coil.request.ImageRequest
import com.meloda.app.fast.datastore.UserSettings
import com.meloda.app.fast.friends.FriendsViewModel
import com.meloda.app.fast.friends.FriendsViewModelImpl
import com.meloda.app.fast.friends.model.FriendsScreenState
@@ -66,6 +66,7 @@ import dev.chrisbanes.haze.hazeChild
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
import dev.chrisbanes.haze.materials.HazeMaterials
import org.koin.androidx.compose.koinViewModel
import org.koin.compose.koinInject
import com.meloda.app.fast.ui.R as UiR
@Composable
@@ -75,8 +76,12 @@ fun FriendsRoute(
) {
val context = LocalContext.current
val baseError by viewModel.baseError.collectAsStateWithLifecycle()
val userSettings: UserSettings = koinInject()
val enablePullToRefresh by userSettings.enablePullToRefresh.collectAsStateWithLifecycle()
val screenState by viewModel.screenState.collectAsStateWithLifecycle()
val baseError by viewModel.baseError.collectAsStateWithLifecycle()
val canPaginate by viewModel.canPaginate.collectAsStateWithLifecycle()
val imagesToPreload by viewModel.imagesToPreload.collectAsStateWithLifecycle()
@@ -93,6 +98,7 @@ fun FriendsRoute(
FriendsScreen(
screenState = screenState,
baseError = baseError,
enablePullToRefresh = enablePullToRefresh,
canPaginate = canPaginate,
onSessionExpiredLogOutButtonClicked = { onError(BaseError.SessionExpired) },
onPaginationConditionsMet = viewModel::onPaginationConditionsMet,
@@ -110,6 +116,7 @@ fun FriendsRoute(
fun FriendsScreen(
screenState: FriendsScreenState = FriendsScreenState.EMPTY,
baseError: BaseError? = null,
enablePullToRefresh: Boolean = false,
canPaginate: Boolean = false,
onSessionExpiredLogOutButtonClicked: () -> Unit = {},
onPaginationConditionsMet: () -> Unit = {},
@@ -119,7 +126,7 @@ fun FriendsScreen(
val maxLines by remember {
derivedStateOf {
if (currentTheme.multiline) 2 else 1
if (currentTheme.isMultiline) 2 else 1
}
}
@@ -141,12 +148,6 @@ fun FriendsScreen(
val hazeState = LocalHazeState.current
val pullToRefreshAlpha by animateFloatAsState(
targetValue = if (!listState.canScrollBackward) 1f else 0f,
label = "pullToRefreshAlpha",
animationSpec = tween(durationMillis = 50)
)
val topBarContainerColorAlpha by animateFloatAsState(
targetValue = if (!currentTheme.usingBlur || !listState.canScrollBackward) 1f else 0f,
label = "toolbarColorAlpha",
@@ -173,18 +174,20 @@ fun FriendsScreen(
mutableIntStateOf(0)
}
val tabItems = listOf(
TabItem(
titleResId = UiR.string.title_friends_all,
unselectedIconResId = null,
selectedIconResId = null
),
TabItem(
titleResId = UiR.string.title_friends_online,
unselectedIconResId = null,
selectedIconResId = null
val tabItems = remember {
listOf(
TabItem(
titleResId = UiR.string.title_friends_all,
unselectedIconResId = null,
selectedIconResId = null
),
TabItem(
titleResId = UiR.string.title_friends_online,
unselectedIconResId = null,
selectedIconResId = null
)
)
)
}
Scaffold(
modifier = Modifier.fillMaxSize(),
@@ -254,8 +257,6 @@ fun FriendsScreen(
screenState.isLoading && screenState.friends.isEmpty() -> FullScreenLoader()
else -> {
val pullToRefreshState = rememberPullToRefreshState()
val pagerState = rememberPagerState { tabItems.size }
LaunchedEffect(selectedTabIndex) {
@@ -270,6 +271,8 @@ fun FriendsScreen(
}
}
val pullToRefreshState = rememberPullToRefreshState()
Box(modifier = Modifier.fillMaxSize()) {
HorizontalPager(
state = pagerState,
@@ -281,7 +284,13 @@ fun FriendsScreen(
.padding(start = padding.calculateStartPadding(LayoutDirection.Ltr))
.padding(end = padding.calculateEndPadding(LayoutDirection.Ltr))
.padding(bottom = padding.calculateBottomPadding())
.nestedScroll(pullToRefreshState.nestedScrollConnection)
.then(
if (enablePullToRefresh) {
Modifier.nestedScroll(
pullToRefreshState.nestedScrollConnection
)
} else Modifier
)
) {
val friendsToDisplay = screenState.friends
@@ -310,26 +319,27 @@ fun FriendsScreen(
)
}
if (pullToRefreshState.isRefreshing) {
LaunchedEffect(true) {
onRefresh()
if (enablePullToRefresh) {
if (pullToRefreshState.isRefreshing) {
LaunchedEffect(true) {
onRefresh()
}
}
}
LaunchedEffect(screenState.isLoading) {
if (!screenState.isLoading) {
pullToRefreshState.endRefresh()
LaunchedEffect(screenState.isLoading) {
if (!screenState.isLoading) {
pullToRefreshState.endRefresh()
}
}
}
PullToRefreshContainer(
state = pullToRefreshState,
modifier = Modifier
.padding(top = padding.calculateTopPadding())
.alpha(pullToRefreshAlpha)
.align(Alignment.TopCenter),
contentColor = MaterialTheme.colorScheme.primary
)
PullToRefreshContainer(
state = pullToRefreshState,
modifier = Modifier
.padding(top = padding.calculateTopPadding())
.align(Alignment.TopCenter),
contentColor = MaterialTheme.colorScheme.primary
)
}
}
}
}
@@ -210,6 +210,11 @@ class SettingsViewModelImpl(
val isUsing = newValue as? Boolean ?: false
userSettings.onUseContactNamesChanged(isUsing)
}
SettingsKeys.KEY_ENABLE_PULL_TO_REFRESH -> {
val enable = newValue as? Boolean ?: false
userSettings.onEnablePullToRefreshChanged(enable)
}
}
}
@@ -250,6 +255,11 @@ class SettingsViewModelImpl(
text = UiText.Resource(UiR.string.settings_general_contact_names_summary),
defaultValue = SettingsKeys.DEFAULT_VALUE_USE_CONTACT_NAMES
)
val generalEnablePullToRefresh = SettingsItem.Switch(
key = SettingsKeys.KEY_ENABLE_PULL_TO_REFRESH,
defaultValue = SettingsKeys.DEFAULT_VALUE_ENABLE_PULL_TO_REFRESH,
title = UiText.Resource(UiR.string.settings_general_enable_pull_to_refresh_title)
)
val appearanceTitle = SettingsItem.Title(
key = SettingsKeys.KEY_APPEARANCE,
@@ -381,7 +391,8 @@ class SettingsViewModelImpl(
)
val generalList = listOf(
generalTitle,
generalUseContactNames
generalUseContactNames,
generalEnablePullToRefresh
)
val appearanceList = listOf(
appearanceTitle,
@@ -81,7 +81,7 @@ fun ListItem(
Text(
text = title,
style = MaterialTheme.typography.headlineSmall,
maxLines = if (currentTheme.multiline) Int.MAX_VALUE else 1,
maxLines = if (currentTheme.isMultiline) Int.MAX_VALUE else 1,
overflow = TextOverflow.Ellipsis,
)
}
@@ -94,7 +94,7 @@ fun ListItem(
Text(
text = text,
style = MaterialTheme.typography.bodyMedium,
maxLines = if (currentTheme.multiline) Int.MAX_VALUE else 1,
maxLines = if (currentTheme.isMultiline) Int.MAX_VALUE else 1,
overflow = TextOverflow.Ellipsis,
)
}
@@ -65,7 +65,7 @@ fun SwitchItem(
Text(
text = title,
style = MaterialTheme.typography.headlineSmall,
maxLines = if (currentTheme.multiline) Int.MAX_VALUE else 1,
maxLines = if (currentTheme.isMultiline) Int.MAX_VALUE else 1,
overflow = TextOverflow.Ellipsis,
)
}
@@ -78,7 +78,7 @@ fun SwitchItem(
Text(
text = text,
style = MaterialTheme.typography.bodyMedium,
maxLines = if (currentTheme.multiline) Int.MAX_VALUE else 1,
maxLines = if (currentTheme.isMultiline) Int.MAX_VALUE else 1,
overflow = TextOverflow.Ellipsis,
)
}
@@ -93,7 +93,7 @@ fun TextFieldItem(
Text(
text = title,
style = MaterialTheme.typography.headlineSmall,
maxLines = if (currentTheme.multiline) Int.MAX_VALUE else 1,
maxLines = if (currentTheme.isMultiline) Int.MAX_VALUE else 1,
overflow = TextOverflow.Ellipsis,
)
}
@@ -106,7 +106,7 @@ fun TextFieldItem(
Text(
text = text,
style = MaterialTheme.typography.bodyMedium,
maxLines = if (currentTheme.multiline) Int.MAX_VALUE else 1,
maxLines = if (currentTheme.isMultiline) Int.MAX_VALUE else 1,
overflow = TextOverflow.Ellipsis,
)
}
@@ -32,7 +32,7 @@ fun TitleItem(
bottom = 4.dp
)
.animateContentSize(),
maxLines = if (currentTheme.multiline) Int.MAX_VALUE else 1,
maxLines = if (currentTheme.isMultiline) Int.MAX_VALUE else 1,
overflow = TextOverflow.Ellipsis,
)
}
@@ -62,7 +62,7 @@ fun TitleTextItem(
Text(
text = title,
style = MaterialTheme.typography.headlineSmall,
maxLines = if (currentTheme.multiline) Int.MAX_VALUE else 1,
maxLines = if (currentTheme.isMultiline) Int.MAX_VALUE else 1,
overflow = TextOverflow.Ellipsis,
)
}
@@ -75,7 +75,7 @@ fun TitleTextItem(
Text(
text = text,
style = MaterialTheme.typography.bodyMedium,
maxLines = if (currentTheme.multiline) Int.MAX_VALUE else 1,
maxLines = if (currentTheme.isMultiline) Int.MAX_VALUE else 1,
overflow = TextOverflow.Ellipsis,
)
}