diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 143c4f79..5a0aa43e 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -55,6 +55,7 @@ dependencies { implementation(Dependencies.ThirdParty.accompanist) implementation(Dependencies.ThirdParty.composeFlowLayout) implementation(Dependencies.ThirdParty.Moshi.lib) + implementation(Dependencies.ThirdParty.pullToRefresh) implementation(Dependencies.ThirdParty.Retrofit.moshi) implementation(Dependencies.ThirdParty.SQLDelight.androidDriver) testImplementation(Dependencies.Testing.junit) diff --git a/app/screenshots/debug/dev.msfjarvis.lobsters.ui.main.LobstersTopBarTest_doesNotShowRefreshIconWhenOnSavedPostsScreen_DarkTheme.png b/app/screenshots/debug/dev.msfjarvis.lobsters.ui.main.LobstersTopBarTest_doesNotShowRefreshIconWhenOnSavedPostsScreen_DarkTheme.png deleted file mode 100644 index ad4d0c8f..00000000 Binary files a/app/screenshots/debug/dev.msfjarvis.lobsters.ui.main.LobstersTopBarTest_doesNotShowRefreshIconWhenOnSavedPostsScreen_DarkTheme.png and /dev/null differ diff --git a/app/screenshots/debug/dev.msfjarvis.lobsters.ui.main.LobstersTopBarTest_doesNotShowRefreshIconWhenOnSavedPostsScreen_LightTheme.png b/app/screenshots/debug/dev.msfjarvis.lobsters.ui.main.LobstersTopBarTest_doesNotShowRefreshIconWhenOnSavedPostsScreen_LightTheme.png deleted file mode 100644 index 19f85e24..00000000 Binary files a/app/screenshots/debug/dev.msfjarvis.lobsters.ui.main.LobstersTopBarTest_doesNotShowRefreshIconWhenOnSavedPostsScreen_LightTheme.png and /dev/null differ diff --git a/app/screenshots/debug/dev.msfjarvis.lobsters.ui.main.LobstersTopBarTest_showsRefreshIconWhenOnHottestPostsScreen_DarkTheme.png b/app/screenshots/debug/dev.msfjarvis.lobsters.ui.main.LobstersTopBarTest_showsRefreshIconWhenOnHottestPostsScreen_DarkTheme.png deleted file mode 100644 index 8610f311..00000000 Binary files a/app/screenshots/debug/dev.msfjarvis.lobsters.ui.main.LobstersTopBarTest_showsRefreshIconWhenOnHottestPostsScreen_DarkTheme.png and /dev/null differ diff --git a/app/screenshots/debug/dev.msfjarvis.lobsters.ui.main.LobstersTopBarTest_showsRefreshIconWhenOnHottestPostsScreen_LightTheme.png b/app/screenshots/debug/dev.msfjarvis.lobsters.ui.main.LobstersTopBarTest_showsRefreshIconWhenOnHottestPostsScreen_LightTheme.png deleted file mode 100644 index 6957a491..00000000 Binary files a/app/screenshots/debug/dev.msfjarvis.lobsters.ui.main.LobstersTopBarTest_showsRefreshIconWhenOnHottestPostsScreen_LightTheme.png and /dev/null differ diff --git a/app/src/androidTest/java/dev/msfjarvis/lobsters/ui/main/LobstersTopBarTest.kt b/app/src/androidTest/java/dev/msfjarvis/lobsters/ui/main/LobstersTopBarTest.kt deleted file mode 100644 index 2923418d..00000000 --- a/app/src/androidTest/java/dev/msfjarvis/lobsters/ui/main/LobstersTopBarTest.kt +++ /dev/null @@ -1,74 +0,0 @@ -package dev.msfjarvis.lobsters.ui.main - -import androidx.compose.ui.graphics.asAndroidBitmap -import androidx.compose.ui.test.captureToImage -import androidx.compose.ui.test.junit4.createComposeRule -import androidx.compose.ui.test.onRoot -import com.karumi.shot.ScreenshotTest -import dev.msfjarvis.lobsters.ui.DarkTestTheme -import dev.msfjarvis.lobsters.ui.LightTestTheme -import dev.msfjarvis.lobsters.ui.navigation.Destination -import org.junit.Rule -import org.junit.Test - -class LobstersTopBarTest : ScreenshotTest { - - @get:Rule - val composeTestRule = createComposeRule() - - @Test - fun showsRefreshIconWhenOnHottestPostsScreen_DarkTheme() { - composeTestRule.setContent { - DarkTestTheme { - LobstersTopBar( - currentDestination = Destination.Hottest, - reloadPosts = { /*TODO*/ } - ) - } - } - - compareScreenshot(composeTestRule.onRoot().captureToImage().asAndroidBitmap()) - } - - @Test - fun showsRefreshIconWhenOnHottestPostsScreen_LightTheme() { - composeTestRule.setContent { - LightTestTheme { - LobstersTopBar( - currentDestination = Destination.Hottest, - reloadPosts = { /*TODO*/ } - ) - } - } - - compareScreenshot(composeTestRule.onRoot().captureToImage().asAndroidBitmap()) - } - - @Test - fun doesNotShowRefreshIconWhenOnSavedPostsScreen_DarkTheme() { - composeTestRule.setContent { - DarkTestTheme { - LobstersTopBar( - currentDestination = Destination.Saved, - reloadPosts = { /*TODO*/ } - ) - } - } - - compareScreenshot(composeTestRule.onRoot().captureToImage().asAndroidBitmap()) - } - - @Test - fun doesNotShowRefreshIconWhenOnSavedPostsScreen_LightTheme() { - composeTestRule.setContent { - LightTestTheme { - LobstersTopBar( - currentDestination = Destination.Saved, - reloadPosts = { /*TODO*/ } - ) - } - } - - compareScreenshot(composeTestRule.onRoot().captureToImage().asAndroidBitmap()) - } -} diff --git a/app/src/main/java/dev/msfjarvis/lobsters/ui/main/LobstersApp.kt b/app/src/main/java/dev/msfjarvis/lobsters/ui/main/LobstersApp.kt index a64680d5..bbcea9f7 100644 --- a/app/src/main/java/dev/msfjarvis/lobsters/ui/main/LobstersApp.kt +++ b/app/src/main/java/dev/msfjarvis/lobsters/ui/main/LobstersApp.kt @@ -1,14 +1,11 @@ package dev.msfjarvis.lobsters.ui.main -import androidx.compose.animation.ExperimentalAnimationApi -import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material.BottomNavigation import androidx.compose.material.BottomNavigationItem import androidx.compose.material.Scaffold import androidx.compose.material.Text -import androidx.compose.material.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue @@ -16,7 +13,6 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.compose.KEY_ROUTE import androidx.navigation.compose.NavHost @@ -25,7 +21,6 @@ import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.navigate import androidx.navigation.compose.rememberNavController import androidx.paging.compose.collectAsLazyPagingItems -import dev.msfjarvis.lobsters.R import dev.msfjarvis.lobsters.ui.navigation.Destination import dev.msfjarvis.lobsters.ui.posts.HottestPosts import dev.msfjarvis.lobsters.ui.posts.SavedPosts @@ -56,12 +51,6 @@ fun LobstersApp() { } Scaffold( - topBar = { - LobstersTopBar( - currentDestination = currentDestination, - reloadPosts = { viewModel.reloadPosts() }, - ) - }, bottomBar = { LobstersBottomNav( currentDestination, @@ -75,9 +64,10 @@ fun LobstersApp() { HottestPosts( posts = hottestPosts, listState = hottestPostsListState, + modifier = Modifier.padding(bottom = innerPadding.calculateBottomPadding()), isPostSaved = viewModel::isPostSaved, saveAction = viewModel::toggleSave, - modifier = Modifier.padding(bottom = innerPadding.calculateBottomPadding()), + refreshAction = viewModel::reloadPosts, ) } composable(Destination.Saved.route) { @@ -91,33 +81,6 @@ fun LobstersApp() { } } -@OptIn(ExperimentalAnimationApi::class) -@Composable -fun LobstersTopBar( - currentDestination: Destination, - reloadPosts: () -> Unit, -) { - TopAppBar( - title = { - Text( - text = stringResource(id = R.string.app_name), - modifier = Modifier.padding(vertical = 8.dp), - ) - }, - actions = { - if (currentDestination == Destination.Hottest) { - IconResource( - resourceId = R.drawable.ic_refresh_24px, - contentDescription = stringResource(id = R.string.refresh_posts_content_description), - modifier = Modifier - .padding(horizontal = 8.dp, vertical = 8.dp) - .clickable { reloadPosts() }, - ) - } - } - ) -} - @Composable fun LobstersBottomNav( currentDestination: Destination, diff --git a/app/src/main/java/dev/msfjarvis/lobsters/ui/posts/HottestPosts.kt b/app/src/main/java/dev/msfjarvis/lobsters/ui/posts/HottestPosts.kt index 6f81db0d..bea13542 100644 --- a/app/src/main/java/dev/msfjarvis/lobsters/ui/posts/HottestPosts.kt +++ b/app/src/main/java/dev/msfjarvis/lobsters/ui/posts/HottestPosts.kt @@ -11,6 +11,7 @@ import androidx.compose.ui.Modifier import androidx.paging.LoadState import androidx.paging.compose.LazyPagingItems import androidx.paging.compose.items +import com.puculek.pulltorefresh.PullToRefresh import dev.msfjarvis.lobsters.data.local.SavedPost import dev.msfjarvis.lobsters.model.LobstersPost import dev.msfjarvis.lobsters.ui.urllauncher.LocalUrlLauncher @@ -20,39 +21,51 @@ import dev.msfjarvis.lobsters.util.toDbModel fun HottestPosts( posts: LazyPagingItems, listState: LazyListState, - isPostSaved: (String) -> Boolean, modifier: Modifier = Modifier, + isPostSaved: (String) -> Boolean, saveAction: (SavedPost) -> Unit, + refreshAction: () -> Unit, ) { val urlLauncher = LocalUrlLauncher.current + var isRefreshing by mutableStateOf(false) - if (posts.loadState.refresh == LoadState.Loading) { - LazyColumn { - items(15) { - LoadingLobstersItem() + PullToRefresh( + isRefreshing = isRefreshing, + onRefresh = { + if (posts.loadState.refresh != LoadState.Loading) { + isRefreshing = isRefreshing.not() + refreshAction() } - } - } else { - LazyColumn( - state = listState, - modifier = Modifier.then(modifier), - ) { - items(posts) { item -> - if (item != null) { - @Suppress("NAME_SHADOWING") - val item = item.toDbModel() - var isSaved by remember(item.shortId) { mutableStateOf(isPostSaved(item.shortId)) } + }, + ) { + if (posts.loadState.refresh == LoadState.Loading) { + LazyColumn { + items(15) { + LoadingLobstersItem() + } + } + } else { + LazyColumn( + state = listState, + modifier = Modifier.then(modifier), + ) { + items(posts) { item -> + if (item != null) { + @Suppress("NAME_SHADOWING") + val item = item.toDbModel() + var isSaved by remember(item.shortId) { mutableStateOf(isPostSaved(item.shortId)) } - LobstersItem( - post = item, - isSaved = isSaved, - onClick = { urlLauncher.launch(item.url.ifEmpty { item.commentsUrl }) }, - onLongClick = { urlLauncher.launch(item.commentsUrl) }, - onSaveButtonClick = { - isSaved = isSaved.not() - saveAction.invoke(item) - }, - ) + LobstersItem( + post = item, + isSaved = isSaved, + onClick = { urlLauncher.launch(item.url.ifEmpty { item.commentsUrl }) }, + onLongClick = { urlLauncher.launch(item.commentsUrl) }, + onSaveButtonClick = { + isSaved = isSaved.not() + saveAction.invoke(item) + }, + ) + } } } } diff --git a/buildSrc/src/main/java/Dependencies.kt b/buildSrc/src/main/java/Dependencies.kt index c61fd8e4..d700b6a6 100644 --- a/buildSrc/src/main/java/Dependencies.kt +++ b/buildSrc/src/main/java/Dependencies.kt @@ -67,6 +67,7 @@ object Dependencies { const val accompanist = "dev.chrisbanes.accompanist:accompanist-coil:0.6.2" const val composeFlowLayout = "com.star-zero:compose-flowlayout:0.0.1" + const val pullToRefresh = "com.puculek.pulltorefresh:pull-to-refresh-compose:1.0.0" object Moshi {