A cheap attempt at a native Bluesky client for Android

MainView: Add a "scroll to top" button

Add a floating action button that appears on the Timeline and Notifications tabs when the user has scrolled down. Tapping the button scrolls the list back to the top.

The top app bar was also changed from `enterAlwaysScrollBehavior` to `pinnedScrollBehavior` to accommodate this.

+41 -6
+41 -6
app/src/main/java/industries/geesawra/monarch/MainView.kt
··· 4 4 import androidx.annotation.StringRes 5 5 import androidx.compose.foundation.clickable 6 6 import androidx.compose.foundation.layout.Arrangement 7 + import androidx.compose.foundation.layout.Column 7 8 import androidx.compose.foundation.layout.Row 8 9 import androidx.compose.foundation.layout.Spacer 9 10 import androidx.compose.foundation.layout.WindowInsets ··· 16 17 import androidx.compose.foundation.rememberScrollState 17 18 import androidx.compose.foundation.shape.CircleShape 18 19 import androidx.compose.material.icons.Icons 20 + import androidx.compose.material.icons.filled.AirlineStops 19 21 import androidx.compose.material.icons.filled.Create 20 22 import androidx.compose.material.icons.filled.Home 21 23 import androidx.compose.material.icons.filled.Notifications ··· 24 26 import androidx.compose.material3.DrawerValue 25 27 import androidx.compose.material3.ExperimentalMaterial3Api 26 28 import androidx.compose.material3.FloatingActionButton 29 + import androidx.compose.material3.FloatingActionButtonDefaults 27 30 import androidx.compose.material3.Icon 28 31 import androidx.compose.material3.IconButton 29 32 import androidx.compose.material3.MaterialTheme ··· 145 148 loginError: () -> Unit, 146 149 ) { 147 150 var currentDestination by rememberSaveable { mutableStateOf(TabBarDestinations.TIMELINE) } 148 - val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior( 151 + val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior( 149 152 rememberTopAppBarState() 150 153 ) 151 154 val timelineState = rememberLazyListState() ··· 288 291 }, 289 292 floatingActionButton = { 290 293 when (currentDestination) { 291 - TabBarDestinations.TIMELINE -> FloatingActionButton( 292 - onClick = fobOnClick 293 - ) { 294 - Icon(Icons.Filled.Create, "Post") 294 + TabBarDestinations.TIMELINE -> { 295 + Column( 296 + verticalArrangement = Arrangement.spacedBy(8.dp), 297 + ) { 298 + if (timelineState.canScrollBackward) { 299 + FloatingActionButton( 300 + onClick = { 301 + coroutineScope.launch { 302 + timelineState.animateScrollToItem(0) 303 + } 304 + }, 305 + shape = FloatingActionButtonDefaults.smallShape, 306 + ) { 307 + Icon(Icons.Default.AirlineStops, "Scroll to top") 308 + } 309 + } 310 + 311 + FloatingActionButton( 312 + onClick = fobOnClick 313 + ) { 314 + Icon(Icons.Filled.Create, "Post") 315 + } 316 + } 295 317 } 296 318 297 - TabBarDestinations.NOTIFICATIONS -> {} 319 + TabBarDestinations.NOTIFICATIONS -> { 320 + if (notificationsState.canScrollBackward) { 321 + FloatingActionButton( 322 + onClick = { 323 + coroutineScope.launch { 324 + notificationsState.animateScrollToItem(0) 325 + } 326 + }, 327 + shape = FloatingActionButtonDefaults.smallShape, 328 + ) { 329 + Icon(Icons.Default.AirlineStops, "Scroll to top") 330 + } 331 + } 332 + } 298 333 } 299 334 }, 300 335 bottomBar = {