From c0f0dd85ffef047d1bda661d738d8295cb87e28a Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Wed, 26 Mar 2025 19:15:09 +0100 Subject: [PATCH 1/2] wip: Pull to refresh on home screen --- .../bitkit/ui/screens/wallets/HomeScreen.kt | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/to/bitkit/ui/screens/wallets/HomeScreen.kt b/app/src/main/java/to/bitkit/ui/screens/wallets/HomeScreen.kt index f1f396433..c8ab6fe37 100644 --- a/app/src/main/java/to/bitkit/ui/screens/wallets/HomeScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/wallets/HomeScreen.kt @@ -15,6 +15,10 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.systemBarsPadding +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.pullrefresh.PullRefreshIndicator +import androidx.compose.material.pullrefresh.pullRefresh +import androidx.compose.material.pullrefresh.rememberPullRefreshState import androidx.compose.material3.VerticalDivider import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -23,6 +27,7 @@ import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -35,6 +40,8 @@ import androidx.navigation.NavController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch import kotlinx.serialization.Serializable import to.bitkit.R import to.bitkit.ext.requiresPermission @@ -162,6 +169,7 @@ fun HomeScreen( } } +@OptIn(ExperimentalMaterialApi::class) @Composable private fun HomeContentView( rootNavController: NavController, @@ -182,7 +190,28 @@ private fun HomeContentView( showEmptyStateSetting && balances.totalSats == 0uL } } - Box(modifier = Modifier.fillMaxSize()) { + + val scope = rememberCoroutineScope() + var isRefreshing by remember { mutableStateOf(false) } + + fun refresh() = scope.launch { + // TODO use viewmodel state?! + isRefreshing = true + onRefresh() + delay(1500) + isRefreshing = false + } + + val pullRefreshState = rememberPullRefreshState( + refreshing = isRefreshing, + onRefresh = ::refresh + ) + + Box( + modifier = Modifier + .fillMaxSize() + .pullRefresh(pullRefreshState) + ) { Column( modifier = Modifier .padding(horizontal = 16.dp) @@ -235,6 +264,12 @@ private fun HomeContentView( .align(Alignment.BottomCenter) ) } + + PullRefreshIndicator( + refreshing = isRefreshing, + state = pullRefreshState, + modifier = Modifier.align(Alignment.TopCenter) + ) } } } From 73da7e6ebcdc7600d0349479db2233285d65a091 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Thu, 27 Mar 2025 11:42:56 +0100 Subject: [PATCH 2/2] feat: Add pull to refresh on home screen --- .../java/to/bitkit/ui/scaffold/AppScaffold.kt | 8 ----- .../bitkit/ui/screens/wallets/HomeScreen.kt | 36 +++++++------------ .../wallets/activity/AllActivityScreen.kt | 31 ++++++---------- .../to/bitkit/viewmodels/WalletViewModel.kt | 14 ++++++++ 4 files changed, 36 insertions(+), 53 deletions(-) diff --git a/app/src/main/java/to/bitkit/ui/scaffold/AppScaffold.kt b/app/src/main/java/to/bitkit/ui/scaffold/AppScaffold.kt index bf7342c76..ad2b7c6d4 100644 --- a/app/src/main/java/to/bitkit/ui/scaffold/AppScaffold.kt +++ b/app/src/main/java/to/bitkit/ui/scaffold/AppScaffold.kt @@ -6,7 +6,6 @@ import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Bolt -import androidx.compose.material.icons.filled.Refresh import androidx.compose.material.icons.outlined.Settings import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon @@ -26,7 +25,6 @@ import to.bitkit.ui.navigateToSettings fun AppScaffold( navController: NavController, titleText: String, - onRefresh: () -> Unit, content: @Composable () -> Unit, ) { Scaffold( @@ -37,12 +35,6 @@ fun AppScaffold( Title(text = titleText) }, actions = { - IconButton(onRefresh) { - Icon( - imageVector = Icons.Default.Refresh, - contentDescription = stringResource(R.string.sync), - ) - } IconButton(onClick = navController::navigateToNodeState) { Icon( imageVector = Icons.Default.Bolt, diff --git a/app/src/main/java/to/bitkit/ui/screens/wallets/HomeScreen.kt b/app/src/main/java/to/bitkit/ui/screens/wallets/HomeScreen.kt index c8ab6fe37..5f13cbdec 100644 --- a/app/src/main/java/to/bitkit/ui/screens/wallets/HomeScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/wallets/HomeScreen.kt @@ -15,6 +15,8 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.systemBarsPadding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.pullrefresh.PullRefreshIndicator import androidx.compose.material.pullrefresh.pullRefresh @@ -27,7 +29,6 @@ import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -40,8 +41,6 @@ import androidx.navigation.NavController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch import kotlinx.serialization.Serializable import to.bitkit.R import to.bitkit.ext.requiresPermission @@ -72,6 +71,7 @@ import to.bitkit.ui.utils.screenSlideIn import to.bitkit.ui.utils.screenSlideOut import to.bitkit.ui.utils.withAccent import to.bitkit.viewmodels.AppViewModel +import to.bitkit.viewmodels.MainUiState import to.bitkit.viewmodels.WalletViewModel @Composable @@ -80,7 +80,7 @@ fun HomeScreen( appViewModel: AppViewModel, rootNavController: NavController, ) { - val uiState by walletViewModel.uiState.collectAsState() + val uiState: MainUiState by walletViewModel.uiState.collectAsState() val currentSheet by appViewModel.currentSheet SheetHost( shouldExpand = currentSheet != null, @@ -110,13 +110,14 @@ fun HomeScreen( val walletNavController = rememberNavController() NavHost( navController = walletNavController, - startDestination = HomeRoutes.Home + startDestination = HomeRoutes.Home, ) { composable { HomeContentView( + uiState = uiState, rootNavController = rootNavController, walletNavController = walletNavController, - onRefresh = walletViewModel::refreshState, + onRefresh = walletViewModel::onPullToRefresh, ) } composable( @@ -172,6 +173,7 @@ fun HomeScreen( @OptIn(ExperimentalMaterialApi::class) @Composable private fun HomeContentView( + uiState: MainUiState, rootNavController: NavController, walletNavController: NavController, onRefresh: () -> Unit, @@ -179,7 +181,6 @@ private fun HomeContentView( AppScaffold( navController = rootNavController, titleText = "Your Name", - onRefresh = onRefresh, ) { RequestNotificationPermissions() val balances = LocalBalances.current @@ -190,22 +191,7 @@ private fun HomeContentView( showEmptyStateSetting && balances.totalSats == 0uL } } - - val scope = rememberCoroutineScope() - var isRefreshing by remember { mutableStateOf(false) } - - fun refresh() = scope.launch { - // TODO use viewmodel state?! - isRefreshing = true - onRefresh() - delay(1500) - isRefreshing = false - } - - val pullRefreshState = rememberPullRefreshState( - refreshing = isRefreshing, - onRefresh = ::refresh - ) + val pullRefreshState = rememberPullRefreshState(refreshing = uiState.isRefreshing, onRefresh = onRefresh) Box( modifier = Modifier @@ -216,6 +202,7 @@ private fun HomeContentView( modifier = Modifier .padding(horizontal = 16.dp) .fillMaxSize() + .verticalScroll(rememberScrollState()) ) { BalanceHeaderView(sats = balances.totalSats.toLong(), modifier = Modifier.fillMaxWidth()) if (!showEmptyState) { @@ -266,7 +253,7 @@ private fun HomeContentView( } PullRefreshIndicator( - refreshing = isRefreshing, + refreshing = uiState.isRefreshing, state = pullRefreshState, modifier = Modifier.align(Alignment.TopCenter) ) @@ -310,6 +297,7 @@ object HomeRoutes { private fun HomeContentViewPreview() { AppThemeSurface { HomeContentView( + uiState = MainUiState(), rootNavController = rememberNavController(), walletNavController = rememberNavController(), onRefresh = {}, diff --git a/app/src/main/java/to/bitkit/ui/screens/wallets/activity/AllActivityScreen.kt b/app/src/main/java/to/bitkit/ui/screens/wallets/activity/AllActivityScreen.kt index e19332031..0833a943e 100644 --- a/app/src/main/java/to/bitkit/ui/screens/wallets/activity/AllActivityScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/wallets/activity/AllActivityScreen.kt @@ -8,7 +8,6 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.HorizontalDivider @@ -177,32 +176,22 @@ fun ActivityList( onActivityItemClick: (String) -> Unit, ) { if (items != null) { - LazyColumn( + Column( horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.fillMaxWidth() ) { - items( - items = items, - key = { - when (it) { - is Activity.Onchain -> it.v1.id - is Activity.Lightning -> it.v1.id - } - } - ) { item -> + items.forEach { item -> ActivityRow(item, onActivityItemClick) HorizontalDivider(color = Colors.White10) } - item { - if (items.isEmpty()) { - BodyMSB(stringResource(R.string.wallet__activity_no), Modifier.padding(16.dp)) - } else { - TertiaryButton( - text = stringResource(R.string.wallet__activity_show_all), - onClick = onAllActivityClick, - modifier = Modifier.wrapContentWidth().padding(top = 8.dp) - ) - } + if (items.isEmpty()) { + BodyMSB(stringResource(R.string.wallet__activity_no), Modifier.padding(16.dp)) + } else { + TertiaryButton( + text = stringResource(R.string.wallet__activity_show_all), + onClick = onAllActivityClick, + modifier = Modifier.wrapContentWidth().padding(top = 8.dp) + ) } } } else { diff --git a/app/src/main/java/to/bitkit/viewmodels/WalletViewModel.kt b/app/src/main/java/to/bitkit/viewmodels/WalletViewModel.kt index 576698a7d..6f73997db 100644 --- a/app/src/main/java/to/bitkit/viewmodels/WalletViewModel.kt +++ b/app/src/main/java/to/bitkit/viewmodels/WalletViewModel.kt @@ -218,6 +218,19 @@ class WalletViewModel @Inject constructor( } } + fun onPullToRefresh() { + viewModelScope.launch { + _uiState.update { it.copy(isRefreshing = true) } + try { + sync() + } catch (e: Throwable) { + ToastEventBus.send(e) + } finally { + _uiState.update { it.copy(isRefreshing = false) } + } + } + } + private suspend fun registerForNotificationsIfNeeded() { val token = firebaseMessaging.token.await() val cachedToken = keychain.loadString(Keychain.Key.PUSH_NOTIFICATION_TOKEN.name) @@ -483,6 +496,7 @@ data class MainUiState( val nodeLifecycleState: NodeLifecycleState = NodeLifecycleState.Stopped, val peers: List = emptyList(), val channels: List = emptyList(), + val isRefreshing: Boolean = false, ) // endregion