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
@@ -159,7 +159,7 @@ class MainActivity : AppCompatActivity() {
selectedColorScheme = theme.selectedColorScheme,
usingAmoledBackground = theme.usingAmoledBackground,
usingBlur = theme.usingBlur,
multiline = theme.multiline,
isMultiline = theme.isMultiline,
isDeviceCompact = isDeviceCompact
)
) {
@@ -85,4 +85,11 @@ object SettingsController {
var deviceId: String
get() = get("device_id", "")
set(value) = put("device_id", value)
var enablePullToRefresh: Boolean
get() = get(
SettingsKeys.KEY_ENABLE_PULL_TO_REFRESH,
SettingsKeys.DEFAULT_VALUE_ENABLE_PULL_TO_REFRESH
)
set(value) = put(SettingsKeys.KEY_ENABLE_PULL_TO_REFRESH, value)
}
@@ -9,6 +9,8 @@ object SettingsKeys {
const val KEY_GENERAL = "general"
const val KEY_USE_CONTACT_NAMES = "general_use_contact_names"
const val DEFAULT_VALUE_USE_CONTACT_NAMES = false
const val KEY_ENABLE_PULL_TO_REFRESH = "general_pull_to_refresh"
const val DEFAULT_VALUE_ENABLE_PULL_TO_REFRESH = false
const val KEY_SHOW_EMOJI_BUTTON = "general_show_emoji_button"
const val DEFAULT_VALUE_KEY_SHOW_EMOJI_BUTTON = false
@@ -17,6 +17,7 @@ interface UserSettings {
val debugSettingsEnabled: StateFlow<Boolean>
val useContactNames: StateFlow<Boolean>
val language: StateFlow<String>
val enablePullToRefresh: StateFlow<Boolean>
fun updateUsingDarkTheme()
fun useDarkThemeChanged(use: Boolean)
@@ -30,6 +31,7 @@ interface UserSettings {
fun enableDebugSettings(enable: Boolean)
fun onUseContactNamesChanged(use: Boolean)
fun onLanguageChanged(newLanguage: String)
fun onEnablePullToRefreshChanged(enable: Boolean)
}
class UserSettingsImpl(
@@ -44,7 +46,7 @@ class UserSettingsImpl(
selectedColorScheme = selectedColorScheme(),
usingAmoledBackground = isUsingAmoledBackground(),
usingBlur = isUsingBlur(),
multiline = isMultiline(),
isMultiline = isMultiline(),
isDeviceCompact = false
)
)
@@ -75,6 +77,8 @@ class UserSettingsImpl(
override val language = MutableStateFlow("")
override val enablePullToRefresh = MutableStateFlow(SettingsController.enablePullToRefresh)
override fun updateUsingDarkTheme() {
useDarkThemeChanged(
isUsingDarkMode(
@@ -105,7 +109,7 @@ class UserSettingsImpl(
}
override fun useMultiline(use: Boolean) {
theme.value = theme.value.copy(multiline = use)
theme.value = theme.value.copy(isMultiline = use)
}
override fun setLongPollStateToApply(newState: LongPollState) {
@@ -133,4 +137,8 @@ class UserSettingsImpl(
override fun onLanguageChanged(newLanguage: String) {
language.update { newLanguage }
}
override fun onEnablePullToRefreshChanged(enable: Boolean) {
enablePullToRefresh.update { enable }
}
}
@@ -6,6 +6,6 @@ data class ThemeConfig(
val selectedColorScheme: Int,
val usingAmoledBackground: Boolean,
val usingBlur: Boolean,
val multiline: Boolean,
val isMultiline: Boolean,
val isDeviceCompact: Boolean
)
@@ -111,7 +111,7 @@ val LocalTheme = compositionLocalOf {
selectedColorScheme = 0,
usingAmoledBackground = false,
usingBlur = false,
multiline = false,
isMultiline = false,
isDeviceCompact = false
)
}
+1
View File
@@ -262,4 +262,5 @@
<string name="warning_confirmation">Confirmation</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="settings_general_enable_pull_to_refresh_title">Enable pull to refresh</string>
</resources>
@@ -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,6 +355,7 @@ fun ChatMaterialsScreen(
}
}
if (enablePullToRefresh) {
if (pullToRefreshState.isRefreshing) {
LaunchedEffect(true) {
onRefresh()
@@ -365,11 +371,11 @@ fun ChatMaterialsScreen(
PullToRefreshContainer(
state = pullToRefreshState,
modifier = Modifier
.alpha(pullToRefreshAlpha)
.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,6 +367,7 @@ fun ConversationsScreen(
padding = padding
)
if (enablePullToRefresh) {
if (pullToRefreshState.isRefreshing) {
LaunchedEffect(true) {
onRefresh()
@@ -377,7 +383,6 @@ fun ConversationsScreen(
PullToRefreshContainer(
state = pullToRefreshState,
modifier = Modifier
.alpha(pullToRefreshAlpha)
.align(Alignment.TopCenter)
.padding(top = padding.calculateTopPadding()),
contentColor = MaterialTheme.colorScheme.primary
@@ -386,6 +391,7 @@ fun ConversationsScreen(
}
}
}
}
}
// TODO: 26.08.2023, Danil Nikolaev: remove usage of viewModel
@@ -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,7 +174,8 @@ fun FriendsScreen(
mutableIntStateOf(0)
}
val tabItems = listOf(
val tabItems = remember {
listOf(
TabItem(
titleResId = UiR.string.title_friends_all,
unselectedIconResId = null,
@@ -185,6 +187,7 @@ fun FriendsScreen(
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,6 +319,7 @@ fun FriendsScreen(
)
}
if (enablePullToRefresh) {
if (pullToRefreshState.isRefreshing) {
LaunchedEffect(true) {
onRefresh()
@@ -326,7 +336,6 @@ fun FriendsScreen(
state = pullToRefreshState,
modifier = Modifier
.padding(top = padding.calculateTopPadding())
.alpha(pullToRefreshAlpha)
.align(Alignment.TopCenter),
contentColor = MaterialTheme.colorScheme.primary
)
@@ -336,4 +345,5 @@ fun FriendsScreen(
}
}
}
}
}
@@ -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,
)
}