diff --git a/app/src/main/java/io/legado/app/data/dao/BookChapterDao.kt b/app/src/main/java/io/legado/app/data/dao/BookChapterDao.kt index 87336219b..1454353d2 100644 --- a/app/src/main/java/io/legado/app/data/dao/BookChapterDao.kt +++ b/app/src/main/java/io/legado/app/data/dao/BookChapterDao.kt @@ -43,4 +43,13 @@ interface BookChapterDao { @Query("update chapters set wordCount = :wordCount where bookUrl = :bookUrl and url = :url") fun upWordCount(bookUrl: String, url: String, wordCount: String) + /** + * 根据书籍的唯一标识 bookUrl 和章节索引 index 查找章节标题。 + */ + @Query(""" + SELECT title FROM chapters + WHERE bookUrl = :bookUrl + AND `index` = :chapterIndex + """) + suspend fun getChapterTitleByUrlAndIndex(bookUrl: String, chapterIndex: Int): String? } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/di/readRecordModule.kt b/app/src/main/java/io/legado/app/di/readRecordModule.kt index 3182aa405..0422ec300 100644 --- a/app/src/main/java/io/legado/app/di/readRecordModule.kt +++ b/app/src/main/java/io/legado/app/di/readRecordModule.kt @@ -9,7 +9,8 @@ import org.koin.dsl.module val readRecordModule = module { single { get().readRecordDao } single { get().bookDao } + single { get().bookChapterDao } single { ReadRecordRepository(get()) } - viewModel { ReadRecordViewModel(get(), get()) } + viewModel { ReadRecordViewModel(get(), get(), get()) } } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/model/ReadBook.kt b/app/src/main/java/io/legado/app/model/ReadBook.kt index 324b5f9b0..3852f9e69 100644 --- a/app/src/main/java/io/legado/app/model/ReadBook.kt +++ b/app/src/main/java/io/legado/app/model/ReadBook.kt @@ -312,7 +312,7 @@ object ReadBook : CoroutineScope by MainScope(), KoinComponent { bookName = currentBookName, startTime = readStartTime, endTime = readStartTime, - words = 0 + words = durChapterIndex.toLong() ) } } @@ -327,12 +327,9 @@ object ReadBook : CoroutineScope by MainScope(), KoinComponent { return } - var wordChange = currentLength - lastReadLength - if (wordChange < 0) wordChange = 0 - currentActiveSession = currentActiveSession!!.copy( endTime = endTime, - words = currentActiveSession!!.words + wordChange + words = durChapterIndex.toLong() ) readStartTime = endTime diff --git a/app/src/main/java/io/legado/app/model/ReadManga.kt b/app/src/main/java/io/legado/app/model/ReadManga.kt index 09ac21d67..a4c29c940 100644 --- a/app/src/main/java/io/legado/app/model/ReadManga.kt +++ b/app/src/main/java/io/legado/app/model/ReadManga.kt @@ -158,7 +158,7 @@ object ReadManga : CoroutineScope by MainScope() , KoinComponent{ bookName = currentBookName, startTime = readStartTime, endTime = readStartTime, - words = 0 + words = durChapterIndex.toLong() ) } } @@ -173,12 +173,9 @@ object ReadManga : CoroutineScope by MainScope() , KoinComponent{ return } - var wordChange = currentLength - lastReadLength - if (wordChange < 0) wordChange = 0 - currentActiveSession = currentActiveSession!!.copy( endTime = endTime, - words = currentActiveSession!!.words + wordChange + words = durChapterIndex.toLong() ) readStartTime = endTime diff --git a/app/src/main/java/io/legado/app/ui/book/explore/ExploreShowScreen.kt b/app/src/main/java/io/legado/app/ui/book/explore/ExploreShowScreen.kt index e1f5596ff..4332078ef 100644 --- a/app/src/main/java/io/legado/app/ui/book/explore/ExploreShowScreen.kt +++ b/app/src/main/java/io/legado/app/ui/book/explore/ExploreShowScreen.kt @@ -337,7 +337,6 @@ fun ExploreBookItem( Text( text = book.author, style = MaterialTheme.typography.bodySmall, - color = Color.Gray, maxLines = 1 ) @@ -365,7 +364,7 @@ fun ExploreBookItem( Text( text = intro, style = MaterialTheme.typography.bodySmall, - color = Color.DarkGray, + color = Color.Gray, maxLines = 2, overflow = TextOverflow.Ellipsis ) 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 89cbe0e1e..a100ef01b 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 @@ -52,6 +52,7 @@ import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.zIndex import cn.hutool.core.date.DateUtil @@ -272,8 +273,7 @@ fun ReadRecordScreen( } DisplayMode.TIMELINE -> { state.timelineRecords.forEach { (date, sessions) -> - val dailyTotalTime = sessions.sumOf { it.endTime - it.startTime } - stickyHeader { DateHeader(date, dailyTotalTime) } + stickyHeader { DateHeader(date) } val timelineItems = sessions.mapIndexed { index, session -> val showHeader = true @@ -360,14 +360,15 @@ fun TimelineSessionItem( ) { val session = item.session var coverPath by remember { mutableStateOf(null) } + var chapterTitle by remember { mutableStateOf("加载中...") } LaunchedEffect(session.bookName) { coverPath = viewModel.getBookCover(session.bookName) + val title = viewModel.getChapterTitle(session.bookName, session.words) + chapterTitle = title ?: "第 ${session.words} 章" } - val startTimeText = DateUtil.format(Date(session.startTime), "HH:mm") val endTimeText = DateUtil.format(Date(session.endTime), "HH:mm") - val duration = session.endTime - session.startTime val nodeRadius = 4.dp val lineWidth = 2.dp @@ -380,7 +381,6 @@ fun TimelineSessionItem( Box( modifier = Modifier .fillMaxWidth() - .padding(horizontal = 0.dp) .clickable { onBookClick(session.bookName) } .drawBehind { val x = timelineX.toPx() @@ -399,7 +399,6 @@ fun TimelineSessionItem( radius = nodeRadius.toPx(), center = Offset(x, cy) ) - } ) { Row( @@ -418,28 +417,26 @@ fun TimelineSessionItem( ) } - Column(modifier = Modifier.weight(1f)) { - if (item.showHeader) { - Row(verticalAlignment = Alignment.CenterVertically) { - Cover(coverPath) - Spacer(modifier = Modifier.width(8.dp)) - Text( - text = session.bookName, - style = MaterialTheme.typography.titleMedium, - maxLines = 2 - ) - } - Spacer(modifier = Modifier.height(6.dp)) + Row( + verticalAlignment = Alignment.CenterVertically + ) { + Cover(coverPath) + Spacer(modifier = Modifier.width(8.dp)) + Column { Text( - "时长: ${formatDuring(duration)}", + text = session.bookName, + style = MaterialTheme.typography.titleMedium, + maxLines = 2, + minLines = 2, + overflow = TextOverflow.Ellipsis + ) + Text( + text = chapterTitle.orEmpty(), style = MaterialTheme.typography.bodySmall, - color = Color.Gray + color = Color.Gray, + maxLines = 1, + overflow = TextOverflow.Ellipsis ) - // Text( - // "字数: ${session.words}", - // style = MaterialTheme.typography.bodySmall, - // color = MaterialTheme.colorScheme.onSurfaceVariant - // ) } } } @@ -494,10 +491,9 @@ fun ReadRecordItem( @Composable fun DateHeader( date: String, - dailyTotalTime: Long + dailyTotalTime: Long? = null ) { val dateText = formatFriendlyDate(date) - val totalTimeText = "已读 ${formatDuring(dailyTotalTime)}" Surface( modifier = Modifier.fillMaxWidth() ) { @@ -509,11 +505,13 @@ fun DateHeader( color = MaterialTheme.colorScheme.secondary ) - Text( - text = totalTimeText, - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.onSurface - ) + dailyTotalTime?.let { total -> + Text( + text = "已读 ${formatDuring(total)}", + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurface + ) + } } } } diff --git a/app/src/main/java/io/legado/app/ui/book/readRecord/ReadRecordViewModel.kt b/app/src/main/java/io/legado/app/ui/book/readRecord/ReadRecordViewModel.kt index 7c576bb6d..65febe075 100644 --- a/app/src/main/java/io/legado/app/ui/book/readRecord/ReadRecordViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/readRecord/ReadRecordViewModel.kt @@ -2,6 +2,7 @@ package io.legado.app.ui.book.readRecord import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import io.legado.app.data.dao.BookChapterDao import io.legado.app.data.dao.BookDao import io.legado.app.data.entities.readRecord.ReadRecord import io.legado.app.data.entities.readRecord.ReadRecordDetail @@ -36,7 +37,8 @@ enum class DisplayMode { class ReadRecordViewModel( private val repository: ReadRecordRepository, - private val bookDao: BookDao + private val bookDao: BookDao, + private val bookChapterDao: BookChapterDao ) : ViewModel() { private val _displayMode = MutableStateFlow(DisplayMode.AGGREGATE) val displayMode = _displayMode.asStateFlow() @@ -149,6 +151,25 @@ class ReadRecordViewModel( return mergedList } + suspend fun getChapterTitle( + bookName: String, + chapterIndexLong: Long + ): String? { + val chapterIndex = chapterIndexLong.toInt() + val book = withContext(Dispatchers.IO) { + bookDao.findByName(bookName).firstOrNull() + } + + val bookUrl = book?.bookUrl + if (bookUrl.isNullOrEmpty()) { + return null + } + + return withContext(Dispatchers.IO) { + bookChapterDao.getChapterTitleByUrlAndIndex(bookUrl, chapterIndex) + } + } + fun deleteDetail(detail: ReadRecordDetail) { viewModelScope.launch { repository.deleteDetail(detail)