Merge branch 'miuix-dev'

# Conflicts:
#	app/version.properties
This commit is contained in:
HapeLee committed 2026-04-03 01:10:47 +08:00
commit 2e7516245e
121 files changed
+5128 -2921

No files matched your search

+5
View File
@@ -285,4 +285,9 @@ dependencies {
implementation(libs.material.kolor) implementation(libs.material.kolor)
implementation(libs.haze.core) implementation(libs.haze.core)
implementation(libs.haze.materials) implementation(libs.haze.materials)
implementation(libs.miuix)
implementation(libs.miuix.icons)
implementation(libs.capsule)
implementation(libs.backdrop)
implementation(libs.timber)
} }
@@ -82,6 +82,7 @@ interface BookDao {
durChapterPos, durChapterPos,
latestChapterTitle, latestChapterTitle,
latestChapterTime, latestChapterTime,
lastCheckCount,
totalChapterNum, totalChapterNum,
durChapterIndex, durChapterIndex,
type, type,
@@ -113,6 +114,7 @@ interface BookDao {
durChapterPos, durChapterPos,
latestChapterTitle, latestChapterTitle,
latestChapterTime, latestChapterTime,
lastCheckCount,
totalChapterNum, totalChapterNum,
durChapterIndex, durChapterIndex,
type, type,
@@ -141,6 +143,7 @@ interface BookDao {
durChapterPos, durChapterPos,
latestChapterTitle, latestChapterTitle,
latestChapterTime, latestChapterTime,
lastCheckCount,
totalChapterNum, totalChapterNum,
durChapterIndex, durChapterIndex,
type, type,
@@ -169,6 +172,7 @@ interface BookDao {
durChapterPos, durChapterPos,
latestChapterTitle, latestChapterTitle,
latestChapterTime, latestChapterTime,
lastCheckCount,
totalChapterNum, totalChapterNum,
durChapterIndex, durChapterIndex,
type, type,
@@ -202,6 +206,7 @@ interface BookDao {
durChapterPos, durChapterPos,
latestChapterTitle, latestChapterTitle,
latestChapterTime, latestChapterTime,
lastCheckCount,
totalChapterNum, totalChapterNum,
durChapterIndex, durChapterIndex,
type, type,
@@ -236,6 +241,7 @@ interface BookDao {
durChapterPos, durChapterPos,
latestChapterTitle, latestChapterTitle,
latestChapterTime, latestChapterTime,
lastCheckCount,
totalChapterNum, totalChapterNum,
durChapterIndex, durChapterIndex,
type, type,
@@ -265,6 +271,7 @@ interface BookDao {
durChapterPos, durChapterPos,
latestChapterTitle, latestChapterTitle,
latestChapterTime, latestChapterTime,
lastCheckCount,
totalChapterNum, totalChapterNum,
durChapterIndex, durChapterIndex,
type, type,
@@ -293,6 +300,7 @@ interface BookDao {
durChapterPos, durChapterPos,
latestChapterTitle, latestChapterTitle,
latestChapterTime, latestChapterTime,
lastCheckCount,
totalChapterNum, totalChapterNum,
durChapterIndex, durChapterIndex,
type, type,
@@ -321,6 +329,7 @@ interface BookDao {
durChapterPos, durChapterPos,
latestChapterTitle, latestChapterTitle,
latestChapterTime, latestChapterTime,
lastCheckCount,
totalChapterNum, totalChapterNum,
durChapterIndex, durChapterIndex,
type, type,
@@ -350,6 +359,7 @@ interface BookDao {
durChapterPos, durChapterPos,
latestChapterTitle, latestChapterTitle,
latestChapterTime, latestChapterTime,
lastCheckCount,
totalChapterNum, totalChapterNum,
durChapterIndex, durChapterIndex,
type, type,
@@ -378,6 +388,7 @@ interface BookDao {
durChapterPos, durChapterPos,
latestChapterTitle, latestChapterTitle,
latestChapterTime, latestChapterTime,
lastCheckCount,
totalChapterNum, totalChapterNum,
durChapterIndex, durChapterIndex,
type, type,
@@ -406,6 +417,7 @@ interface BookDao {
durChapterPos, durChapterPos,
latestChapterTitle, latestChapterTitle,
latestChapterTime, latestChapterTime,
lastCheckCount,
totalChapterNum, totalChapterNum,
durChapterIndex, durChapterIndex,
type, type,
@@ -434,6 +446,7 @@ interface BookDao {
durChapterPos, durChapterPos,
latestChapterTitle, latestChapterTitle,
latestChapterTime, latestChapterTime,
lastCheckCount,
totalChapterNum, totalChapterNum,
durChapterIndex, durChapterIndex,
type, type,
@@ -462,6 +475,7 @@ interface BookDao {
durChapterPos, durChapterPos,
latestChapterTitle, latestChapterTitle,
latestChapterTime, latestChapterTime,
lastCheckCount,
totalChapterNum, totalChapterNum,
durChapterIndex, durChapterIndex,
type, type,
@@ -15,9 +15,6 @@ import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.FilledTonalIconButton import androidx.compose.material3.FilledTonalIconButton
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.MaterialTheme.colorScheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@@ -31,13 +28,15 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import io.legado.app.R import io.legado.app.R
import io.legado.app.constant.AppConst.appInfo import io.legado.app.constant.AppConst.appInfo
import io.legado.app.ui.theme.LegadoTheme
import io.legado.app.ui.widget.components.AppScaffold import io.legado.app.ui.widget.components.AppScaffold
import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
import io.legado.app.ui.widget.components.SplicedColumnGroup import io.legado.app.ui.widget.components.SplicedColumnGroup
import io.legado.app.ui.widget.components.button.TopbarNavigationButton import io.legado.app.ui.widget.components.button.TopbarNavigationButton
import io.legado.app.ui.widget.components.card.TextCard import io.legado.app.ui.widget.components.card.TextCard
import io.legado.app.ui.widget.components.settingItem.SettingItem import io.legado.app.ui.widget.components.settingItem.SettingItem
import io.legado.app.ui.widget.components.text.AppText
import io.legado.app.ui.widget.components.topbar.GlassMediumFlexibleTopAppBar
import io.legado.app.ui.widget.components.topbar.GlassTopAppBarDefaults
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class) @OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
@Composable @Composable
@@ -59,7 +58,7 @@ fun AboutScreen(
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
topBar = { topBar = {
GlassMediumFlexibleTopAppBar( GlassMediumFlexibleTopAppBar(
title = { Text(stringResource(R.string.about)) }, title = stringResource(R.string.about),
navigationIcon = { navigationIcon = {
TopbarNavigationButton(onClick = onBack) TopbarNavigationButton(onClick = onBack)
}, },
@@ -81,9 +80,9 @@ fun AboutScreen(
.width(160.dp) .width(160.dp)
.align(Alignment.CenterHorizontally) .align(Alignment.CenterHorizontally)
) )
Text( AppText(
text = stringResource(R.string.app_name), text = stringResource(R.string.app_name),
style = MaterialTheme.typography.bodyLarge, style = LegadoTheme.typography.bodyLarge,
fontSize = 18.sp, fontSize = 18.sp,
fontWeight = FontWeight.Bold, fontWeight = FontWeight.Bold,
modifier = Modifier modifier = Modifier
@@ -91,16 +90,14 @@ fun AboutScreen(
.wrapContentWidth(Alignment.CenterHorizontally)) .wrapContentWidth(Alignment.CenterHorizontally))
TextCard( TextCard(
text = versionName, text = versionName,
backgroundColor = colorScheme.tertiaryContainer,
contentColor = colorScheme.onTertiaryContainer,
cornerRadius = 8.dp, cornerRadius = 8.dp,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.wrapContentWidth(Alignment.CenterHorizontally) .wrapContentWidth(Alignment.CenterHorizontally)
.padding(vertical = 4.dp)) .padding(vertical = 4.dp))
Text( AppText(
text = stringResource(R.string.about_description), text = stringResource(R.string.about_description),
style = MaterialTheme.typography.bodyLarge, style = LegadoTheme.typography.bodyLarge,
fontSize = 12.sp, fontSize = 12.sp,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@@ -18,7 +18,6 @@ import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
@@ -31,19 +30,23 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import io.legado.app.R import io.legado.app.R
import io.legado.app.constant.AppLog import io.legado.app.constant.AppLog
import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet import io.legado.app.ui.theme.LegadoTheme
import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
import io.legado.app.ui.widget.components.text.AppText
import io.legado.app.utils.LogUtils import io.legado.app.utils.LogUtils
import java.util.Date import java.util.Date
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun AppLogSheet( fun AppLogSheet(
show: Boolean,
onDismissRequest: () -> Unit onDismissRequest: () -> Unit
) { ) {
var logs by remember { mutableStateOf(AppLog.logs) } var logs by remember { mutableStateOf(AppLog.logs) }
var showStackTrace by remember { mutableStateOf<String?>(null) } var showStackTrace by remember { mutableStateOf<String?>(null) }
GlassModalBottomSheet( AppModalBottomSheet(
show = show,
onDismissRequest = onDismissRequest onDismissRequest = onDismissRequest
) { ) {
Row( Row(
@@ -52,9 +55,9 @@ fun AppLogSheet(
.padding(horizontal = 16.dp, vertical = 8.dp), .padding(horizontal = 16.dp, vertical = 8.dp),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
Text( AppText(
text = stringResource(R.string.log), text = stringResource(R.string.log),
style = MaterialTheme.typography.titleLarge, style = LegadoTheme.typography.titleLarge,
modifier = Modifier.weight(1f) modifier = Modifier.weight(1f)
) )
IconButton(onClick = { IconButton(onClick = {
@@ -82,17 +85,17 @@ fun AppLogSheet(
showStackTrace?.let { stackTrace -> showStackTrace?.let { stackTrace ->
AlertDialog( AlertDialog(
onDismissRequest = { showStackTrace = null }, onDismissRequest = { showStackTrace = null },
title = { Text("Log") }, title = { AppText("Log") },
text = { text = {
SelectionContainer { SelectionContainer {
Column(modifier = Modifier.verticalScroll(rememberScrollState())) { Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
Text(text = stackTrace, style = MaterialTheme.typography.bodySmall) AppText(text = stackTrace, style = LegadoTheme.typography.bodySmall)
} }
} }
}, },
confirmButton = { confirmButton = {
TextButton(onClick = { showStackTrace = null }) { TextButton(onClick = { showStackTrace = null }) {
Text(stringResource(android.R.string.ok)) AppText(stringResource(android.R.string.ok))
} }
} }
) )
@@ -110,14 +113,14 @@ private fun LogItem(
.clickable(onClick = onClick) .clickable(onClick = onClick)
.padding(16.dp) .padding(16.dp)
) { ) {
Text( AppText(
text = LogUtils.logTimeFormat.format(Date(item.first)), text = LogUtils.logTimeFormat.format(Date(item.first)),
style = MaterialTheme.typography.labelMedium, style = LegadoTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.outline color = MaterialTheme.colorScheme.outline
) )
Text( AppText(
text = item.second, text = item.second,
style = MaterialTheme.typography.bodyMedium, style = LegadoTheme.typography.bodyMedium,
modifier = Modifier.padding(top = 4.dp) modifier = Modifier.padding(top = 4.dp)
) )
} }
@@ -26,7 +26,6 @@ import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
@@ -41,8 +40,6 @@ import io.legado.app.data.entities.Bookmark
import io.legado.app.ui.widget.CollapsibleHeader import io.legado.app.ui.widget.CollapsibleHeader
import io.legado.app.ui.widget.components.AppScaffold import io.legado.app.ui.widget.components.AppScaffold
import io.legado.app.ui.widget.components.EmptyMessageView import io.legado.app.ui.widget.components.EmptyMessageView
import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
import io.legado.app.ui.widget.components.SearchBarSection import io.legado.app.ui.widget.components.SearchBarSection
import io.legado.app.ui.widget.components.bookmark.BookmarkEditSheet import io.legado.app.ui.widget.components.bookmark.BookmarkEditSheet
import io.legado.app.ui.widget.components.bookmark.BookmarkItem import io.legado.app.ui.widget.components.bookmark.BookmarkItem
@@ -51,6 +48,8 @@ import io.legado.app.ui.widget.components.lazylist.FastScrollLazyColumn
import io.legado.app.ui.widget.components.lazylist.Scroller import io.legado.app.ui.widget.components.lazylist.Scroller
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
import io.legado.app.ui.widget.components.topbar.GlassMediumFlexibleTopAppBar
import io.legado.app.ui.widget.components.topbar.GlassTopAppBarDefaults
import org.koin.androidx.compose.koinViewModel import org.koin.androidx.compose.koinViewModel
@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class, @OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class,
@@ -98,11 +97,7 @@ fun AllBookmarkScreen(
topBar = { topBar = {
Column { Column {
GlassMediumFlexibleTopAppBar( GlassMediumFlexibleTopAppBar(
title = { title = "所有书签",
Text(
text = "所有书签"
)
},
scrollBehavior = scrollBehavior, scrollBehavior = scrollBehavior,
navigationIcon = { navigationIcon = {
TopbarNavigationButton(onClick = onBack) TopbarNavigationButton(onClick = onBack)
@@ -132,7 +127,7 @@ fun AllBookmarkScreen(
onDismissRequest = { showMenu = false } onDismissRequest = { showMenu = false }
) { ) {
RoundDropdownMenuItem( RoundDropdownMenuItem(
text = { Text("导出 JSON") }, text = "导出 JSON",
onClick = { onClick = {
showMenu = false showMenu = false
pendingExportIsMd = false pendingExportIsMd = false
@@ -140,7 +135,7 @@ fun AllBookmarkScreen(
} }
) )
RoundDropdownMenuItem( RoundDropdownMenuItem(
text = { Text("导出 Markdown") }, text = "导出 Markdown",
onClick = { onClick = {
showMenu = false showMenu = false
pendingExportIsMd = true pendingExportIsMd = true
@@ -242,22 +237,21 @@ fun AllBookmarkScreen(
} }
} }
if (showBottomSheet && editingBookmark != null) { BookmarkEditSheet(
BookmarkEditSheet( show = showBottomSheet && editingBookmark != null,
bookmark = editingBookmark!!, bookmark = editingBookmark ?: Bookmark(),
onDismiss = { onDismiss = {
showBottomSheet = false showBottomSheet = false
editingBookmark = null editingBookmark = null
}, },
onSave = { updatedBookmark -> onSave = { updatedBookmark ->
viewModel.updateBookmark(updatedBookmark) viewModel.updateBookmark(updatedBookmark)
showBottomSheet = false showBottomSheet = false
}, },
onDelete = { bookmarkToDelete -> onDelete = { bookmarkToDelete ->
viewModel.deleteBookmark(bookmarkToDelete) viewModel.deleteBookmark(bookmarkToDelete)
showBottomSheet = false showBottomSheet = false
} }
) )
}
} }
} }
@@ -52,7 +52,6 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Slider import androidx.compose.material3.Slider
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.pulltorefresh.PullToRefreshBox import androidx.compose.material3.pulltorefresh.PullToRefreshBox
import androidx.compose.material3.pulltorefresh.PullToRefreshDefaults import androidx.compose.material3.pulltorefresh.PullToRefreshDefaults
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
@@ -79,12 +78,10 @@ import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
import io.legado.app.data.entities.SearchBook import io.legado.app.data.entities.SearchBook
import io.legado.app.data.entities.rule.ExploreKind import io.legado.app.data.entities.rule.ExploreKind
import io.legado.app.model.BookShelfState import io.legado.app.model.BookShelfState
import io.legado.app.ui.theme.LegadoTheme
import io.legado.app.ui.theme.responsiveHazeEffect import io.legado.app.ui.theme.responsiveHazeEffect
import io.legado.app.ui.theme.responsiveHazeSource import io.legado.app.ui.theme.responsiveHazeSource
import io.legado.app.ui.widget.components.AnimatedTextLine
import io.legado.app.ui.widget.components.AppScaffold import io.legado.app.ui.widget.components.AppScaffold
import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
import io.legado.app.ui.widget.components.SearchBarSection import io.legado.app.ui.widget.components.SearchBarSection
import io.legado.app.ui.widget.components.button.AnimatedTextButton import io.legado.app.ui.widget.components.button.AnimatedTextButton
import io.legado.app.ui.widget.components.button.TopBarActionButton import io.legado.app.ui.widget.components.button.TopBarActionButton
@@ -93,7 +90,10 @@ import io.legado.app.ui.widget.components.card.TextCard
import io.legado.app.ui.widget.components.cover.Cover import io.legado.app.ui.widget.components.cover.Cover
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
import io.legado.app.ui.widget.components.text.AppText
import io.legado.app.ui.widget.components.topbar.GlassMediumFlexibleTopAppBar
import io.legado.app.ui.widget.components.topbar.GlassTopAppBarDefaults
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import org.koin.androidx.compose.koinViewModel import org.koin.androidx.compose.koinViewModel
@@ -180,112 +180,111 @@ fun ExploreShowScreen(
} }
} }
if (showGridCountSheet) {
GlassModalBottomSheet( AppModalBottomSheet(
show = showGridCountSheet,
modifier = Modifier
.padding(16.dp),
onDismissRequest = { showGridCountSheet = false }
) {
Row(
modifier = Modifier modifier = Modifier
.padding(16.dp), .fillMaxWidth()
onDismissRequest = { showGridCountSheet = false } .padding(bottom = 16.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center
) { ) {
Row( AppText(
modifier = Modifier text = "布局列数",
.fillMaxWidth() style = LegadoTheme.typography.titleMedium.copy(fontWeight = FontWeight.Bold)
.padding(bottom = 16.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center
) {
Text(
text = "布局列数",
style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.Bold)
)
Spacer(modifier = Modifier.width(12.dp))
TextCard(
text = "$gridColumnCount 列",
textStyle = MaterialTheme.typography.titleSmall,
verticalPadding = 4.dp,
horizontalPadding = 12.dp,
cornerRadius = 12.dp
)
}
Slider(
value = gridColumnCount.toFloat(),
onValueChange = {
val col = it.toInt().coerceIn(1, 10)
viewModel.saveGridCount(col)
},
valueRange = 1f..10f,
steps = 8,
modifier = Modifier.padding(horizontal = 20.dp)
) )
Spacer(Modifier.height(24.dp)) Spacer(modifier = Modifier.width(12.dp))
OutlinedButton( TextCard(
onClick = { showGridCountSheet = false }, text = "$gridColumnCount 列",
modifier = Modifier textStyle = LegadoTheme.typography.titleSmall,
.fillMaxWidth() verticalPadding = 4.dp,
.padding(horizontal = 16.dp) horizontalPadding = 12.dp,
) { cornerRadius = 12.dp
Text("完成") )
} }
Slider(
value = gridColumnCount.toFloat(),
onValueChange = {
val col = it.toInt().coerceIn(1, 10)
viewModel.saveGridCount(col)
},
valueRange = 1f..10f,
steps = 8,
modifier = Modifier.padding(horizontal = 20.dp)
)
Spacer(Modifier.height(24.dp))
OutlinedButton(
onClick = { showGridCountSheet = false },
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
) {
AppText("完成")
} }
} }
if (showKindSheet) {
GlassModalBottomSheet(
onDismissRequest = { showKindSheet = false }
) {
var kindQuery by remember { mutableStateOf("") } AppModalBottomSheet(
show = showKindSheet,
onDismissRequest = { showKindSheet = false }
) {
SearchBarSection( var kindQuery by remember { mutableStateOf("") }
query = kindQuery,
backgroundColor = MaterialTheme.colorScheme.surfaceContainerHigh,
onQueryChange = { kindQuery = it },
placeholder = "选择或搜索分类",
)
val filteredKinds = remember(kindQuery, kinds) { SearchBarSection(
if (kindQuery.isBlank()) kinds query = kindQuery,
else kinds.filter { kind -> backgroundColor = MaterialTheme.colorScheme.surfaceContainerHigh,
kind.title.contains(kindQuery, ignoreCase = true) || onQueryChange = { kindQuery = it },
(kind.url?.contains(kindQuery, ignoreCase = true) == true) placeholder = "选择或搜索分类",
} )
val filteredKinds = remember(kindQuery, kinds) {
if (kindQuery.isBlank()) kinds
else kinds.filter { kind ->
kind.title.contains(kindQuery, ignoreCase = true) ||
(kind.url?.contains(kindQuery, ignoreCase = true) == true)
} }
}
LazyVerticalGrid( LazyVerticalGrid(
columns = GridCells.Fixed(3), columns = GridCells.Fixed(3),
contentPadding = PaddingValues(16.dp), contentPadding = PaddingValues(16.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp), horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp), verticalArrangement = Arrangement.spacedBy(8.dp),
modifier = Modifier.weight(1f, fill = false) modifier = Modifier.weight(1f, fill = false)
) { ) {
itemsIndexed( itemsIndexed(
items = filteredKinds, items = filteredKinds,
key = { index, kind -> "${kind.url ?: kind.title}_$index" }, key = { index, kind -> "${kind.url ?: kind.title}_$index" },
span = { _, kind -> span = { _, kind ->
val isClickable = !kind.url.isNullOrBlank() val isClickable = !kind.url.isNullOrBlank()
if (isClickable) GridItemSpan(1) else GridItemSpan(3) if (isClickable) GridItemSpan(1) else GridItemSpan(3)
}
) { _, kind ->
KindGridItem(
modifier = Modifier.animateItem(),
kind = kind,
currentTitle = selectedTitle ?: title,
onClick = {
showKindSheet = false
viewModel.switchExploreUrl(kind)
} }
) { _, kind -> )
KindGridItem(
modifier = Modifier.animateItem(),
kind = kind,
currentTitle = selectedTitle ?: title,
onClick = {
showKindSheet = false
viewModel.switchExploreUrl(kind)
}
)
}
} }
} }
} }
AppScaffold( AppScaffold(
modifier = Modifier modifier = Modifier
.nestedScroll(scrollBehavior.nestedScrollConnection), .nestedScroll(scrollBehavior.nestedScrollConnection),
@@ -294,14 +293,7 @@ fun ExploreShowScreen(
modifier = Modifier.responsiveHazeEffect( modifier = Modifier.responsiveHazeEffect(
state = hazeState state = hazeState
), ),
title = { title = selectedTitle ?: title,
AnimatedTextLine(
selectedTitle ?: title,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
},
colors = GlassTopAppBarDefaults.glassColors(),
navigationIcon = { navigationIcon = {
TopbarNavigationButton(onClick = onBack) TopbarNavigationButton(onClick = onBack)
}, },
@@ -346,7 +338,7 @@ fun ExploreShowScreen(
onDismissRequest = { showMenu = false } onDismissRequest = { showMenu = false }
) { ) {
RoundDropdownMenuItem( RoundDropdownMenuItem(
text = { Text("全部显示") }, text = "全部显示",
onClick = { onClick = {
viewModel.setFilterState(BookFilterState.SHOW_ALL) viewModel.setFilterState(BookFilterState.SHOW_ALL)
showMenu = false showMenu = false
@@ -358,7 +350,7 @@ fun ExploreShowScreen(
) )
RoundDropdownMenuItem( RoundDropdownMenuItem(
text = { Text("隐藏已在书架的同源书籍") }, text = "隐藏已在书架的同源书籍",
onClick = { onClick = {
viewModel.setFilterState(BookFilterState.HIDE_IN_SHELF) viewModel.setFilterState(BookFilterState.HIDE_IN_SHELF)
showMenu = false showMenu = false
@@ -370,7 +362,7 @@ fun ExploreShowScreen(
) )
RoundDropdownMenuItem( RoundDropdownMenuItem(
text = { Text("隐藏已在书架的非同源书籍") }, text = "隐藏已在书架的非同源书籍",
onClick = { onClick = {
viewModel.setFilterState(BookFilterState.HIDE_SAME_NAME_AUTHOR) viewModel.setFilterState(BookFilterState.HIDE_SAME_NAME_AUTHOR)
showMenu = false showMenu = false
@@ -382,7 +374,7 @@ fun ExploreShowScreen(
) )
RoundDropdownMenuItem( RoundDropdownMenuItem(
text = { Text("只显示不在书架的书籍") }, text = "只显示不在书架的书籍",
onClick = { onClick = {
viewModel.setFilterState(BookFilterState.SHOW_NOT_IN_SHELF_ONLY) viewModel.setFilterState(BookFilterState.SHOW_NOT_IN_SHELF_ONLY)
showMenu = false showMenu = false
@@ -509,7 +501,7 @@ fun KindGridItem(
enabled = isClickable, enabled = isClickable,
selected = isSelected, selected = isSelected,
label = { label = {
Text( AppText(
text = kind.title, text = kind.title,
fontSize = 12.sp, fontSize = 12.sp,
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
@@ -576,32 +568,32 @@ fun ExploreBookItem(
.weight(1f) .weight(1f)
.align(Alignment.CenterVertically)) { .align(Alignment.CenterVertically)) {
Text( AppText(
text = book.name, text = book.name,
style = MaterialTheme.typography.titleSmall, style = LegadoTheme.typography.titleSmall,
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis overflow = TextOverflow.Ellipsis
) )
Row { Row {
Text( AppText(
text = book.author, text = book.author,
style = MaterialTheme.typography.bodySmall, style = LegadoTheme.typography.bodySmall,
maxLines = 1 maxLines = 1
) )
val latestChapter = book.latestChapterTitle val latestChapter = book.latestChapterTitle
if (!latestChapter.isNullOrEmpty()) { if (!latestChapter.isNullOrEmpty()) {
Text( AppText(
text = " • ", text = " • ",
style = MaterialTheme.typography.bodySmall, style = LegadoTheme.typography.bodySmall,
color = Color.Gray, color = Color.Gray,
maxLines = 1 maxLines = 1
) )
Text( AppText(
text = "最新: $latestChapter", text = "最新: $latestChapter",
style = MaterialTheme.typography.bodySmall, style = LegadoTheme.typography.bodySmall,
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis overflow = TextOverflow.Ellipsis
) )
@@ -612,9 +604,9 @@ fun ExploreBookItem(
val intro = book.intro?.replace("\\s+".toRegex(), "") ?: "" val intro = book.intro?.replace("\\s+".toRegex(), "") ?: ""
if (intro.isNotEmpty()) { if (intro.isNotEmpty()) {
Text( AppText(
text = intro, text = intro,
style = MaterialTheme.typography.labelSmall, style = LegadoTheme.typography.labelSmall,
color = Color.Gray, color = Color.Gray,
maxLines = 2, maxLines = 2,
minLines = 2, minLines = 2,
@@ -654,9 +646,12 @@ fun ExploreBookGridItem(
val content: (@Composable RowScope.() -> Unit)? = if (!badgeText.isNullOrBlank()) { val content: (@Composable RowScope.() -> Unit)? = if (!badgeText.isNullOrBlank()) {
{ {
Text( AppText(
text = badgeText, text = badgeText,
style = MaterialTheme.typography.labelSmall.copy(fontWeight = FontWeight.Bold, fontSize = 9.sp) style = LegadoTheme.typography.labelSmall.copy(
fontWeight = FontWeight.Bold,
fontSize = 9.sp
)
) )
} }
} else { } else {
@@ -681,9 +676,9 @@ fun ExploreBookGridItem(
Spacer(modifier = Modifier.height(4.dp)) Spacer(modifier = Modifier.height(4.dp))
Text( AppText(
text = book.name, text = book.name,
style = MaterialTheme.typography.bodySmall, style = LegadoTheme.typography.bodySmall,
fontWeight = FontWeight.Bold, fontWeight = FontWeight.Bold,
maxLines = 2, maxLines = 2,
overflow = TextOverflow.Ellipsis overflow = TextOverflow.Ellipsis
@@ -698,10 +693,10 @@ fun TagChip(text: String) {
color = MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.5f), color = MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.5f),
shape = RoundedCornerShape(4.dp) shape = RoundedCornerShape(4.dp)
) { ) {
Text( AppText(
text = text, text = text,
modifier = Modifier.padding(horizontal = 4.dp, vertical = 2.dp), modifier = Modifier.padding(horizontal = 4.dp, vertical = 2.dp),
style = MaterialTheme.typography.labelSmall, style = LegadoTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.primary color = MaterialTheme.colorScheme.primary
) )
} }
@@ -742,13 +737,13 @@ fun LoadMoreFooter(
}, },
label = "FooterTextChange" label = "FooterTextChange"
) { text -> ) { text ->
Text( AppText(
text = text, text = text,
color = when { color = when {
errorMsg != null -> Color.Red errorMsg != null -> Color.Red
else -> Color.Gray else -> Color.Gray
}, },
style = MaterialTheme.typography.bodySmall style = LegadoTheme.typography.bodySmall
) )
} }
@@ -8,7 +8,6 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
@@ -18,11 +17,9 @@ import androidx.compose.material.icons.filled.Restore
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.OutlinedIconButton import androidx.compose.material3.OutlinedIconButton
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableIntStateOf
@@ -39,9 +36,10 @@ import io.legado.app.R
import io.legado.app.data.entities.BookGroup import io.legado.app.data.entities.BookGroup
import io.legado.app.lib.dialogs.alert import io.legado.app.lib.dialogs.alert
import io.legado.app.ui.widget.components.cover.Cover import io.legado.app.ui.widget.components.cover.Cover
import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
import io.legado.app.ui.widget.components.settingItem.CompactDropdownSettingItem import io.legado.app.ui.widget.components.settingItem.CompactDropdownSettingItem
import io.legado.app.ui.widget.components.settingItem.CompactSwitchSettingItem import io.legado.app.ui.widget.components.settingItem.CompactSwitchSettingItem
import io.legado.app.ui.widget.components.text.AppText
import io.legado.app.utils.FileUtils import io.legado.app.utils.FileUtils
import io.legado.app.utils.MD5Utils import io.legado.app.utils.MD5Utils
import io.legado.app.utils.SelectImageContract import io.legado.app.utils.SelectImageContract
@@ -55,11 +53,12 @@ import java.io.FileOutputStream
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun GroupEditSheet( fun GroupEditSheet(
show: Boolean,
group: BookGroup? = null, group: BookGroup? = null,
onDismissRequest: () -> Unit, onDismissRequest: () -> Unit,
viewModel: GroupViewModel = koinViewModel() viewModel: GroupViewModel = koinViewModel()
) { ) {
GlassModalBottomSheet(onDismissRequest = onDismissRequest) { AppModalBottomSheet(show = show, onDismissRequest = onDismissRequest) {
GroupEditContent( GroupEditContent(
group = group, group = group,
onDismissRequest = onDismissRequest, onDismissRequest = onDismissRequest,
@@ -108,17 +107,9 @@ fun GroupEditContent(
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 16.dp)
.padding(bottom = 16.dp)
.verticalScroll(rememberScrollState()), .verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally,
) { ) {
Text(
text = stringResource(R.string.group_edit),
style = MaterialTheme.typography.titleLarge,
modifier = Modifier.padding(vertical = 16.dp)
)
Row( Row(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.Top, verticalAlignment = Alignment.Top,
@@ -138,7 +129,7 @@ fun GroupEditContent(
OutlinedTextField( OutlinedTextField(
value = groupName, value = groupName,
onValueChange = { groupName = it }, onValueChange = { groupName = it },
label = { Text(stringResource(R.string.group_name)) }, label = { AppText(stringResource(R.string.group_name)) },
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
singleLine = true singleLine = true
) )
@@ -206,7 +197,7 @@ fun GroupEditContent(
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
OutlinedButton(onClick = onDismissRequest) { OutlinedButton(onClick = onDismissRequest) {
Text(stringResource(R.string.cancel)) AppText(stringResource(R.string.cancel))
} }
Button(onClick = { Button(onClick = {
if (groupName.isEmpty()) { if (groupName.isEmpty()) {
@@ -235,7 +226,7 @@ fun GroupEditContent(
} }
} }
}) { }) {
Text(stringResource(R.string.ok)) AppText(stringResource(R.string.ok))
} }
} }
} }
@@ -51,7 +51,6 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.RadioButton import androidx.compose.material3.RadioButton
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.material3.pulltorefresh.PullToRefreshBox import androidx.compose.material3.pulltorefresh.PullToRefreshBox
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
@@ -80,6 +79,7 @@ import io.legado.app.data.entities.Book
import io.legado.app.data.entities.Server import io.legado.app.data.entities.Server
import io.legado.app.help.config.AppConfig import io.legado.app.help.config.AppConfig
import io.legado.app.model.remote.RemoteBook import io.legado.app.model.remote.RemoteBook
import io.legado.app.ui.theme.LegadoTheme
import io.legado.app.ui.widget.components.ActionItem import io.legado.app.ui.widget.components.ActionItem
import io.legado.app.ui.widget.components.EmptyMessageView import io.legado.app.ui.widget.components.EmptyMessageView
import io.legado.app.ui.widget.components.SelectionActions import io.legado.app.ui.widget.components.SelectionActions
@@ -88,7 +88,8 @@ import io.legado.app.ui.widget.components.button.SmallTonalIconButton
import io.legado.app.ui.widget.components.card.TextCard import io.legado.app.ui.widget.components.card.TextCard
import io.legado.app.ui.widget.components.list.ListScaffold import io.legado.app.ui.widget.components.list.ListScaffold
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
import io.legado.app.ui.widget.components.text.AppText
import io.legado.app.utils.ArchiveUtils import io.legado.app.utils.ArchiveUtils
import io.legado.app.utils.ConvertUtils import io.legado.app.utils.ConvertUtils
import io.legado.app.utils.FileDoc import io.legado.app.utils.FileDoc
@@ -157,19 +158,19 @@ fun RemoteBookScreen(
is RemoteBookDialog.DownloadArchive -> { is RemoteBookDialog.DownloadArchive -> {
AlertDialog( AlertDialog(
onDismissRequest = { dialogState = null }, onDismissRequest = { dialogState = null },
title = { Text(stringResource(R.string.draw)) }, title = { AppText(stringResource(R.string.draw)) },
text = { Text(stringResource(R.string.archive_not_found)) }, text = { AppText(stringResource(R.string.archive_not_found)) },
confirmButton = { confirmButton = {
OutlinedButton(onClick = { OutlinedButton(onClick = {
scope.launch { viewModel.addToBookshelf(setOf(state.remoteBook)) } scope.launch { viewModel.addToBookshelf(setOf(state.remoteBook)) }
dialogState = null dialogState = null
}) { }) {
Text(stringResource(android.R.string.ok)) AppText(stringResource(android.R.string.ok))
} }
}, },
dismissButton = { dismissButton = {
TextButton(onClick = { dialogState = null }) { TextButton(onClick = { dialogState = null }) {
Text(stringResource(android.R.string.cancel)) AppText(stringResource(android.R.string.cancel))
} }
} }
) )
@@ -178,19 +179,19 @@ fun RemoteBookScreen(
is RemoteBookDialog.ReImport -> { is RemoteBookDialog.ReImport -> {
AlertDialog( AlertDialog(
onDismissRequest = { dialogState = null }, onDismissRequest = { dialogState = null },
title = { Text("是否重新加入书架?") }, title = { AppText("是否重新加入书架?") },
text = { Text("将会覆盖书籍") }, text = { AppText("将会覆盖书籍") },
confirmButton = { confirmButton = {
OutlinedButton(onClick = { OutlinedButton(onClick = {
scope.launch { viewModel.addToBookshelf(setOf(state.remoteBook)) } scope.launch { viewModel.addToBookshelf(setOf(state.remoteBook)) }
dialogState = null dialogState = null
}) { }) {
Text(stringResource(android.R.string.ok)) AppText(stringResource(android.R.string.ok))
} }
}, },
dismissButton = { dismissButton = {
TextButton(onClick = { dialogState = null }) { TextButton(onClick = { dialogState = null }) {
Text(stringResource(android.R.string.cancel)) AppText(stringResource(android.R.string.cancel))
} }
} }
) )
@@ -198,42 +199,41 @@ fun RemoteBookScreen(
} }
} }
if (showSheet != null) { AppModalBottomSheet(
GlassModalBottomSheet( show = showSheet != null,
onDismissRequest = { showSheet = null } onDismissRequest = { showSheet = null }
) { ) {
when (val state = showSheet) { when (val state = showSheet) {
is RemoteBookSheet.Servers -> { is RemoteBookSheet.Servers -> {
ServersSheetContent( ServersSheetContent(
servers = uiState.servers, servers = uiState.servers,
selectedServerId = uiState.selectedServerId, selectedServerId = uiState.selectedServerId,
onSelect = { onSelect = {
viewModel.selectServer(it) viewModel.selectServer(it)
showSheet = null showSheet = null
}, },
onEdit = { showSheet = RemoteBookSheet.ServerConfig(it) }, onEdit = { showSheet = RemoteBookSheet.ServerConfig(it) },
onDelete = { viewModel.deleteServer(it) }, onDelete = { viewModel.deleteServer(it) },
onAdd = { showSheet = RemoteBookSheet.ServerConfig(null) }, onAdd = { showSheet = RemoteBookSheet.ServerConfig(null) },
onDefault = { onDefault = {
viewModel.selectServer(AppConst.DEFAULT_WEBDAV_ID) viewModel.selectServer(AppConst.DEFAULT_WEBDAV_ID)
showSheet = null showSheet = null
} }
) )
}
is RemoteBookSheet.ServerConfig -> {
ServerConfigSheetContent(
server = state.server,
onSave = {
viewModel.saveServer(it)
showSheet = RemoteBookSheet.Servers
},
onCancel = { showSheet = RemoteBookSheet.Servers }
)
}
else -> {}
} }
is RemoteBookSheet.ServerConfig -> {
ServerConfigSheetContent(
server = state.server,
onSave = {
viewModel.saveServer(it)
showSheet = RemoteBookSheet.Servers
},
onCancel = { showSheet = RemoteBookSheet.Servers }
)
}
else -> {}
} }
} }
@@ -259,7 +259,7 @@ fun RemoteBookScreen(
}, },
dropDownMenuContent = { dismiss -> dropDownMenuContent = { dismiss ->
RoundDropdownMenuItem( RoundDropdownMenuItem(
text = { Text("按名称排序") }, text = "按名称排序",
onClick = { onClick = {
viewModel.toggleSort(RemoteBookSort.Name) viewModel.toggleSort(RemoteBookSort.Name)
dismiss() dismiss()
@@ -271,7 +271,7 @@ fun RemoteBookScreen(
} }
) )
RoundDropdownMenuItem( RoundDropdownMenuItem(
text = { Text("按时间排序") }, text = "按时间排序",
onClick = { onClick = {
viewModel.toggleSort(RemoteBookSort.Default) viewModel.toggleSort(RemoteBookSort.Default)
dismiss() dismiss()
@@ -381,9 +381,9 @@ private fun ServersSheetContent(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween horizontalArrangement = Arrangement.SpaceBetween
) { ) {
Text( AppText(
text = stringResource(R.string.server_config), text = stringResource(R.string.server_config),
style = MaterialTheme.typography.titleLarge style = LegadoTheme.typography.titleLarge
) )
IconButton(onClick = onAdd) { IconButton(onClick = onAdd) {
Icon(Icons.Default.Add, contentDescription = "添加") Icon(Icons.Default.Add, contentDescription = "添加")
@@ -447,9 +447,9 @@ private fun ServerItem(
ListItem( ListItem(
modifier = Modifier.animateContentSize(), modifier = Modifier.animateContentSize(),
headlineContent = { headlineContent = {
Text( AppText(
text = name, text = name,
style = MaterialTheme.typography.titleMedium, style = LegadoTheme.typography.titleMedium,
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis overflow = TextOverflow.Ellipsis
) )
@@ -462,9 +462,9 @@ private fun ServerItem(
}, },
supportingContent = url?.let { supportingContent = url?.let {
{ {
Text( AppText(
text = it, text = it,
style = MaterialTheme.typography.bodySmall, style = LegadoTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant, color = MaterialTheme.colorScheme.onSurfaceVariant,
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis overflow = TextOverflow.Ellipsis
@@ -514,16 +514,16 @@ private fun ServerConfigSheetContent(
.padding(16.dp) .padding(16.dp)
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState())
) { ) {
Text( AppText(
text = if (server == null) "添加服务器" else "编辑服务器", text = if (server == null) "添加服务器" else "编辑服务器",
style = MaterialTheme.typography.titleLarge, style = LegadoTheme.typography.titleLarge,
modifier = Modifier.padding(bottom = 16.dp) modifier = Modifier.padding(bottom = 16.dp)
) )
OutlinedTextField( OutlinedTextField(
value = name, value = name,
onValueChange = { name = it }, onValueChange = { name = it },
label = { Text("名称") }, label = { AppText("名称") },
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
singleLine = true singleLine = true
) )
@@ -531,7 +531,7 @@ private fun ServerConfigSheetContent(
OutlinedTextField( OutlinedTextField(
value = url, value = url,
onValueChange = { url = it }, onValueChange = { url = it },
label = { Text("URL") }, label = { AppText("URL") },
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
singleLine = true singleLine = true
) )
@@ -539,7 +539,7 @@ private fun ServerConfigSheetContent(
OutlinedTextField( OutlinedTextField(
value = username, value = username,
onValueChange = { username = it }, onValueChange = { username = it },
label = { Text("用户名") }, label = { AppText("用户名") },
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
singleLine = true singleLine = true
) )
@@ -547,7 +547,7 @@ private fun ServerConfigSheetContent(
OutlinedTextField( OutlinedTextField(
value = password, value = password,
onValueChange = { password = it }, onValueChange = { password = it },
label = { Text("密码") }, label = { AppText("密码") },
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
singleLine = true, singleLine = true,
visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(), visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(),
@@ -571,7 +571,7 @@ private fun ServerConfigSheetContent(
onClick = onCancel, onClick = onCancel,
modifier = Modifier.weight(1f) modifier = Modifier.weight(1f)
) { ) {
Text(stringResource(android.R.string.cancel)) AppText(stringResource(android.R.string.cancel))
} }
Button( Button(
onClick = { onClick = {
@@ -587,7 +587,7 @@ private fun ServerConfigSheetContent(
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f),
enabled = name.isNotBlank() && url.isNotBlank() enabled = name.isNotBlank() && url.isNotBlank()
) { ) {
Text("保存") AppText("保存")
} }
} }
} }
@@ -625,9 +625,9 @@ private fun PathNavigationBar(
itemsIndexed(pathNames) { index, name -> itemsIndexed(pathNames) { index, name ->
val isLast = index == pathNames.lastIndex val isLast = index == pathNames.lastIndex
Text( AppText(
text = name, text = name,
style = MaterialTheme.typography.labelSmall, style = LegadoTheme.typography.labelSmall,
fontWeight = if (isLast) FontWeight.SemiBold else FontWeight.Medium, fontWeight = if (isLast) FontWeight.SemiBold else FontWeight.Medium,
color = if (isLast) color = if (isLast)
MaterialTheme.colorScheme.primary MaterialTheme.colorScheme.primary
@@ -716,9 +716,9 @@ private fun RemoteBookItem(
modifier = Modifier.weight(1f) modifier = Modifier.weight(1f)
) { ) {
Text( AppText(
text = book.filename.substringBeforeLast("."), text = book.filename.substringBeforeLast("."),
style = MaterialTheme.typography.bodyMedium, style = LegadoTheme.typography.bodyMedium,
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis overflow = TextOverflow.Ellipsis
) )
@@ -732,7 +732,7 @@ private fun RemoteBookItem(
if (book.contentType.isNotEmpty()) { if (book.contentType.isNotEmpty()) {
TextCard( TextCard(
text = book.contentType.uppercase(), text = book.contentType.uppercase(),
textStyle = MaterialTheme.typography.labelSmall, textStyle = LegadoTheme.typography.labelSmall,
horizontalPadding = 4.dp, horizontalPadding = 4.dp,
verticalPadding = 2.dp, verticalPadding = 2.dp,
cornerRadius = 4.dp, cornerRadius = 4.dp,
@@ -743,11 +743,11 @@ private fun RemoteBookItem(
Spacer(modifier = Modifier.width(6.dp)) Spacer(modifier = Modifier.width(6.dp))
} }
Text( AppText(
text = "${ConvertUtils.formatFileSize(book.size)} • ${ text = "${ConvertUtils.formatFileSize(book.size)} • ${
AppConst.dateFormat.format(book.lastModify) AppConst.dateFormat.format(book.lastModify)
}", }",
style = MaterialTheme.typography.labelMedium, style = LegadoTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant color = MaterialTheme.colorScheme.onSurfaceVariant
) )
} }
@@ -30,7 +30,6 @@ import androidx.compose.material3.ExposedDropdownMenuDefaults
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.OutlinedButton import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
@@ -47,12 +46,13 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import io.legado.app.R import io.legado.app.R
import io.legado.app.ui.book.changecover.ChangeCoverDialog import io.legado.app.ui.book.changecover.ChangeCoverDialog
import io.legado.app.ui.widget.components.AppScaffold import io.legado.app.ui.widget.components.AppScaffold
import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
import io.legado.app.ui.widget.components.button.TopBarButtonVariant import io.legado.app.ui.widget.components.button.TopBarButtonVariant
import io.legado.app.ui.widget.components.button.TopbarNavigationButton import io.legado.app.ui.widget.components.button.TopbarNavigationButton
import io.legado.app.ui.widget.components.cover.Cover import io.legado.app.ui.widget.components.cover.Cover
import io.legado.app.ui.widget.components.settingItem.SwitchSettingItem import io.legado.app.ui.widget.components.settingItem.SwitchSettingItem
import io.legado.app.ui.widget.components.text.AppText
import io.legado.app.ui.widget.components.topbar.GlassMediumFlexibleTopAppBar
import io.legado.app.ui.widget.components.topbar.GlassTopAppBarDefaults
import io.legado.app.utils.SelectImageContract import io.legado.app.utils.SelectImageContract
import io.legado.app.utils.launch import io.legado.app.utils.launch
import io.legado.app.utils.showDialogFragment import io.legado.app.utils.showDialogFragment
@@ -71,7 +71,7 @@ fun BookInfoEditScreen(
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
topBar = { topBar = {
GlassMediumFlexibleTopAppBar( GlassMediumFlexibleTopAppBar(
title = { Text(text = stringResource(id = R.string.book_info_edit)) }, title = stringResource(id = R.string.book_info_edit),
navigationIcon = { navigationIcon = {
TopbarNavigationButton( TopbarNavigationButton(
onClick = onBack, onClick = onBack,
@@ -191,35 +191,35 @@ fun BookInfoEditContent(
OutlinedTextField( OutlinedTextField(
value = uiState.name, value = uiState.name,
onValueChange = { viewModel.onNameChange(it) }, onValueChange = { viewModel.onNameChange(it) },
label = { Text("书名") }, label = { AppText("书名") },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) )
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))
OutlinedTextField( OutlinedTextField(
value = uiState.author, value = uiState.author,
onValueChange = { viewModel.onAuthorChange(it) }, onValueChange = { viewModel.onAuthorChange(it) },
label = { Text("作者") }, label = { AppText("作者") },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) )
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))
OutlinedTextField( OutlinedTextField(
value = uiState.coverUrl ?: "", value = uiState.coverUrl ?: "",
onValueChange = { viewModel.onCoverUrlChange(it) }, onValueChange = { viewModel.onCoverUrlChange(it) },
label = { Text("封面链接") }, label = { AppText("封面链接") },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) )
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))
OutlinedTextField( OutlinedTextField(
value = uiState.intro ?: "", value = uiState.intro ?: "",
onValueChange = { viewModel.onIntroChange(it) }, onValueChange = { viewModel.onIntroChange(it) },
label = { Text("简介") }, label = { AppText("简介") },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) )
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))
OutlinedTextField( OutlinedTextField(
value = uiState.remark ?: "", value = uiState.remark ?: "",
onValueChange = { viewModel.onRemarkChange(it) }, onValueChange = { viewModel.onRemarkChange(it) },
label = { Text("备注") }, label = { AppText("备注") },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) )
} }
@@ -251,7 +251,7 @@ fun BookTypeDropdown(
state = textFieldState, state = textFieldState,
readOnly = true, readOnly = true,
lineLimits = TextFieldLineLimits.SingleLine, lineLimits = TextFieldLineLimits.SingleLine,
label = { Text("书籍类型") }, label = { AppText("书籍类型") },
trailingIcon = { trailingIcon = {
ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded)
}, },
@@ -269,7 +269,7 @@ fun BookTypeDropdown(
) { ) {
bookTypes.forEach { option -> bookTypes.forEach { option ->
DropdownMenuItem( DropdownMenuItem(
text = { Text(option) }, text = { AppText(option) },
onClick = { onClick = {
onTypeSelected(option) onTypeSelected(option)
expanded = false expanded = false
@@ -32,18 +32,13 @@ import androidx.compose.material.icons.filled.Schedule
import androidx.compose.material.icons.filled.Search import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.filled.Timeline import androidx.compose.material.icons.filled.Timeline
import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.Checkbox import androidx.compose.material3.Checkbox
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
@@ -78,15 +73,15 @@ import androidx.compose.ui.zIndex
import cn.hutool.core.date.DateUtil import cn.hutool.core.date.DateUtil
import io.legado.app.data.entities.readRecord.ReadRecord import io.legado.app.data.entities.readRecord.ReadRecord
import io.legado.app.data.entities.readRecord.ReadRecordDetail import io.legado.app.data.entities.readRecord.ReadRecordDetail
import io.legado.app.ui.widget.components.AnimatedTextLine import io.legado.app.ui.theme.LegadoTheme
import io.legado.app.ui.widget.CollapsibleHeader
import io.legado.app.ui.widget.components.AppScaffold import io.legado.app.ui.widget.components.AppScaffold
import io.legado.app.ui.widget.components.EmptyMessageView import io.legado.app.ui.widget.components.EmptyMessageView
import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
import io.legado.app.ui.widget.components.SearchBarSection import io.legado.app.ui.widget.components.SearchBarSection
import io.legado.app.ui.widget.components.SectionHeader
import io.legado.app.ui.widget.components.button.AlertButton import io.legado.app.ui.widget.components.button.AlertButton
import io.legado.app.ui.widget.components.button.AppIconButton
import io.legado.app.ui.widget.components.button.TopbarNavigationButton import io.legado.app.ui.widget.components.button.TopbarNavigationButton
import io.legado.app.ui.widget.components.card.GlassCard
import io.legado.app.ui.widget.components.cover.Cover import io.legado.app.ui.widget.components.cover.Cover
import io.legado.app.ui.widget.components.heatmap.HeatmapCalendarTopBar import io.legado.app.ui.widget.components.heatmap.HeatmapCalendarTopBar
import io.legado.app.ui.widget.components.heatmap.HeatmapConfig import io.legado.app.ui.widget.components.heatmap.HeatmapConfig
@@ -98,9 +93,14 @@ import io.legado.app.ui.widget.components.heatmap.WeekdayLabelsColumn
import io.legado.app.ui.widget.components.heatmap.rememberDateRange import io.legado.app.ui.widget.components.heatmap.rememberDateRange
import io.legado.app.ui.widget.components.heatmap.rememberDaysInRange import io.legado.app.ui.widget.components.heatmap.rememberDaysInRange
import io.legado.app.ui.widget.components.heatmap.rememberWeeks import io.legado.app.ui.widget.components.heatmap.rememberWeeks
import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet import io.legado.app.ui.widget.components.icon.AppIcon
import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
import io.legado.app.ui.widget.components.swipe.SwipeAction import io.legado.app.ui.widget.components.swipe.SwipeAction
import io.legado.app.ui.widget.components.swipe.SwipeActionContainer import io.legado.app.ui.widget.components.swipe.SwipeActionContainer
import io.legado.app.ui.widget.components.text.AnimatedTextLine
import io.legado.app.ui.widget.components.text.AppText
import io.legado.app.ui.widget.components.topbar.GlassMediumFlexibleTopAppBar
import io.legado.app.ui.widget.components.topbar.GlassTopAppBarDefaults
import io.legado.app.utils.StringUtils.formatFriendlyDate import io.legado.app.utils.StringUtils.formatFriendlyDate
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.koin.androidx.compose.koinViewModel import org.koin.androidx.compose.koinViewModel
@@ -148,11 +148,7 @@ fun ReadRecordScreen(
topBar = { topBar = {
Column { Column {
GlassMediumFlexibleTopAppBar( GlassMediumFlexibleTopAppBar(
title = { title = "阅读记录",
Text(
text = "阅读记录"
)
},
subtitle = { subtitle = {
val subTitle = when (displayMode) { val subTitle = when (displayMode) {
DisplayMode.AGGREGATE -> "汇总视图" DisplayMode.AGGREGATE -> "汇总视图"
@@ -167,7 +163,7 @@ fun ReadRecordScreen(
TopbarNavigationButton(onClick = onBackClick) TopbarNavigationButton(onClick = onBackClick)
}, },
actions = { actions = {
IconButton(onClick = { AppIconButton(onClick = {
val newMode = when (displayMode) { val newMode = when (displayMode) {
DisplayMode.AGGREGATE -> DisplayMode.TIMELINE DisplayMode.AGGREGATE -> DisplayMode.TIMELINE
DisplayMode.TIMELINE -> DisplayMode.LATEST DisplayMode.TIMELINE -> DisplayMode.LATEST
@@ -181,13 +177,16 @@ fun ReadRecordScreen(
DisplayMode.LATEST -> Icons.AutoMirrored.Filled.List DisplayMode.LATEST -> Icons.AutoMirrored.Filled.List
} }
val description = if (displayMode == DisplayMode.AGGREGATE) "Switch to Timeline" else "Switch to Aggregate" val description = if (displayMode == DisplayMode.AGGREGATE) "Switch to Timeline" else "Switch to Aggregate"
Icon(icon, description) AppIcon(icon, description)
} }
IconButton(onClick = { showCalendar = !showCalendar }) { AppIconButton(onClick = { showCalendar = !showCalendar }) {
Icon(Icons.Default.CalendarMonth, contentDescription = "Toggle Calendar") AppIcon(
Icons.Default.CalendarMonth,
contentDescription = "Toggle Calendar"
)
} }
IconButton(onClick = { showSearch = !showSearch }) { AppIconButton(onClick = { showSearch = !showSearch }) {
Icon(Icons.Default.Search, contentDescription = null) AppIcon(Icons.Default.Search, contentDescription = null)
} }
}, },
scrollBehavior = scrollBehavior scrollBehavior = scrollBehavior
@@ -271,10 +270,10 @@ fun ReadRecordScreen(
onDismissRequest = { onDismissRequest = {
pendingDeleteAction = null pendingDeleteAction = null
}, },
title = { Text("确认删除") }, title = { AppText("确认删除") },
text = { text = {
Column { Column {
Text("确定要删除这条记录吗?") AppText("确定要删除这条记录吗?")
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))
Row( Row(
modifier = Modifier modifier = Modifier
@@ -289,10 +288,10 @@ fun ReadRecordScreen(
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
Checkbox(checked = skipDeleteConfirmTemp, onCheckedChange = null) Checkbox(checked = skipDeleteConfirmTemp, onCheckedChange = null)
Text( AppText(
text = "不再提示", text = "不再提示",
modifier = Modifier.padding(start = 8.dp), modifier = Modifier.padding(start = 8.dp),
style = MaterialTheme.typography.bodyMedium style = LegadoTheme.typography.bodyMedium
) )
} }
} }
@@ -311,30 +310,29 @@ fun ReadRecordScreen(
TextButton(onClick = { TextButton(onClick = {
pendingDeleteAction = null pendingDeleteAction = null
}) { }) {
Text("取消") AppText("取消")
} }
} }
) )
} }
if (showCalendar) { AppModalBottomSheet(
GlassModalBottomSheet( show = showCalendar,
onDismissRequest = { showCalendar = false } onDismissRequest = { showCalendar = false }
) { ) {
HeatmapCalendarSection( HeatmapCalendarSection(
dailyReadCounts = state.dailyReadCounts, dailyReadCounts = state.dailyReadCounts,
dailyReadTimes = state.dailyReadTimes, dailyReadTimes = state.dailyReadTimes,
selectedDate = state.selectedDate, selectedDate = state.selectedDate,
onDateSelected = { date -> onDateSelected = { date ->
viewModel.setSelectedDate(date) viewModel.setSelectedDate(date)
showCalendar = false showCalendar = false
}, },
onClearDate = { onClearDate = {
viewModel.setSelectedDate(null) viewModel.setSelectedDate(null)
showCalendar = false showCalendar = false
} }
) )
}
} }
@@ -346,10 +344,10 @@ fun ReadRecordScreen(
AlertDialog( AlertDialog(
onDismissRequest = { mergeDialogData = null }, onDismissRequest = { mergeDialogData = null },
title = { Text("合并阅读记录") }, title = { AppText("合并阅读记录") },
text = { text = {
Column { Column {
Text("将以下作者的“${targetRecord.bookName}”合并到 ${targetRecord.bookAuthor.ifBlank { "未知作者" }}") AppText("将以下作者的“${targetRecord.bookName}”合并到 ${targetRecord.bookAuthor.ifBlank { "未知作者" }}")
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))
candidates.forEach { candidate -> candidates.forEach { candidate ->
@@ -367,10 +365,10 @@ fun ReadRecordScreen(
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
Checkbox(checked = isChecked, onCheckedChange = null) Checkbox(checked = isChecked, onCheckedChange = null)
Text( AppText(
text = "$author(${formatDuring(candidate.readTime)})", text = "$author(${formatDuring(candidate.readTime)})",
modifier = Modifier.padding(start = 8.dp), modifier = Modifier.padding(start = 8.dp),
style = MaterialTheme.typography.bodyMedium style = LegadoTheme.typography.bodyMedium
) )
} }
} }
@@ -386,12 +384,12 @@ fun ReadRecordScreen(
mergeDialogData = null mergeDialogData = null
} }
) { ) {
Text("合并") AppText("合并")
} }
}, },
dismissButton = { dismissButton = {
TextButton(onClick = { mergeDialogData = null }) { TextButton(onClick = { mergeDialogData = null }) {
Text("取消") AppText("取消")
} }
} }
) )
@@ -686,21 +684,21 @@ fun LatestReadItem(
Spacer(modifier = Modifier.width(16.dp)) Spacer(modifier = Modifier.width(16.dp))
Column(modifier = Modifier.weight(1f)) { Column(modifier = Modifier.weight(1f)) {
Text( AppText(
text = record.bookName, text = record.bookName,
style = MaterialTheme.typography.titleMedium, style = LegadoTheme.typography.titleMedium,
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis overflow = TextOverflow.Ellipsis
) )
Text( AppText(
text = record.bookAuthor.ifBlank { "未知作者" }, text = record.bookAuthor.ifBlank { "未知作者" },
style = MaterialTheme.typography.bodySmall, style = LegadoTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.outline, color = MaterialTheme.colorScheme.outline,
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis overflow = TextOverflow.Ellipsis
) )
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))
Text( AppText(
modifier = Modifier modifier = Modifier
.basicMarquee( .basicMarquee(
iterations = Int.MAX_VALUE, iterations = Int.MAX_VALUE,
@@ -716,7 +714,7 @@ fun LatestReadItem(
append("${DateUtil.format(Date(record.lastRead), "yyyy-MM-dd HH:mm")}") append("${DateUtil.format(Date(record.lastRead), "yyyy-MM-dd HH:mm")}")
} }
}, },
style = MaterialTheme.typography.labelSmall, style = LegadoTheme.typography.labelSmall,
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis overflow = TextOverflow.Ellipsis
) )
@@ -783,9 +781,9 @@ fun TimelineSessionItem(
modifier = Modifier.width(48.dp), modifier = Modifier.width(48.dp),
verticalArrangement = Arrangement.Center verticalArrangement = Arrangement.Center
) { ) {
Text( AppText(
text = endTimeText, text = endTimeText,
style = MaterialTheme.typography.bodySmall style = LegadoTheme.typography.bodySmall
) )
} }
@@ -795,23 +793,23 @@ fun TimelineSessionItem(
Cover(coverPath) Cover(coverPath)
Spacer(modifier = Modifier.width(8.dp)) Spacer(modifier = Modifier.width(8.dp))
Column { Column {
Text( AppText(
text = session.bookName, text = session.bookName,
style = MaterialTheme.typography.titleMedium, style = LegadoTheme.typography.titleMedium,
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis overflow = TextOverflow.Ellipsis
) )
Text( AppText(
text = session.bookAuthor.ifBlank { "未知作者" }, text = session.bookAuthor.ifBlank { "未知作者" },
style = MaterialTheme.typography.bodySmall, style = LegadoTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.outline, color = MaterialTheme.colorScheme.outline,
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis overflow = TextOverflow.Ellipsis
) )
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))
Text( AppText(
text = chapterTitle.orEmpty(), text = chapterTitle.orEmpty(),
style = MaterialTheme.typography.labelSmall, style = LegadoTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.outline, color = MaterialTheme.colorScheme.outline,
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis overflow = TextOverflow.Ellipsis
@@ -847,21 +845,21 @@ fun ReadRecordItem(
Spacer(modifier = Modifier.width(16.dp)) Spacer(modifier = Modifier.width(16.dp))
Column(modifier = Modifier.weight(1f)) { Column(modifier = Modifier.weight(1f)) {
Text( AppText(
text = detail.bookName, text = detail.bookName,
style = MaterialTheme.typography.titleMedium, style = LegadoTheme.typography.titleMedium,
maxLines = 1 maxLines = 1
) )
Text( AppText(
text = detail.bookAuthor.ifBlank { "未知作者" }, text = detail.bookAuthor.ifBlank { "未知作者" },
style = MaterialTheme.typography.bodySmall, style = LegadoTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.outline, color = MaterialTheme.colorScheme.outline,
) )
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))
Text( AppText(
text = "阅读时长: ${formatDuring(detail.readTime)}", text = "阅读时长: ${formatDuring(detail.readTime)}",
color = MaterialTheme.colorScheme.outline, color = MaterialTheme.colorScheme.outline,
style = MaterialTheme.typography.labelSmall style = LegadoTheme.typography.labelSmall
) )
} }
} }
@@ -872,28 +870,27 @@ fun DateHeader(
date: String, date: String,
dailyTotalTime: Long? = null dailyTotalTime: Long? = null
) { ) {
val dateText = formatFriendlyDate(date) CollapsibleHeader(
SectionHeader( showIcon = false,
isCollapsed = false,
onToggle = { },
title = "",
titleContent = { titleContent = {
Column { val dateText = formatFriendlyDate(date)
Text( AppText(
text = dateText, text = dateText,
style = MaterialTheme.typography.bodyMedium, style = LegadoTheme.typography.bodyMedium,
fontWeight = FontWeight.Bold, fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.secondary color = LegadoTheme.colorScheme.primary
)
dailyTotalTime?.let { total ->
AppText(
text = "已读 ${formatDuring(total)}",
style = LegadoTheme.typography.bodySmall,
color = LegadoTheme.colorScheme.onSurface
) )
dailyTotalTime?.let { total ->
Text(
text = "已读 ${formatDuring(total)}",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurface
)
}
} }
}, }
detailContent = null,
horizontalArrangement = Arrangement.Start
) )
} }
@@ -915,14 +912,12 @@ fun ReadingSummaryCard(
val totalDurationMinutes = totalTimeMillis / 60000 val totalDurationMinutes = totalTimeMillis / 60000
Card( GlassCard(
onClick = onClick, onClick = onClick,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp), .padding(horizontal = 16.dp, vertical = 8.dp),
colors = CardDefaults.cardColors( containerColor = LegadoTheme.colorScheme.surfaceContainer
containerColor = MaterialTheme.colorScheme.surfaceContainer
)
) { ) {
Row( Row(
modifier = Modifier modifier = Modifier
@@ -934,28 +929,28 @@ fun ReadingSummaryCard(
Column(modifier = Modifier.weight(1f)) { Column(modifier = Modifier.weight(1f)) {
Text( AppText(
text = title, text = title,
style = MaterialTheme.typography.labelLarge, style = LegadoTheme.typography.labelLarge,
color = MaterialTheme.colorScheme.primary color = LegadoTheme.colorScheme.primary
) )
Spacer(modifier = Modifier.height(4.dp)) Spacer(modifier = Modifier.height(4.dp))
Row(verticalAlignment = Alignment.Bottom) { Row(verticalAlignment = Alignment.Bottom) {
Text( AppText(
text = "已读 ", text = "已读 ",
style = MaterialTheme.typography.titleMedium style = LegadoTheme.typography.titleMedium
) )
Text( AppText(
text = "$bookCount", text = "$bookCount",
style = MaterialTheme.typography.headlineMedium, style = LegadoTheme.typography.headlineMedium,
color = MaterialTheme.colorScheme.primary, color = LegadoTheme.colorScheme.primary,
fontWeight = FontWeight.Bold, fontWeight = FontWeight.Bold,
) )
Text( AppText(
text = " 本书", text = " 本书",
style = MaterialTheme.typography.titleMedium style = LegadoTheme.typography.titleMedium
) )
} }
@@ -965,10 +960,10 @@ fun ReadingSummaryCard(
val minutes = totalDurationMinutes % 60 val minutes = totalDurationMinutes % 60
val timeString = if (hours > 0) "${hours}小时${minutes}分钟" else "${minutes}分钟" val timeString = if (hours > 0) "${hours}小时${minutes}分钟" else "${minutes}分钟"
Text( AppText(
text = "共阅读 $timeString", text = "共阅读 $timeString",
style = MaterialTheme.typography.bodySmall, style = LegadoTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant color = LegadoTheme.colorScheme.onSurfaceVariant
) )
} }
@@ -44,7 +44,6 @@ import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.PlainTooltip import androidx.compose.material3.PlainTooltip
import androidx.compose.material3.Text
import androidx.compose.material3.TooltipAnchorPosition import androidx.compose.material3.TooltipAnchorPosition
import androidx.compose.material3.TooltipBox import androidx.compose.material3.TooltipBox
import androidx.compose.material3.TooltipDefaults import androidx.compose.material3.TooltipDefaults
@@ -64,11 +63,9 @@ import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import io.legado.app.data.entities.SearchContentHistory import io.legado.app.data.entities.SearchContentHistory
import io.legado.app.ui.widget.components.AnimatedText import io.legado.app.ui.theme.LegadoTheme
import io.legado.app.ui.widget.components.AppScaffold import io.legado.app.ui.widget.components.AppScaffold
import io.legado.app.ui.widget.components.EmptyMessageView import io.legado.app.ui.widget.components.EmptyMessageView
import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
import io.legado.app.ui.widget.components.SearchBarSection import io.legado.app.ui.widget.components.SearchBarSection
import io.legado.app.ui.widget.components.button.SmallAnimatedActionButton import io.legado.app.ui.widget.components.button.SmallAnimatedActionButton
import io.legado.app.ui.widget.components.button.SmallIconButton import io.legado.app.ui.widget.components.button.SmallIconButton
@@ -76,6 +73,9 @@ import io.legado.app.ui.widget.components.button.TopBarAnimatedActionButton
import io.legado.app.ui.widget.components.button.TopbarNavigationButton import io.legado.app.ui.widget.components.button.TopbarNavigationButton
import io.legado.app.ui.widget.components.card.TextCard import io.legado.app.ui.widget.components.card.TextCard
import io.legado.app.ui.widget.components.lazylist.FastScrollLazyColumn import io.legado.app.ui.widget.components.lazylist.FastScrollLazyColumn
import io.legado.app.ui.widget.components.text.AppText
import io.legado.app.ui.widget.components.topbar.GlassMediumFlexibleTopAppBar
import io.legado.app.ui.widget.components.topbar.GlassTopAppBarDefaults
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.koin.androidx.compose.koinViewModel import org.koin.androidx.compose.koinViewModel
@@ -140,12 +140,9 @@ fun SearchContentScreen(
topBar = { topBar = {
Column { Column {
GlassMediumFlexibleTopAppBar( GlassMediumFlexibleTopAppBar(
title = { title = if (searchQuery.isNotBlank() && searchResults.isNotEmpty()) {
val title = if (searchQuery.isNotBlank() && searchResults.isNotEmpty()) { "共 ${searchResults.size} 条结果"
"共 ${searchResults.size} 条结果" } else "搜索内容",
} else "搜索内容"
AnimatedText(text = title)
},
navigationIcon = { TopbarNavigationButton(onClick = onBack) }, navigationIcon = { TopbarNavigationButton(onClick = onBack) },
actions = { actions = {
Row( Row(
@@ -191,7 +188,7 @@ fun SearchContentScreen(
), ),
tooltip = { tooltip = {
PlainTooltip { PlainTooltip {
Text(if (isSearching) "停止搜索" else "跳转到当前章节") AppText(if (isSearching) "停止搜索" else "跳转到当前章节")
} }
}, },
state = rememberTooltipState(), state = rememberTooltipState(),
@@ -310,10 +307,10 @@ fun SearchHistoryList(
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp) .padding(horizontal = 16.dp, vertical = 8.dp)
) { ) {
Text( AppText(
text = "搜索历史", text = "搜索历史",
style = MaterialTheme.typography.titleSmallEmphasized, style = LegadoTheme.typography.titleSmallEmphasized,
color = MaterialTheme.colorScheme.primary, color = LegadoTheme.colorScheme.primary,
modifier = Modifier.align(Alignment.Center) modifier = Modifier.align(Alignment.Center)
) )
SmallAnimatedActionButton( SmallAnimatedActionButton(
@@ -342,7 +339,7 @@ fun SearchHistoryList(
.clickable { onHistoryClick(item) } .clickable { onHistoryClick(item) }
.animateItem(), .animateItem(),
headlineContent = { headlineContent = {
Text( AppText(
text = item.query, text = item.query,
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis overflow = TextOverflow.Ellipsis
@@ -378,7 +375,7 @@ fun SearchHistoryList(
modifier = Modifier.size(18.dp) modifier = Modifier.size(18.dp)
) )
Spacer(Modifier.width(8.dp)) Spacer(Modifier.width(8.dp))
Text("清除搜索历史") AppText("清除搜索历史")
} }
} }
} }
@@ -407,7 +404,7 @@ fun SearchResultItem(
Box(modifier = Modifier.padding(16.dp)) { Box(modifier = Modifier.padding(16.dp)) {
Column { Column {
Text( AppText(
text = buildAnnotatedString { text = buildAnnotatedString {
append( append(
result.getTitleSpannable( result.getTitleSpannable(
@@ -415,14 +412,14 @@ fun SearchResultItem(
) )
) )
}, },
style = MaterialTheme.typography.titleSmall style = LegadoTheme.typography.titleSmall
) )
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))
HorizontalDivider() HorizontalDivider()
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))
Text( AppText(
text = buildAnnotatedString { text = buildAnnotatedString {
append( append(
result.getContentSpannable( result.getContentSpannable(
@@ -432,7 +429,7 @@ fun SearchResultItem(
) )
) )
}, },
style = MaterialTheme.typography.bodyMedium style = LegadoTheme.typography.bodyMedium
) )
} }
@@ -63,7 +63,6 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.PrimaryScrollableTabRow import androidx.compose.material3.PrimaryScrollableTabRow
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Tab import androidx.compose.material3.Tab
import androidx.compose.material3.Text
import androidx.compose.material3.ToggleFloatingActionButton import androidx.compose.material3.ToggleFloatingActionButton
import androidx.compose.material3.ToggleFloatingActionButtonDefaults.animateIcon import androidx.compose.material3.ToggleFloatingActionButtonDefaults.animateIcon
import androidx.compose.material3.animateFloatingActionButton import androidx.compose.material3.animateFloatingActionButton
@@ -95,11 +94,11 @@ import io.legado.app.data.entities.Bookmark
import io.legado.app.help.book.isLocal import io.legado.app.help.book.isLocal
import io.legado.app.ui.book.toc.rule.TxtTocRuleActivity import io.legado.app.ui.book.toc.rule.TxtTocRuleActivity
import io.legado.app.ui.replace.ReplaceEditRoute import io.legado.app.ui.replace.ReplaceEditRoute
import io.legado.app.ui.theme.LegadoTheme
import io.legado.app.ui.widget.CollapsibleHeader import io.legado.app.ui.widget.CollapsibleHeader
import io.legado.app.ui.widget.components.ActionItem import io.legado.app.ui.widget.components.ActionItem
import io.legado.app.ui.widget.components.AppScaffold import io.legado.app.ui.widget.components.AppScaffold
import io.legado.app.ui.widget.components.EmptyMessageView import io.legado.app.ui.widget.components.EmptyMessageView
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
import io.legado.app.ui.widget.components.SelectionBottomBar import io.legado.app.ui.widget.components.SelectionBottomBar
import io.legado.app.ui.widget.components.bookmark.BookmarkEditSheet import io.legado.app.ui.widget.components.bookmark.BookmarkEditSheet
import io.legado.app.ui.widget.components.bookmark.BookmarkItem import io.legado.app.ui.widget.components.bookmark.BookmarkItem
@@ -109,7 +108,9 @@ import io.legado.app.ui.widget.components.divider.PillHeaderDivider
import io.legado.app.ui.widget.components.lazylist.FastScrollLazyColumn import io.legado.app.ui.widget.components.lazylist.FastScrollLazyColumn
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
import io.legado.app.ui.widget.components.text.AppText
import io.legado.app.ui.widget.components.topbar.DynamicTopAppBar import io.legado.app.ui.widget.components.topbar.DynamicTopAppBar
import io.legado.app.ui.widget.components.topbar.GlassTopAppBarDefaults
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.koin.androidx.compose.koinViewModel import org.koin.androidx.compose.koinViewModel
@@ -312,33 +313,33 @@ fun TocScreen(
when (pagerState.currentPage) { when (pagerState.currentPage) {
0 -> { 0 -> {
RoundDropdownMenuItem( RoundDropdownMenuItem(
text = { Text("使用替换规则") }, text = "使用替换规则",
trailingIcon = { trailingIcon = {
Checkbox(checked = useReplace, onCheckedChange = null) Checkbox(checked = useReplace, onCheckedChange = null)
}, },
onClick = { viewModel.toggleUseReplace() } onClick = { viewModel.toggleUseReplace() }
) )
RoundDropdownMenuItem( RoundDropdownMenuItem(
text = { Text("显示字数") }, text = "显示字数",
trailingIcon = { trailingIcon = {
Checkbox(checked = showWordCount, onCheckedChange = null) Checkbox(checked = showWordCount, onCheckedChange = null)
}, },
onClick = { viewModel.toggleShowWordCount() } onClick = { viewModel.toggleShowWordCount() }
) )
RoundDropdownMenuItem( RoundDropdownMenuItem(
text = { Text("反转目录") }, text = "反转目录",
onClick = { viewModel.reverseToc() } onClick = { viewModel.reverseToc() }
) )
PillDivider() PillDivider()
RoundDropdownMenuItem( RoundDropdownMenuItem(
text = { Text("替换规则") }, text = "替换规则",
onClick = { onClick = {
onOpenReplaceRule(null) onOpenReplaceRule(null)
dismiss() dismiss()
} }
) )
RoundDropdownMenuItem( RoundDropdownMenuItem(
text = { Text("新建替换规则") }, text = "新建替换规则",
onClick = { onClick = {
val scopes = mutableListOf<String>() val scopes = mutableListOf<String>()
book?.name?.let { scopes.add(it) } book?.name?.let { scopes.add(it) }
@@ -358,7 +359,7 @@ fun TocScreen(
if (book?.isLocal == true) { if (book?.isLocal == true) {
PillHeaderDivider(title = "本地书籍选项") PillHeaderDivider(title = "本地书籍选项")
RoundDropdownMenuItem( RoundDropdownMenuItem(
text = { Text("本地书籍目录规则") }, text = "本地书籍目录规则",
onClick = { onClick = {
val intent = val intent =
Intent(context, TxtTocRuleActivity::class.java).apply { Intent(context, TxtTocRuleActivity::class.java).apply {
@@ -369,7 +370,7 @@ fun TocScreen(
} }
) )
RoundDropdownMenuItem( RoundDropdownMenuItem(
text = { Text("拆分超长章节") }, text = "拆分超长章节",
trailingIcon = { trailingIcon = {
Checkbox( Checkbox(
checked = viewModel.isSplitLongChapter, checked = viewModel.isSplitLongChapter,
@@ -386,7 +387,7 @@ fun TocScreen(
else -> { else -> {
RoundDropdownMenuItem( RoundDropdownMenuItem(
text = { Text("导出书签为JSON") }, text = "导出书签为JSON",
onClick = { onClick = {
val dateFormat = SimpleDateFormat( val dateFormat = SimpleDateFormat(
"yyyyMMdd_HHmm", "yyyyMMdd_HHmm",
@@ -398,7 +399,7 @@ fun TocScreen(
} }
) )
RoundDropdownMenuItem( RoundDropdownMenuItem(
text = { Text("导出书签为MarkDown") }, text = "导出书签为MarkDown",
onClick = { onClick = {
val dateFormat = SimpleDateFormat( val dateFormat = SimpleDateFormat(
"yyyyMMdd_HHmm", "yyyyMMdd_HHmm",
@@ -428,7 +429,7 @@ fun TocScreen(
selected = pagerState.currentPage == 0, selected = pagerState.currentPage == 0,
onClick = { scope.launch { pagerState.animateScrollToPage(0) } }, onClick = { scope.launch { pagerState.animateScrollToPage(0) } },
text = { text = {
Text( AppText(
text = "目录", text = "目录",
modifier = Modifier.padding(horizontal = 16.dp), modifier = Modifier.padding(horizontal = 16.dp),
color = MaterialTheme.colorScheme.onSurfaceVariant color = MaterialTheme.colorScheme.onSurfaceVariant
@@ -439,7 +440,7 @@ fun TocScreen(
selected = pagerState.currentPage == 1, selected = pagerState.currentPage == 1,
onClick = { scope.launch { pagerState.animateScrollToPage(1) } }, onClick = { scope.launch { pagerState.animateScrollToPage(1) } },
text = { text = {
Text( AppText(
text = "书签", text = "书签",
modifier = Modifier.padding(horizontal = 16.dp), modifier = Modifier.padding(horizontal = 16.dp),
color = MaterialTheme.colorScheme.onSurfaceVariant color = MaterialTheme.colorScheme.onSurfaceVariant
@@ -464,13 +465,13 @@ fun TocScreen(
onDismissRequest = { showVolumeMenu = false } onDismissRequest = { showVolumeMenu = false }
) { ) {
RoundDropdownMenuItem( RoundDropdownMenuItem(
text = { Text("展开所有卷") }, text = "展开所有卷",
onClick = { onClick = {
viewModel.expandAllVolumes(); showVolumeMenu = false viewModel.expandAllVolumes(); showVolumeMenu = false
} }
) )
RoundDropdownMenuItem( RoundDropdownMenuItem(
text = { Text("收起所有卷") }, text = "收起所有卷",
onClick = { onClick = {
viewModel.collapseAllVolumes(); showVolumeMenu = false viewModel.collapseAllVolumes(); showVolumeMenu = false
} }
@@ -482,7 +483,7 @@ fun TocScreen(
PillHeaderDivider(title = "快速跳转") PillHeaderDivider(title = "快速跳转")
volumeItems.forEach { uiItem -> volumeItems.forEach { uiItem ->
RoundDropdownMenuItem( RoundDropdownMenuItem(
text = { Text(uiItem.title) }, text = uiItem.title,
onClick = { onClick = {
scope.launch { scope.launch {
val targetIndex = val targetIndex =
@@ -541,7 +542,7 @@ fun TocScreen(
fabMenuExpanded = false fabMenuExpanded = false
}, },
icon = { Icon(icon, contentDescription = null) }, icon = { Icon(icon, contentDescription = null) },
text = { Text(text = label) } text = { AppText(text = label) }
) )
} }
} }
@@ -593,20 +594,19 @@ fun TocScreen(
} }
} }
if (editingBookmark != null) { BookmarkEditSheet(
BookmarkEditSheet( show = editingBookmark != null,
bookmark = editingBookmark!!, bookmark = editingBookmark!!,
onDismiss = { editingBookmark = null }, onDismiss = { editingBookmark = null },
onSave = { updatedBookmark -> onSave = { updatedBookmark ->
viewModel.updateBookmark(updatedBookmark) viewModel.updateBookmark(updatedBookmark)
editingBookmark = null editingBookmark = null
}, },
onDelete = { bookmarkToDelete -> onDelete = { bookmarkToDelete ->
viewModel.deleteBookmark(bookmarkToDelete) viewModel.deleteBookmark(bookmarkToDelete)
editingBookmark = null editingBookmark = null
} }
) )
}
} }
} }
<