A cheap attempt at a native Bluesky client for Android

Timeline: Use ViewModel state for refresh status

The `isRefreshing` state in `MainView` is now derived directly from the `TimelineViewModel`'s `isFetchingMoreTimeline` and `isFetchingMoreNotifications` properties. This removes the local `mutableStateOf` and simplifies state management.

Additionally, timeline and notification data is now fetched on ViewModel initialization instead of via a `LaunchedEffect` in the view.

+10 -25
+4 -14
app/src/main/java/industries/geesawra/monarch/MainView.kt
··· 204 204 val drawerState = rememberDrawerState( 205 205 initialValue = DrawerValue.Closed 206 206 ) 207 - val isRefreshing = remember { mutableStateOf(true) } 208 - val isScrollEnabled = !isRefreshing.value 207 + val isRefreshing = 208 + timelineViewModel.uiState.isFetchingMoreTimeline || timelineViewModel.uiState.isFetchingMoreNotifications 209 + val isScrollEnabled = !isRefreshing 209 210 val ctx = LocalContext.current 210 211 211 212 LaunchedEffect(Unit) { ··· 228 229 } 229 230 230 231 PullToRefreshBox( 231 - isRefreshing = isRefreshing.value, 232 + isRefreshing = isRefreshing, 232 233 onRefresh = { 233 - isRefreshing.value = true 234 234 when (currentDestination) { 235 235 TabBarDestinations.TIMELINE -> { 236 236 timelineViewModel.fetchTimeline(fresh = true) { 237 237 coroutineScope.launch { 238 - isRefreshing.value = false 239 238 timelineState.scrollToItem(0) 240 239 } 241 240 } ··· 244 243 TabBarDestinations.NOTIFICATIONS -> { 245 244 timelineViewModel.fetchNotifications(fresh = true) { 246 245 coroutineScope.launch { 247 - isRefreshing.value = false 248 246 notificationsState.scrollToItem(0) 249 247 } 250 248 } ··· 258 256 drawerContent = { 259 257 FeedsDrawer( 260 258 { uri: String, displayName: String, avatar: String? -> 261 - isRefreshing.value = true 262 259 timelineViewModel.selectFeed( 263 260 uri, 264 261 displayName, 265 262 avatar 266 263 ) { 267 264 coroutineScope.launch { 268 - isRefreshing.value = false 269 265 timelineState.scrollToItem(0) 270 266 } 271 267 } ··· 474 470 } 475 471 } 476 472 ) { values -> 477 - LaunchedEffect(Unit) { 478 - timelineViewModel.fetchNewData { 479 - isRefreshing.value = false 480 - } 481 - } 482 - 483 473 LaunchedEffect(notificationsState.canScrollBackward) { 484 474 TabBarDestinations.NOTIFICATIONS.badgeValue?.intValue = 0 485 475 }
+6 -11
app/src/main/java/industries/geesawra/monarch/datalayer/TimelineViewModel.kt
··· 79 79 private var timelineFetchJob: Job? = null 80 80 private var notificationsFetchJob: Job? = null 81 81 82 + init { 83 + fetchTimeline(fresh = true) 84 + fetchNotifications(fresh = true) 85 + } 86 + 82 87 fun loadSession() { 83 88 viewModelScope.launch { 84 89 if (!bskyConn.hasSession()) { ··· 110 115 }.getOrThrow() 111 116 112 117 return Result.success(ret) 113 - } 114 - 115 - fun fetchNewData(then: () -> Unit = {}) { 116 - fetchTimeline(fresh = true) 117 - fetchNotifications(fresh = true) 118 - viewModelScope.launch { 119 - timelineFetchJob?.join() 120 - notificationsFetchJob?.join() 121 - then() 122 - } 123 118 } 124 119 125 120 fun fetchTimeline(fresh: Boolean = false, then: () -> Unit = {}) { ··· 595 590 } 596 591 } 597 592 } 598 - } 593 + }