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, selectedColorScheme = theme.selectedColorScheme,
usingAmoledBackground = theme.usingAmoledBackground, usingAmoledBackground = theme.usingAmoledBackground,
usingBlur = theme.usingBlur, usingBlur = theme.usingBlur,
multiline = theme.multiline, isMultiline = theme.isMultiline,
isDeviceCompact = isDeviceCompact isDeviceCompact = isDeviceCompact
) )
) { ) {
@@ -85,4 +85,11 @@ object SettingsController {
var deviceId: String var deviceId: String
get() = get("device_id", "") get() = get("device_id", "")
set(value) = put("device_id", value) 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_GENERAL = "general"
const val KEY_USE_CONTACT_NAMES = "general_use_contact_names" const val KEY_USE_CONTACT_NAMES = "general_use_contact_names"
const val DEFAULT_VALUE_USE_CONTACT_NAMES = false 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 KEY_SHOW_EMOJI_BUTTON = "general_show_emoji_button"
const val DEFAULT_VALUE_KEY_SHOW_EMOJI_BUTTON = false const val DEFAULT_VALUE_KEY_SHOW_EMOJI_BUTTON = false
@@ -17,6 +17,7 @@ interface UserSettings {
val debugSettingsEnabled: StateFlow<Boolean> val debugSettingsEnabled: StateFlow<Boolean>
val useContactNames: StateFlow<Boolean> val useContactNames: StateFlow<Boolean>
val language: StateFlow<String> val language: StateFlow<String>
val enablePullToRefresh: StateFlow<Boolean>
fun updateUsingDarkTheme() fun updateUsingDarkTheme()
fun useDarkThemeChanged(use: Boolean) fun useDarkThemeChanged(use: Boolean)
@@ -30,6 +31,7 @@ interface UserSettings {
fun enableDebugSettings(enable: Boolean) fun enableDebugSettings(enable: Boolean)
fun onUseContactNamesChanged(use: Boolean) fun onUseContactNamesChanged(use: Boolean)
fun onLanguageChanged(newLanguage: String) fun onLanguageChanged(newLanguage: String)
fun onEnablePullToRefreshChanged(enable: Boolean)
} }
class UserSettingsImpl( class UserSettingsImpl(
@@ -44,7 +46,7 @@ class UserSettingsImpl(
selectedColorScheme = selectedColorScheme(), selectedColorScheme = selectedColorScheme(),
usingAmoledBackground = isUsingAmoledBackground(), usingAmoledBackground = isUsingAmoledBackground(),
usingBlur = isUsingBlur(), usingBlur = isUsingBlur(),
multiline = isMultiline(), isMultiline = isMultiline(),
isDeviceCompact = false isDeviceCompact = false
) )
) )
@@ -75,6 +77,8 @@ class UserSettingsImpl(
override val language = MutableStateFlow("") override val language = MutableStateFlow("")
override val enablePullToRefresh = MutableStateFlow(SettingsController.enablePullToRefresh)
override fun updateUsingDarkTheme() { override fun updateUsingDarkTheme() {
useDarkThemeChanged( useDarkThemeChanged(
isUsingDarkMode( isUsingDarkMode(
@@ -105,7 +109,7 @@ class UserSettingsImpl(
} }
override fun useMultiline(use: Boolean) { override fun useMultiline(use: Boolean) {
theme.value = theme.value.copy(multiline = use) theme.value = theme.value.copy(isMultiline = use)
} }
override fun setLongPollStateToApply(newState: LongPollState) { override fun setLongPollStateToApply(newState: LongPollState) {
@@ -133,4 +137,8 @@ class UserSettingsImpl(
override fun onLanguageChanged(newLanguage: String) { override fun onLanguageChanged(newLanguage: String) {
language.update { newLanguage } language.update { newLanguage }
} }
override fun onEnablePullToRefreshChanged(enable: Boolean) {
enablePullToRefresh.update { enable }
}
} }
@@ -6,6 +6,6 @@ data class ThemeConfig(
val selectedColorScheme: Int, val selectedColorScheme: Int,
val usingAmoledBackground: Boolean, val usingAmoledBackground: Boolean,
val usingBlur: Boolean, val usingBlur: Boolean,
val multiline: Boolean, val isMultiline: Boolean,
val isDeviceCompact: Boolean val isDeviceCompact: Boolean
) )
@@ -111,7 +111,7 @@ val LocalTheme = compositionLocalOf {
selectedColorScheme = 0, selectedColorScheme = 0,
usingAmoledBackground = false, usingAmoledBackground = false,
usingBlur = false, usingBlur = false,
multiline = false, isMultiline = false,
isDeviceCompact = false isDeviceCompact = false
) )
} }
+1
View File
@@ -262,4 +262,5 @@
<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>
</resources> </resources>
@@ -54,7 +54,6 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.res.stringResource 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.ChatMaterialsViewModel
import com.meloda.app.fast.chatmaterials.ChatMaterialsViewModelImpl import com.meloda.app.fast.chatmaterials.ChatMaterialsViewModelImpl
import com.meloda.app.fast.chatmaterials.model.ChatMaterialsScreenState 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.R
import com.meloda.app.fast.ui.theme.LocalTheme import com.meloda.app.fast.ui.theme.LocalTheme
import dev.chrisbanes.haze.HazeState 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.ExperimentalHazeMaterialsApi
import dev.chrisbanes.haze.materials.HazeMaterials import dev.chrisbanes.haze.materials.HazeMaterials
import org.koin.androidx.compose.koinViewModel import org.koin.androidx.compose.koinViewModel
import org.koin.compose.koinInject
@Composable @Composable
fun ChatMaterialsRoute( fun ChatMaterialsRoute(
onBack: () -> Unit, onBack: () -> Unit,
viewModel: ChatMaterialsViewModel = koinViewModel<ChatMaterialsViewModelImpl>() viewModel: ChatMaterialsViewModel = koinViewModel<ChatMaterialsViewModelImpl>()
) { ) {
val userSettings: UserSettings = koinInject()
val enablePullToRefresh by userSettings.enablePullToRefresh.collectAsStateWithLifecycle()
val screenState by viewModel.screenState.collectAsStateWithLifecycle() val screenState by viewModel.screenState.collectAsStateWithLifecycle()
ChatMaterialsScreen( ChatMaterialsScreen(
screenState = screenState, screenState = screenState,
enablePullToRefresh = enablePullToRefresh,
onBack = onBack, onBack = onBack,
onTypeChanged = viewModel::onTypeChanged, onTypeChanged = viewModel::onTypeChanged,
onRefreshDropdownItemClicked = viewModel::onRefresh, onRefreshDropdownItemClicked = viewModel::onRefresh,
@@ -97,7 +103,8 @@ fun ChatMaterialsRoute(
) )
@Composable @Composable
fun ChatMaterialsScreen( fun ChatMaterialsScreen(
screenState: ChatMaterialsScreenState, screenState: ChatMaterialsScreenState = ChatMaterialsScreenState.EMPTY,
enablePullToRefresh: Boolean = false,
onBack: () -> Unit = {}, onBack: () -> Unit = {},
onTypeChanged: (String) -> Unit = {}, onTypeChanged: (String) -> Unit = {},
onRefreshDropdownItemClicked: () -> 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() val pullToRefreshState = rememberPullToRefreshState()
Scaffold( Scaffold(
@@ -286,7 +287,11 @@ fun ChatMaterialsScreen(
.fillMaxSize() .fillMaxSize()
.padding(start = padding.calculateStartPadding(LayoutDirection.Ltr)) .padding(start = padding.calculateStartPadding(LayoutDirection.Ltr))
.padding(end = padding.calculateEndPadding(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)) { if (checkedTypeIndex in listOf(0, 1)) {
LazyVerticalGrid( LazyVerticalGrid(
@@ -350,26 +355,27 @@ fun ChatMaterialsScreen(
} }
} }
if (pullToRefreshState.isRefreshing) { if (enablePullToRefresh) {
LaunchedEffect(true) { if (pullToRefreshState.isRefreshing) {
onRefresh() LaunchedEffect(true) {
onRefresh()
}
} }
}
LaunchedEffect(screenState.isLoading) { LaunchedEffect(screenState.isLoading) {
if (!screenState.isLoading) { if (!screenState.isLoading) {
pullToRefreshState.endRefresh() pullToRefreshState.endRefresh()
}
} }
}
PullToRefreshContainer( PullToRefreshContainer(
state = pullToRefreshState, state = pullToRefreshState,
modifier = Modifier modifier = Modifier
.alpha(pullToRefreshAlpha) .align(Alignment.TopCenter)
.align(Alignment.TopCenter) .padding(top = padding.calculateTopPadding()),
.padding(top = padding.calculateTopPadding()), contentColor = MaterialTheme.colorScheme.primary
contentColor = MaterialTheme.colorScheme.primary )
) }
} }
} }
} }
@@ -51,7 +51,6 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.rotate import androidx.compose.ui.draw.rotate
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext 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.ConversationOption
import com.meloda.app.fast.conversations.model.ConversationsScreenState import com.meloda.app.fast.conversations.model.ConversationsScreenState
import com.meloda.app.fast.conversations.model.UiConversation 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.model.BaseError
import com.meloda.app.fast.ui.components.ErrorView import com.meloda.app.fast.ui.components.ErrorView
import com.meloda.app.fast.ui.components.FullScreenLoader 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 dev.chrisbanes.haze.materials.HazeMaterials
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.koin.androidx.compose.koinViewModel import org.koin.androidx.compose.koinViewModel
import org.koin.compose.koinInject
import com.meloda.app.fast.ui.R as UiR import com.meloda.app.fast.ui.R as UiR
@Composable @Composable
@@ -96,6 +97,10 @@ fun ConversationsRoute(
) { ) {
val context = LocalContext.current val context = LocalContext.current
val userSettings: UserSettings = koinInject()
val enablePullToRefresh by userSettings.enablePullToRefresh.collectAsStateWithLifecycle()
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()
@@ -115,6 +120,7 @@ fun ConversationsRoute(
screenState = screenState, screenState = screenState,
baseError = baseError, baseError = baseError,
canPaginate = canPaginate, canPaginate = canPaginate,
enablePullToRefresh = enablePullToRefresh,
onSessionExpiredLogOutButtonClicked = { onError(BaseError.SessionExpired) }, onSessionExpiredLogOutButtonClicked = { onError(BaseError.SessionExpired) },
onConversationItemClicked = { id -> onConversationItemClicked = { id ->
onConversationItemClicked(id) onConversationItemClicked(id)
@@ -143,6 +149,7 @@ fun ConversationsScreen(
screenState: ConversationsScreenState = ConversationsScreenState.EMPTY, screenState: ConversationsScreenState = ConversationsScreenState.EMPTY,
baseError: BaseError? = null, baseError: BaseError? = null,
canPaginate: Boolean = false, canPaginate: Boolean = false,
enablePullToRefresh: Boolean = false,
onSessionExpiredLogOutButtonClicked: () -> Unit, onSessionExpiredLogOutButtonClicked: () -> Unit,
onConversationItemClicked: (conversationId: Int) -> Unit = {}, onConversationItemClicked: (conversationId: Int) -> Unit = {},
onConversationItemLongClicked: (conversation: UiConversation) -> Unit = {}, onConversationItemLongClicked: (conversation: UiConversation) -> Unit = {},
@@ -155,7 +162,7 @@ fun ConversationsScreen(
val currentTheme = LocalTheme.current val currentTheme = LocalTheme.current
val maxLines by remember(currentTheme) { val maxLines by remember(currentTheme) {
mutableIntStateOf(if (currentTheme.multiline) 2 else 1) mutableIntStateOf(if (currentTheme.isMultiline) 2 else 1)
} }
val listState = rememberLazyListState() val listState = rememberLazyListState()
@@ -196,12 +203,6 @@ fun ConversationsScreen(
animationSpec = tween(durationMillis = 50) animationSpec = tween(durationMillis = 50)
) )
val pullToRefreshAlpha by animateFloatAsState(
targetValue = if (!listState.canScrollBackward) 1f else 0f,
label = "pullToRefreshAlpha",
animationSpec = tween(durationMillis = 50)
)
Scaffold( Scaffold(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
contentWindowInsets = WindowInsets.statusBars, contentWindowInsets = WindowInsets.statusBars,
@@ -342,7 +343,11 @@ fun ConversationsScreen(
.padding(start = padding.calculateStartPadding(LayoutDirection.Ltr)) .padding(start = padding.calculateStartPadding(LayoutDirection.Ltr))
.padding(end = padding.calculateEndPadding(LayoutDirection.Ltr)) .padding(end = padding.calculateEndPadding(LayoutDirection.Ltr))
.padding(bottom = padding.calculateBottomPadding()) .padding(bottom = padding.calculateBottomPadding())
.nestedScroll(pullToRefreshState.nestedScrollConnection) .then(
if (enablePullToRefresh) {
Modifier.nestedScroll(pullToRefreshState.nestedScrollConnection)
} else Modifier
)
) { ) {
ConversationsListComposable( ConversationsListComposable(
onConversationsClick = onConversationItemClicked, onConversationsClick = onConversationItemClicked,
@@ -362,26 +367,27 @@ fun ConversationsScreen(
padding = padding padding = padding
) )
if (pullToRefreshState.isRefreshing) { if (enablePullToRefresh) {
LaunchedEffect(true) { if (pullToRefreshState.isRefreshing) {
onRefresh() LaunchedEffect(true) {
onRefresh()
}
} }
}
LaunchedEffect(screenState.isLoading) { LaunchedEffect(screenState.isLoading) {
if (!screenState.isLoading) { if (!screenState.isLoading) {
pullToRefreshState.endRefresh() pullToRefreshState.endRefresh()
}
} }
}
PullToRefreshContainer( PullToRefreshContainer(
state = pullToRefreshState, state = pullToRefreshState,
modifier = Modifier modifier = Modifier
.alpha(pullToRefreshAlpha) .align(Alignment.TopCenter)
.align(Alignment.TopCenter) .padding(top = padding.calculateTopPadding()),
.padding(top = padding.calculateTopPadding()), contentColor = MaterialTheme.colorScheme.primary
contentColor = MaterialTheme.colorScheme.primary )
) }
} }
} }
} }
@@ -39,7 +39,6 @@ import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
@@ -50,6 +49,7 @@ 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 com.meloda.app.fast.datastore.UserSettings
import com.meloda.app.fast.friends.FriendsViewModel import com.meloda.app.fast.friends.FriendsViewModel
import com.meloda.app.fast.friends.FriendsViewModelImpl import com.meloda.app.fast.friends.FriendsViewModelImpl
import com.meloda.app.fast.friends.model.FriendsScreenState 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.ExperimentalHazeMaterialsApi
import dev.chrisbanes.haze.materials.HazeMaterials import dev.chrisbanes.haze.materials.HazeMaterials
import org.koin.androidx.compose.koinViewModel import org.koin.androidx.compose.koinViewModel
import org.koin.compose.koinInject
import com.meloda.app.fast.ui.R as UiR import com.meloda.app.fast.ui.R as UiR
@Composable @Composable
@@ -75,8 +76,12 @@ fun FriendsRoute(
) { ) {
val context = LocalContext.current 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 screenState by viewModel.screenState.collectAsStateWithLifecycle()
val baseError by viewModel.baseError.collectAsStateWithLifecycle()
val canPaginate by viewModel.canPaginate.collectAsStateWithLifecycle() val canPaginate by viewModel.canPaginate.collectAsStateWithLifecycle()
val imagesToPreload by viewModel.imagesToPreload.collectAsStateWithLifecycle() val imagesToPreload by viewModel.imagesToPreload.collectAsStateWithLifecycle()
@@ -93,6 +98,7 @@ fun FriendsRoute(
FriendsScreen( FriendsScreen(
screenState = screenState, screenState = screenState,
baseError = baseError, baseError = baseError,
enablePullToRefresh = enablePullToRefresh,
canPaginate = canPaginate, canPaginate = canPaginate,
onSessionExpiredLogOutButtonClicked = { onError(BaseError.SessionExpired) }, onSessionExpiredLogOutButtonClicked = { onError(BaseError.SessionExpired) },
onPaginationConditionsMet = viewModel::onPaginationConditionsMet, onPaginationConditionsMet = viewModel::onPaginationConditionsMet,
@@ -110,6 +116,7 @@ fun FriendsRoute(
fun FriendsScreen( fun FriendsScreen(
screenState: FriendsScreenState = FriendsScreenState.EMPTY, screenState: FriendsScreenState = FriendsScreenState.EMPTY,
baseError: BaseError? = null, baseError: BaseError? = null,
enablePullToRefresh: Boolean = false,
canPaginate: Boolean = false, canPaginate: Boolean = false,
onSessionExpiredLogOutButtonClicked: () -> Unit = {}, onSessionExpiredLogOutButtonClicked: () -> Unit = {},
onPaginationConditionsMet: () -> Unit = {}, onPaginationConditionsMet: () -> Unit = {},
@@ -119,7 +126,7 @@ fun FriendsScreen(
val maxLines by remember { val maxLines by remember {
derivedStateOf { derivedStateOf {
if (currentTheme.multiline) 2 else 1 if (currentTheme.isMultiline) 2 else 1
} }
} }
@@ -141,12 +148,6 @@ fun FriendsScreen(
val hazeState = LocalHazeState.current 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( val topBarContainerColorAlpha by animateFloatAsState(
targetValue = if (!currentTheme.usingBlur || !listState.canScrollBackward) 1f else 0f, targetValue = if (!currentTheme.usingBlur || !listState.canScrollBackward) 1f else 0f,
label = "toolbarColorAlpha", label = "toolbarColorAlpha",
@@ -173,18 +174,20 @@ fun FriendsScreen(
mutableIntStateOf(0) mutableIntStateOf(0)
} }
val tabItems = listOf( val tabItems = remember {
TabItem( listOf(
titleResId = UiR.string.title_friends_all, TabItem(
unselectedIconResId = null, titleResId = UiR.string.title_friends_all,
selectedIconResId = null unselectedIconResId = null,
), selectedIconResId = null
TabItem( ),
titleResId = UiR.string.title_friends_online, TabItem(
unselectedIconResId = null, titleResId = UiR.string.title_friends_online,
selectedIconResId = null unselectedIconResId = null,
selectedIconResId = null
)
) )
) }
Scaffold( Scaffold(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
@@ -254,8 +257,6 @@ fun FriendsScreen(
screenState.isLoading && screenState.friends.isEmpty() -> FullScreenLoader() screenState.isLoading && screenState.friends.isEmpty() -> FullScreenLoader()
else -> { else -> {
val pullToRefreshState = rememberPullToRefreshState()
val pagerState = rememberPagerState { tabItems.size } val pagerState = rememberPagerState { tabItems.size }
LaunchedEffect(selectedTabIndex) { LaunchedEffect(selectedTabIndex) {
@@ -270,6 +271,8 @@ fun FriendsScreen(
} }
} }
val pullToRefreshState = rememberPullToRefreshState()
Box(modifier = Modifier.fillMaxSize()) { Box(modifier = Modifier.fillMaxSize()) {
HorizontalPager( HorizontalPager(
state = pagerState, state = pagerState,
@@ -281,7 +284,13 @@ fun FriendsScreen(
.padding(start = padding.calculateStartPadding(LayoutDirection.Ltr)) .padding(start = padding.calculateStartPadding(LayoutDirection.Ltr))
.padding(end = padding.calculateEndPadding(LayoutDirection.Ltr)) .padding(end = padding.calculateEndPadding(LayoutDirection.Ltr))
.padding(bottom = padding.calculateBottomPadding()) .padding(bottom = padding.calculateBottomPadding())
.nestedScroll(pullToRefreshState.nestedScrollConnection) .then(
if (enablePullToRefresh) {
Modifier.nestedScroll(
pullToRefreshState.nestedScrollConnection
)
} else Modifier
)
) { ) {
val friendsToDisplay = screenState.friends val friendsToDisplay = screenState.friends
@@ -310,26 +319,27 @@ fun FriendsScreen(
) )
} }
if (pullToRefreshState.isRefreshing) { if (enablePullToRefresh) {
LaunchedEffect(true) { if (pullToRefreshState.isRefreshing) {
onRefresh() LaunchedEffect(true) {
onRefresh()
}
} }
}
LaunchedEffect(screenState.isLoading) { LaunchedEffect(screenState.isLoading) {
if (!screenState.isLoading) { if (!screenState.isLoading) {
pullToRefreshState.endRefresh() pullToRefreshState.endRefresh()
}
} }
}
PullToRefreshContainer( PullToRefreshContainer(
state = pullToRefreshState, state = pullToRefreshState,
modifier = Modifier modifier = Modifier
.padding(top = padding.calculateTopPadding()) .padding(top = padding.calculateTopPadding())
.alpha(pullToRefreshAlpha) .align(Alignment.TopCenter),
.align(Alignment.TopCenter), contentColor = MaterialTheme.colorScheme.primary
contentColor = MaterialTheme.colorScheme.primary )
) }
} }
} }
} }
@@ -210,6 +210,11 @@ class SettingsViewModelImpl(
val isUsing = newValue as? Boolean ?: false val isUsing = newValue as? Boolean ?: false
userSettings.onUseContactNamesChanged(isUsing) 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), text = UiText.Resource(UiR.string.settings_general_contact_names_summary),
defaultValue = SettingsKeys.DEFAULT_VALUE_USE_CONTACT_NAMES 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( val appearanceTitle = SettingsItem.Title(
key = SettingsKeys.KEY_APPEARANCE, key = SettingsKeys.KEY_APPEARANCE,
@@ -381,7 +391,8 @@ class SettingsViewModelImpl(
) )
val generalList = listOf( val generalList = listOf(
generalTitle, generalTitle,
generalUseContactNames generalUseContactNames,
generalEnablePullToRefresh
) )
val appearanceList = listOf( val appearanceList = listOf(
appearanceTitle, appearanceTitle,
@@ -81,7 +81,7 @@ fun ListItem(
Text( Text(
text = title, text = title,
style = MaterialTheme.typography.headlineSmall, 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, overflow = TextOverflow.Ellipsis,
) )
} }
@@ -94,7 +94,7 @@ fun ListItem(
Text( Text(
text = text, text = text,
style = MaterialTheme.typography.bodyMedium, 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, overflow = TextOverflow.Ellipsis,
) )
} }
@@ -65,7 +65,7 @@ fun SwitchItem(
Text( Text(
text = title, text = title,
style = MaterialTheme.typography.headlineSmall, 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, overflow = TextOverflow.Ellipsis,
) )
} }
@@ -78,7 +78,7 @@ fun SwitchItem(
Text( Text(
text = text, text = text,
style = MaterialTheme.typography.bodyMedium, 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, overflow = TextOverflow.Ellipsis,
) )
} }
@@ -93,7 +93,7 @@ fun TextFieldItem(
Text( Text(
text = title, text = title,
style = MaterialTheme.typography.headlineSmall, 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, overflow = TextOverflow.Ellipsis,
) )
} }
@@ -106,7 +106,7 @@ fun TextFieldItem(
Text( Text(
text = text, text = text,
style = MaterialTheme.typography.bodyMedium, 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, overflow = TextOverflow.Ellipsis,
) )
} }
@@ -32,7 +32,7 @@ fun TitleItem(
bottom = 4.dp bottom = 4.dp
) )
.animateContentSize(), .animateContentSize(),
maxLines = if (currentTheme.multiline) Int.MAX_VALUE else 1, maxLines = if (currentTheme.isMultiline) Int.MAX_VALUE else 1,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
) )
} }
@@ -62,7 +62,7 @@ fun TitleTextItem(
Text( Text(
text = title, text = title,
style = MaterialTheme.typography.headlineSmall, 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, overflow = TextOverflow.Ellipsis,
) )
} }
@@ -75,7 +75,7 @@ fun TitleTextItem(
Text( Text(
text = text, text = text,
style = MaterialTheme.typography.bodyMedium, 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, overflow = TextOverflow.Ellipsis,
) )
} }