Parcourir la source

[新增] 阅读记录时间线视图现在可以显示一次记录的最后阅读章节

HapeLee il y a 11 mois
Parent
commit
ec90fc0121

+ 9 - 0
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?
 }

+ 2 - 1
app/src/main/java/io/legado/app/di/readRecordModule.kt

@@ -9,7 +9,8 @@ import org.koin.dsl.module
 val readRecordModule = module {
     single { get<AppDatabase>().readRecordDao }
     single { get<AppDatabase>().bookDao }
+    single { get<AppDatabase>().bookChapterDao }
     single { ReadRecordRepository(get()) }
 
-    viewModel { ReadRecordViewModel(get(), get()) }
+    viewModel { ReadRecordViewModel(get(), get(), get()) }
 }

+ 2 - 5
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

+ 2 - 5
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

+ 1 - 2
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
                 )

+ 30 - 32
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<String?>(null) }
+    var chapterTitle by remember { mutableStateOf<String?>("加载中...") }
 
     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
+                )
+            }
         }
     }
 }

+ 22 - 1
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)