diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 000000000..43de65f84 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,15 @@ +{ + "permissions": { + "allow": [ + "Bash(./gradlew compileAppDebugKotlin)", + "Bash(./gradlew test *)", + "Bash(./gradlew :app:compileAppDebugUnitTestKotlin)", + "Bash(head -n 508 \"app/src/main/java/io/legado/app/model/CacheBook.kt\")", + "Bash(mv /tmp/cachebook_tmp.kt \"app/src/main/java/io/legado/app/model/CacheBook.kt\")", + "Bash(./gradlew :app:cleanCompileAppDebugKotlin)", + "Bash(./gradlew :app:compileAppDebugKotlin --rerun-tasks)", + "Bash(./gradlew :app:compileAppDebugKotlin)", + "Bash(./gradlew :app:cleanCompileAppDebugKotlin :app:compileAppDebugKotlin)" + ] + } +} diff --git a/app/src/main/java/io/legado/app/ui/main/MainScreen.kt b/app/src/main/java/io/legado/app/ui/main/MainScreen.kt index 3b8b27b00..5e81f0a8d 100644 --- a/app/src/main/java/io/legado/app/ui/main/MainScreen.kt +++ b/app/src/main/java/io/legado/app/ui/main/MainScreen.kt @@ -37,7 +37,6 @@ import androidx.compose.material3.WideNavigationRailValue import androidx.compose.material3.rememberWideNavigationRailState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -51,6 +50,7 @@ import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.kyant.backdrop.backdrops.layerBackdrop import com.kyant.backdrop.backdrops.rememberLayerBackdrop import dev.chrisbanes.haze.HazeState @@ -102,7 +102,7 @@ fun MainScreen( val coroutineScope = rememberCoroutineScope() val bookshelfViewModel: BookshelfViewModel = koinViewModel() - val bookshelfUiState by bookshelfViewModel.uiState.collectAsState() + val bookshelfUiState by bookshelfViewModel.uiState.collectAsStateWithLifecycle() val hazeState = remember { HazeState() } val floatingBarSurfaceColor = MaterialTheme.colorScheme.surface diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfScreen.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfScreen.kt index 87025ada2..3034cd298 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfScreen.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfScreen.kt @@ -55,7 +55,6 @@ import androidx.compose.material3.pulltorefresh.pullToRefresh import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -73,6 +72,7 @@ import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog +import androidx.lifecycle.compose.collectAsStateWithLifecycle import io.legado.app.R import io.legado.app.base.BaseRuleEvent import io.legado.app.data.entities.BookGroup @@ -127,7 +127,7 @@ fun BookshelfScreen( onNavigateToCache: (Long) -> Unit ) { val context = LocalContext.current - val uiState by viewModel.uiState.collectAsState() + val uiState by viewModel.uiState.collectAsStateWithLifecycle() val scope = rememberCoroutineScope() var showAddUrlDialog by remember { mutableStateOf(false) } @@ -721,7 +721,7 @@ fun BookshelfScreen( HorizontalPager( state = pagerState, modifier = Modifier.fillMaxSize(), - beyondViewportPageCount = 3, + beyondViewportPageCount = 1, key = { if (it < uiState.groups.size) uiState.groups[it].groupId else it } ) { pageIndex -> val group = uiState.groups.getOrNull(pageIndex) @@ -729,7 +729,7 @@ fun BookshelfScreen( val booksFlow = remember(group.groupId) { viewModel.getBooksFlow(group.groupId) } - val books by booksFlow.collectAsState(emptyList()) + val books by booksFlow.collectAsStateWithLifecycle(emptyList()) BookshelfPage( paddingValues = paddingValues, books = books, diff --git a/app/src/test/java/io/legado/app/domain/usecase/SearchBooksUseCaseTest.kt b/app/src/test/java/io/legado/app/domain/usecase/SearchBooksUseCaseTest.kt new file mode 100644 index 000000000..aa9373dad --- /dev/null +++ b/app/src/test/java/io/legado/app/domain/usecase/SearchBooksUseCaseTest.kt @@ -0,0 +1,54 @@ +package io.legado.app.domain.usecase + +import io.legado.app.data.entities.BookSource +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test + +class SearchBooksUseCaseTest { + + @Test + fun `search url without dynamic page does not support next page`() { + val source = BookSource( + searchUrl = "https://example.com/search?keyword={{key}}&page=1" + ) + + assertFalse(source.supportsSearchPage()) + } + + @Test + fun `search url with page group supports next page`() { + val source = BookSource( + searchUrl = "https://example.com/search/{{key}}/<1,2,3>" + ) + + assertTrue(source.supportsSearchPage()) + } + + @Test + fun `search url with page inner rule supports next page`() { + val source = BookSource( + searchUrl = "https://example.com/search?keyword={{key}}&page={{page}}" + ) + + assertTrue(source.supportsSearchPage()) + } + + @Test + fun `search url with page js supports next page`() { + val source = BookSource( + searchUrl = "baseUrl + '/search?keyword=' + key + '&page=' + page" + ) + + assertTrue(source.supportsSearchPage()) + } + + @Test + fun `search url with js but without page does not support next page`() { + val source = BookSource( + searchUrl = "baseUrl + '/search?keyword=' + key" + ) + + assertFalse(source.supportsSearchPage()) + } +}