From c1f7faf050c8f81f4765dd67a44e4275a2b3a90e Mon Sep 17 00:00:00 2001 From: HapeLee <63206378+HapeLee@users.noreply.github.com> Date: Wed, 13 May 2026 01:58:31 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E9=98=85=E8=AF=BB=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=E6=80=BB=E8=A7=88=EF=BC=88=E5=9C=A8=E9=98=85=E8=AF=BB?= =?UTF-8?q?=E8=AE=B0=E5=BD=95=E7=9A=84=E6=88=90=E5=B0=B1=E5=8D=A1=E7=89=87?= =?UTF-8?q?=E8=BF=9B=E5=85=A5=EF=BC=8C=E7=A8=B3=E5=AE=9A=E5=90=8E=E4=BC=9A?= =?UTF-8?q?=E5=8A=A0=E5=85=A5=E4=B8=BB=E9=A1=B5=E9=9D=A2=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../GetReadRecordOverviewUseCase.kt | 12 +++++ .../readRecord/ReadRecordOverviewScreen.kt | 30 +++++++++---- .../ui/book/readRecord/ReadRecordScreen.kt | 41 ++++++++++++----- .../readRecord/component/ReadRecordCharts.kt | 44 ++++++++++++------- .../component/ReadRecordListItems.kt | 23 ++++++++-- .../readRecord/component/ReadRecordSummary.kt | 8 ++-- .../widget/components/cover/CoilBookCover.kt | 2 + 7 files changed, 119 insertions(+), 41 deletions(-) diff --git a/app/src/main/java/io/legado/app/domain/usecase/readRecord/GetReadRecordOverviewUseCase.kt b/app/src/main/java/io/legado/app/domain/usecase/readRecord/GetReadRecordOverviewUseCase.kt index 38887fc5a..4de750ef4 100644 --- a/app/src/main/java/io/legado/app/domain/usecase/readRecord/GetReadRecordOverviewUseCase.kt +++ b/app/src/main/java/io/legado/app/domain/usecase/readRecord/GetReadRecordOverviewUseCase.kt @@ -69,6 +69,18 @@ class GetReadRecordOverviewUseCase { val dailyTimeData = if (period == ReadPeriod.ALL) { emptyList() + } else if (period == ReadPeriod.YEAR) { + val dateToTime = filteredDetails.groupBy { + LocalDate.parse(it.date, DateTimeFormatter.ISO_LOCAL_DATE).with(TemporalAdjusters.firstDayOfMonth()) + }.mapValues { it.value.sumOf { d -> d.readTime } } + + val monthList = mutableListOf>() + var curr = startDate.with(TemporalAdjusters.firstDayOfMonth()) + while (!curr.isAfter(endDate)) { + monthList.add(curr to (dateToTime[curr] ?: 0L)) + curr = curr.plusMonths(1) + } + monthList } else { val dateToTime = filteredDetails.groupBy { it.date } .mapValues { it.value.sumOf { d -> d.readTime } } diff --git a/app/src/main/java/io/legado/app/ui/book/readRecord/ReadRecordOverviewScreen.kt b/app/src/main/java/io/legado/app/ui/book/readRecord/ReadRecordOverviewScreen.kt index 35851c44f..4a32327ac 100644 --- a/app/src/main/java/io/legado/app/ui/book/readRecord/ReadRecordOverviewScreen.kt +++ b/app/src/main/java/io/legado/app/ui/book/readRecord/ReadRecordOverviewScreen.kt @@ -29,7 +29,7 @@ import io.legado.app.ui.theme.adaptiveHorizontalPadding import io.legado.app.ui.widget.components.AppScaffold import io.legado.app.ui.widget.components.button.MediumIconButton 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.CoilBookCover import io.legado.app.ui.widget.components.heatmap.HeatmapMode import io.legado.app.ui.widget.components.text.AppText import io.legado.app.ui.widget.components.topbar.GlassMediumFlexibleTopAppBar @@ -223,8 +223,12 @@ fun TopReadingListCard( .fillMaxWidth() .adaptiveHorizontalPadding(vertical = 8.dp) ) { - Column(modifier = Modifier.padding(16.dp)) { - AppText("阅读时长榜", style = LegadoTheme.typography.titleMedium) + Column(modifier = Modifier.padding(vertical = 16.dp)) { + AppText( + modifier = Modifier.padding(horizontal = 16.dp), + text = "阅读时长榜", + style = LegadoTheme.typography.titleMedium + ) Spacer(modifier = Modifier.height(8.dp)) topBooks.forEachIndexed { index, book -> @@ -237,7 +241,7 @@ fun TopReadingListCard( modifier = Modifier .fillMaxWidth() .clickable { onBookClick(book.bookName, book.bookAuthor) } - .padding(vertical = 8.dp), + .padding(vertical = 8.dp, horizontal = 8.dp), verticalAlignment = Alignment.CenterVertically ) { AppText( @@ -247,7 +251,12 @@ fun TopReadingListCard( textAlign = TextAlign.Center, color = if (index < 3) LegadoTheme.colorScheme.primary else LegadoTheme.colorScheme.onSurfaceVariant ) - Cover(path = coverPath, modifier = Modifier.size(40.dp, 56.dp)) + CoilBookCover( + name = book.bookName, + author = book.bookAuthor, + path = coverPath, + modifier = Modifier.width(40.dp) + ) Spacer(modifier = Modifier.width(12.dp)) Column(modifier = Modifier.weight(1f)) { AppText( @@ -264,6 +273,7 @@ fun TopReadingListCard( ) } AppText( + modifier = Modifier.padding(end = 8.dp), text = ReadRecordFormatter.formatDuration(book.readTime), style = LegadoTheme.typography.bodySmall, color = LegadoTheme.colorScheme.primary @@ -355,10 +365,14 @@ fun CalendarDayCell( .background(LegadoTheme.colorScheme.surfaceVariant), contentAlignment = Alignment.Center ) { - if (coverPath != null) { - Cover( + if (topBook != null) { + CoilBookCover( + name = topBook.first, + author = topBook.second, path = coverPath, - modifier = Modifier.fillMaxSize().alpha(0.4f) + modifier = Modifier + .fillMaxSize() + .alpha(0.4f) ) } diff --git a/app/src/main/java/io/legado/app/ui/book/readRecord/ReadRecordScreen.kt b/app/src/main/java/io/legado/app/ui/book/readRecord/ReadRecordScreen.kt index 4edb98624..815e24810 100644 --- a/app/src/main/java/io/legado/app/ui/book/readRecord/ReadRecordScreen.kt +++ b/app/src/main/java/io/legado/app/ui/book/readRecord/ReadRecordScreen.kt @@ -83,7 +83,7 @@ import io.legado.app.ui.widget.components.topbar.TopBarNavigationButton import io.legado.app.ui.widget.components.card.GlassCard import io.legado.app.ui.widget.components.card.TextCard import io.legado.app.ui.widget.components.checkBox.CheckboxItem -import io.legado.app.ui.widget.components.cover.Cover +import io.legado.app.ui.widget.components.cover.CoilBookCover import io.legado.app.ui.widget.components.heatmap.HEATMAP_CALENDAR_TITLE import io.legado.app.ui.widget.components.heatmap.HeatmapCalendarEndAction import io.legado.app.ui.widget.components.heatmap.HeatmapCalendarStartAction @@ -728,7 +728,12 @@ fun LatestReadItem( .adaptiveHorizontalPadding(vertical = 8.dp), verticalAlignment = Alignment.CenterVertically ) { - Cover(coverPath) + CoilBookCover( + name = record.bookName, + author = record.bookAuthor, + path = coverPath, + modifier = Modifier.width(44.dp) + ) Spacer(modifier = Modifier.width(16.dp)) @@ -839,7 +844,12 @@ fun TimelineSessionItem( Row( verticalAlignment = Alignment.CenterVertically ) { - Cover(coverPath) + CoilBookCover( + name = session.bookName, + author = session.bookAuthor, + path = coverPath, + modifier = Modifier.width(44.dp) + ) Spacer(modifier = Modifier.width(8.dp)) Column { AppText( @@ -889,7 +899,12 @@ fun ReadRecordItem( .adaptiveHorizontalPadding(vertical = 8.dp), verticalAlignment = Alignment.CenterVertically ) { - Cover(coverPath) + CoilBookCover( + name = detail.bookName, + author = detail.bookAuthor, + path = coverPath, + modifier = Modifier.width(44.dp) + ) Spacer(modifier = Modifier.width(16.dp)) @@ -1017,24 +1032,25 @@ fun ReadingSummaryCard( } if (bookNamesForCover.isNotEmpty()) { - BookStackView(coverPaths = coverPaths) + val combined = bookNamesForCover.zip(coverPaths) + BookStackView(books = combined) } } } } @Composable -fun BookStackView(coverPaths: List) { +fun BookStackView(books: List, String?>>) { val xOffsetStep = 12.dp - val stackWidth = 48.dp + (xOffsetStep * (coverPaths.size - 1).coerceAtLeast(0)) + val stackWidth = 44.dp + (xOffsetStep * (books.size - 1).coerceAtLeast(0)) Box( modifier = Modifier .width(stackWidth) - .height(72.dp), + .height(64.dp), contentAlignment = Alignment.CenterStart ) { - coverPaths.forEachIndexed { index, path -> + books.forEachIndexed { index, (info, path) -> Box( modifier = Modifier .padding(start = xOffsetStep * index) @@ -1046,7 +1062,12 @@ fun BookStackView(coverPaths: List) { shape = RoundedCornerShape(4.dp), color = Color.Transparent ) { - Cover(path = path) + CoilBookCover( + name = info.first, + author = info.second, + path = path, + modifier = Modifier.width(44.dp) + ) } } } diff --git a/app/src/main/java/io/legado/app/ui/book/readRecord/component/ReadRecordCharts.kt b/app/src/main/java/io/legado/app/ui/book/readRecord/component/ReadRecordCharts.kt index 3d3aff51f..5fedce8a8 100644 --- a/app/src/main/java/io/legado/app/ui/book/readRecord/component/ReadRecordCharts.kt +++ b/app/src/main/java/io/legado/app/ui/book/readRecord/component/ReadRecordCharts.kt @@ -14,7 +14,6 @@ import io.legado.app.ui.theme.LegadoTheme import io.legado.app.ui.theme.adaptiveHorizontalPadding import io.legado.app.ui.widget.components.card.GlassCard import io.legado.app.ui.widget.components.text.AppText -import io.legado.app.utils.formatReadDuration import java.time.LocalDate @Composable @@ -48,7 +47,7 @@ fun ReadingTimeBarChartCard( horizontalAlignment = Alignment.End ) { AppText( - text = formatReadDuration(maxTime), + text = formatChartDuration(maxTime), style = LegadoTheme.typography.labelSmall, fontSize = 8.sp, color = LegadoTheme.colorScheme.onSurfaceVariant @@ -76,16 +75,16 @@ fun ReadingTimeBarChartCard( val showLabel = when (period) { ReadPeriod.DAY -> true - ReadPeriod.WEEK -> date.dayOfWeek.value in listOf(2, 4, 6) - ReadPeriod.MONTH -> date.dayOfMonth == 1 || date.dayOfMonth % 7 == 0 || index == data.lastIndex - ReadPeriod.YEAR -> date.dayOfMonth == 1 + ReadPeriod.WEEK -> true + ReadPeriod.MONTH -> date.dayOfMonth == 1 || date.dayOfMonth == 15 || index == data.lastIndex + ReadPeriod.YEAR -> true else -> false } val labelText = when (period) { ReadPeriod.YEAR -> "${date.monthValue}月" ReadPeriod.WEEK -> when (date.dayOfWeek.value) { - 2 -> "二"; 4 -> "四"; 6 -> "六"; else -> "" + 1 -> "一"; 2 -> "二"; 3 -> "三"; 4 -> "四"; 5 -> "五"; 6 -> "六"; 7 -> "日"; else -> "" } else -> date.dayOfMonth.toString() } @@ -94,20 +93,26 @@ fun ReadingTimeBarChartCard( modifier = Modifier .weight(1f) .fillMaxHeight(), - verticalArrangement = Arrangement.Bottom, horizontalAlignment = Alignment.CenterHorizontally ) { Box( modifier = Modifier - .fillMaxWidth(0.6f) - .fillMaxHeight(heightFactor.coerceAtLeast(0.01f)) - .padding(bottom = 4.dp) - .clip(RoundedCornerShape(topStart = 4.dp, topEnd = 4.dp)) - .background( - if (time > 0) LegadoTheme.colorScheme.primary - else LegadoTheme.colorScheme.surfaceVariant.copy(alpha = 0.3f) - ) - ) + .weight(1f) + .fillMaxWidth(), + contentAlignment = Alignment.BottomCenter + ) { + Box( + modifier = Modifier + .fillMaxWidth(if (period == ReadPeriod.MONTH) 0.8f else 0.6f) + .fillMaxHeight(heightFactor.coerceAtLeast(0.01f)) + .padding(horizontal = 1.dp) + .clip(RoundedCornerShape(topStart = 4.dp, topEnd = 4.dp)) + .background( + if (time > 0) LegadoTheme.colorScheme.primary + else LegadoTheme.colorScheme.surfaceVariant.copy(alpha = 0.3f) + ) + ) + } Box(modifier = Modifier.height(20.dp), contentAlignment = Alignment.TopCenter) { if (showLabel) { @@ -126,3 +131,10 @@ fun ReadingTimeBarChartCard( } } } + +private fun formatChartDuration(millis: Long): String { + val totalMinutes = millis / (1000 * 60) + val hours = totalMinutes / 60 + val minutes = totalMinutes % 60 + return if (hours > 0) "${hours}h${if (minutes > 0) "${minutes}m" else ""}" else "${minutes}m" +} diff --git a/app/src/main/java/io/legado/app/ui/book/readRecord/component/ReadRecordListItems.kt b/app/src/main/java/io/legado/app/ui/book/readRecord/component/ReadRecordListItems.kt index d74034d74..9dbd6b272 100644 --- a/app/src/main/java/io/legado/app/ui/book/readRecord/component/ReadRecordListItems.kt +++ b/app/src/main/java/io/legado/app/ui/book/readRecord/component/ReadRecordListItems.kt @@ -16,7 +16,7 @@ import io.legado.app.ui.book.readRecord.ReadRecordFormatter import io.legado.app.ui.book.readRecord.ReadRecordViewModel import io.legado.app.ui.book.readRecord.TimelineItem import io.legado.app.ui.theme.LegadoTheme -import io.legado.app.ui.widget.components.cover.Cover +import io.legado.app.ui.widget.components.cover.CoilBookCover import io.legado.app.ui.widget.components.text.AppText import io.legado.app.utils.StringUtils.formatFriendlyDate @@ -39,7 +39,12 @@ fun LatestReadItem( .padding(16.dp), verticalAlignment = Alignment.CenterVertically ) { - Cover(path = coverPath, modifier = Modifier.size(48.dp, 64.dp)) + CoilBookCover( + name = record.bookName, + author = record.bookAuthor, + path = coverPath, + modifier = Modifier.size(48.dp, 64.dp) + ) Spacer(modifier = Modifier.width(16.dp)) Column(modifier = Modifier.weight(1f)) { AppText( @@ -89,7 +94,12 @@ fun TimelineSessionItem( .padding(horizontal = 16.dp, vertical = 12.dp), verticalAlignment = Alignment.CenterVertically ) { - Cover(path = coverPath, modifier = Modifier.size(40.dp, 56.dp)) + CoilBookCover( + name = session.bookName, + author = session.bookAuthor, + path = coverPath, + modifier = Modifier.size(40.dp, 56.dp) + ) Spacer(modifier = Modifier.width(12.dp)) Column(modifier = Modifier.weight(1f)) { AppText( @@ -131,7 +141,12 @@ fun ReadRecordItem( .padding(16.dp), verticalAlignment = Alignment.CenterVertically ) { - Cover(path = coverPath, modifier = Modifier.size(40.dp, 56.dp)) + CoilBookCover( + name = detail.bookName, + author = detail.bookAuthor, + path = coverPath, + modifier = Modifier.size(40.dp, 56.dp) + ) Spacer(modifier = Modifier.width(16.dp)) Column(modifier = Modifier.weight(1f)) { AppText( diff --git a/app/src/main/java/io/legado/app/ui/book/readRecord/component/ReadRecordSummary.kt b/app/src/main/java/io/legado/app/ui/book/readRecord/component/ReadRecordSummary.kt index df5696b33..c0554a42d 100644 --- a/app/src/main/java/io/legado/app/ui/book/readRecord/component/ReadRecordSummary.kt +++ b/app/src/main/java/io/legado/app/ui/book/readRecord/component/ReadRecordSummary.kt @@ -12,7 +12,7 @@ import io.legado.app.ui.book.readRecord.ReadRecordViewModel import io.legado.app.ui.theme.LegadoTheme import io.legado.app.ui.theme.adaptiveHorizontalPadding 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.CoilBookCover import io.legado.app.ui.widget.components.text.AppText import java.time.format.DateTimeFormatter @@ -107,11 +107,13 @@ fun BookStackView( coverPath = viewModel.getBookCover(name, author) } - Cover( + CoilBookCover( + name = name, + author = author, path = coverPath, modifier = Modifier .padding(end = (index * 12).dp) - .size(32.dp, 44.dp) + .width(32.dp) ) } } diff --git a/app/src/main/java/io/legado/app/ui/widget/components/cover/CoilBookCover.kt b/app/src/main/java/io/legado/app/ui/widget/components/cover/CoilBookCover.kt index 0e723690b..67454d78f 100644 --- a/app/src/main/java/io/legado/app/ui/widget/components/cover/CoilBookCover.kt +++ b/app/src/main/java/io/legado/app/ui/widget/components/cover/CoilBookCover.kt @@ -13,6 +13,7 @@ import androidx.compose.foundation.Canvas import androidx.compose.foundation.background import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width @@ -78,6 +79,7 @@ fun CoilBookCover( Box( modifier = modifier + .aspectRatio(5f / 7f) .then( if (CoverConfig.coverShowShadow) { Modifier.shadow(4.dp, RoundedCornerShape(4.dp))