HapeLee 5 месяцев назад
Родитель
Сommit
f4ca57f972
39 измененных файлов с 137 добавлено и 1009 удалено
  1. 4 4
      app/src/main/assets/defaultData/coverRule.json
  2. 1 42
      app/src/main/assets/defaultData/rssSources.json
  3. 1 1
      app/src/main/java/io/legado/app/di/appModule.kt
  4. 5 5
      app/src/main/java/io/legado/app/domain/usecase/ExploreKindUiUseCase.kt
  5. 1 1
      app/src/main/java/io/legado/app/ui/book/explore/ExploreShowScreen.kt
  6. 1 1
      app/src/main/java/io/legado/app/ui/book/group/GroupEditSheet.kt
  7. 2 2
      app/src/main/java/io/legado/app/ui/book/info/BookInfoScreen.kt
  8. 1 1
      app/src/main/java/io/legado/app/ui/book/info/BookInfoSheets.kt
  9. 1 1
      app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt
  10. 1 1
      app/src/main/java/io/legado/app/ui/book/info/edit/BookInfoEditScreen.kt
  11. 1 1
      app/src/main/java/io/legado/app/ui/book/manage/BookshelfManageScreen.kt
  12. 1 1
      app/src/main/java/io/legado/app/ui/book/readRecord/ReadRecordOverviewScreen.kt
  13. 2 2
      app/src/main/java/io/legado/app/ui/book/readRecord/ReadRecordScreen.kt
  14. 15 3
      app/src/main/java/io/legado/app/ui/book/readRecord/component/ReadRecordListItems.kt
  15. 15 4
      app/src/main/java/io/legado/app/ui/book/readRecord/component/ReadRecordSummary.kt
  16. 19 29
      app/src/main/java/io/legado/app/ui/book/search/SearchScreen.kt
  17. 1 1
      app/src/main/java/io/legado/app/ui/book/toc/TocScreen.kt
  18. 0 328
      app/src/main/java/io/legado/app/ui/book/toc/rule/TxtTocRuleDialog.kt
  19. 0 147
      app/src/main/java/io/legado/app/ui/book/toc/rule/TxtTocRuleEditDialog.kt
  20. 0 66
      app/src/main/java/io/legado/app/ui/config/CoverRuleConfigDialog.kt
  21. 0 128
      app/src/main/java/io/legado/app/ui/config/DirectLinkUploadConfig.kt
  22. 27 64
      app/src/main/java/io/legado/app/ui/config/coverConfig/CoverRuleConfigSheet.kt
  23. 5 5
      app/src/main/java/io/legado/app/ui/main/bookshelf/BookItem.kt
  24. 3 6
      app/src/main/java/io/legado/app/ui/main/explore/ExploreScreen.kt
  25. 1 1
      app/src/main/java/io/legado/app/ui/main/explore/ExploreViewModel.kt
  26. 1 1
      app/src/main/java/io/legado/app/ui/main/rss/RssScreen.kt
  27. 1 1
      app/src/main/java/io/legado/app/ui/rss/article/RssArticlesCompose.kt
  28. 1 1
      app/src/main/java/io/legado/app/ui/rss/favorites/RssFavoritesScreen.kt
  29. 1 2
      app/src/main/java/io/legado/app/ui/widget/components/CollapsibleHeader.kt
  30. 0 40
      app/src/main/java/io/legado/app/ui/widget/components/SectionHeader.kt
  31. 8 1
      app/src/main/java/io/legado/app/ui/widget/components/alert/AppAlertDialog.kt
  32. 1 1
      app/src/main/java/io/legado/app/ui/widget/components/book/SearchBookItem.kt
  33. 0 97
      app/src/main/java/io/legado/app/ui/widget/components/cover/Cover.kt
  34. 4 12
      app/src/main/java/io/legado/app/ui/widget/components/explore/ExploreKindMultiTypeItem.kt
  35. 2 2
      app/src/main/java/io/legado/app/ui/widget/components/image/cover/BookshelfCover.kt
  36. 7 4
      app/src/main/java/io/legado/app/ui/widget/components/image/cover/CoilBookCover.kt
  37. 1 1
      app/src/main/java/io/legado/app/ui/widget/components/image/cover/CoverImageRequest.kt
  38. 1 1
      app/src/main/java/io/legado/app/ui/widget/components/image/sourceIcon/SourceIcon.kt
  39. 1 0
      app/src/main/java/io/legado/app/ui/widget/components/tabRow/AppTabRow.kt

+ 4 - 4
app/src/main/assets/defaultData/coverRule.json

@@ -1,5 +1,5 @@
 {
-  "enable": false,
-  "searchUrl": "",
-  "coverRule": ""
-}
+  "enable": true,
+  "searchUrl": "data:;base64,{{java.base64Encode(key)}},{\"type\":\"lyc\"}",
+  "coverRule": "@js:\nvar key = java.hexDecodeToString(result);\nvar url1 = `https://pre-api.tuishujun.com/api/searchBook?search_value=${key}&page=1&pageSize=20`;\nvar url2 = `http://m.ypshuo.com/api/novel/search?keyword=${key}&searchType=1&page=1`;\nvar [rr1, rr2] = java.ajaxAll([url1, url2]).map(r => r.body());\nfunction jjson(str, rule) {\n    try {\n        return com.jayway.jsonpath.JsonPath.read(str, rule);\n    } catch (e) {\n        return [];\n    }\n}\nrr1 = jjson(rr1, '$.data.data[*]');\nrr2 = jjson(rr2, '$.data.data[*]');\nvar na = String(book.name),\n    au = String(book.author);\nfunction search() {\n    for (let char of rr1) {\n        //本地书名包含搜索结果书名\n        if (na.includes(char.title + '')) {\n            let au2 = char.author_nickname + '';\n            //作者匹配\n            if (au.includes(au2) || au2.includes(au)) {\n                return char.cover;\n            }\n        }\n    }\n    for (let char of rr2) {\n        if (na.includes(char.novel_name + '')) {\n            let au2 = char.author_name + '';\n            if (au.includes(au2) || au2.includes(au)) {\n                return char.novel_img;\n            }\n        }\n    }\n    return '';\n}\nsearch()"
+}

+ 1 - 42
app/src/main/assets/defaultData/rssSources.json

@@ -1,42 +1 @@
-[
-  {
-    "customOrder": 2,
-    "enableJs": true,
-    "enabled": true,
-    "singleUrl": true,
-    "sourceGroup": "legado",
-    "sourceIcon": "https://cdn.jsdelivr.net/gh/gedoor/legado@master/app/src/main/res/mipmap-hdpi/ic_launcher.png",
-    "sourceName": "使用说明",
-    "sourceUrl": "https://www.yuque.com/legado"
-  },
-  {
-    "customOrder": 3,
-    "enableJs": true,
-    "enabled": true,
-    "singleUrl": true,
-    "sourceGroup": "legado",
-    "sourceIcon": "http://mmbiz.qpic.cn/mmbiz_png/hpfMV8hEuL2eS6vnCxvTzoOiaCAibV6exBzJWq9xMic9xDg3YXAick87tsfafic0icRwkQ5ibV0bJ84JtSuxhPuEDVquA/0?wx_fmt=png",
-    "sourceName": "小说拾遗",
-    "sourceUrl": "snssdk1128://user/profile/562564899806367"
-  },
-  {
-    "customOrder": 4,
-    "enableJs": true,
-    "enabled": true,
-    "singleUrl": true,
-    "sourceGroup": "legado",
-    "sourceIcon": "https://cdn.jsdelivr.net/gh/mgz0227/meowcloud/icon.png",
-    "sourceName": "Meow云",
-    "sourceUrl": "https://pan.miaogongzi.net"
-  },
-  {
-    "customOrder": 5,
-    "enableJs": true,
-    "enabled": true,
-    "singleUrl": true,
-    "sourceGroup": "legado",
-    "sourceIcon": "https://cdn.jsdelivr.net/gh/gedoor/legado@master/app/src/main/res/mipmap-hdpi/ic_launcher.png",
-    "sourceName": "烏雲净化",
-    "sourceUrl": "https://www.lanzoux.com/b0bw8jwoh"
-  }
-]
+[]

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

@@ -45,6 +45,7 @@ import io.legado.app.domain.usecase.CacheBookChaptersUseCase
 import io.legado.app.domain.usecase.ChangeBookSourceUseCase
 import io.legado.app.domain.usecase.ClearBookCacheUseCase
 import io.legado.app.domain.usecase.DeleteBooksUseCase
+import io.legado.app.domain.usecase.ExploreKindUiUseCase
 import io.legado.app.domain.usecase.GetReadingProgressUseCase
 import io.legado.app.domain.usecase.RemoveBookGroupAssignmentUseCase
 import io.legado.app.domain.usecase.ResolveBookShelfStateUseCase
@@ -103,7 +104,6 @@ import io.legado.app.ui.rss.favorites.RssFavoritesViewModel
 import io.legado.app.ui.rss.read.ReadRssViewModel
 import io.legado.app.ui.rss.source.manage.RssSourceViewModel
 import io.legado.app.ui.rss.subscription.RuleSubViewModel
-import io.legado.app.ui.widget.components.explore.ExploreKindUiUseCase
 import org.koin.core.module.dsl.singleOf
 import org.koin.core.module.dsl.viewModel
 import org.koin.core.module.dsl.viewModelOf

+ 5 - 5
app/src/main/java/io/legado/app/ui/widget/components/explore/ExploreKindUiUseCase.kt → app/src/main/java/io/legado/app/domain/usecase/ExploreKindUiUseCase.kt

@@ -1,4 +1,4 @@
-package io.legado.app.ui.widget.components.explore
+package io.legado.app.domain.usecase
 
 import androidx.appcompat.app.AppCompatActivity
 import com.script.rhino.runScriptWithContext
@@ -10,7 +10,7 @@ import io.legado.app.data.entities.rule.ExploreKind
 import io.legado.app.help.source.getExploreInfoMap
 import io.legado.app.ui.login.SourceLoginJsExtensions
 import io.legado.app.utils.InfoMap
-import kotlinx.coroutines.Dispatchers.IO
+import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.withContext
 import java.util.concurrent.ConcurrentHashMap
 
@@ -76,7 +76,7 @@ class ExploreKindUiUseCase(
                 override fun reUiView() = onRefreshKinds()
             }
         )
-        withContext(IO) {
+        withContext(Dispatchers.IO) {
             evalButtonClick(actionText, source, effectiveInfoMap, title, sourceJsExtensions)
         }
     }
@@ -92,7 +92,7 @@ class ExploreKindUiUseCase(
 
     private suspend fun getOrLoadBookSource(sourceUrl: String): BookSource? {
         sourceCache[sourceUrl]?.let { return it }
-        return withContext(IO) {
+        return withContext(Dispatchers.IO) {
             bookSourceDao.getBookSource(sourceUrl)
         }?.also { source ->
             sourceCache[sourceUrl] = source
@@ -127,4 +127,4 @@ class ExploreKindUiUseCase(
             AppLog.put("ExploreUI Button $name JavaScript error", e)
         }
     }
-}
+}

+ 1 - 1
app/src/main/java/io/legado/app/ui/book/explore/ExploreShowScreen.kt

@@ -61,6 +61,7 @@ import dev.chrisbanes.haze.HazeState
 import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
 import io.legado.app.data.entities.SearchBook
 import io.legado.app.domain.model.BookShelfState
+import io.legado.app.domain.usecase.ExploreKindUiUseCase
 import io.legado.app.ui.main.bookCoverSharedElementKey
 import io.legado.app.ui.theme.LegadoTheme
 import io.legado.app.ui.theme.ThemeResolver
@@ -74,7 +75,6 @@ import io.legado.app.ui.widget.components.book.SearchBookListItem
 import io.legado.app.ui.widget.components.button.AnimatedTextButton
 import io.legado.app.ui.widget.components.card.TextCard
 import io.legado.app.ui.widget.components.explore.ExploreKindMultiTypeItem
-import io.legado.app.ui.widget.components.explore.ExploreKindUiUseCase
 import io.legado.app.ui.widget.components.explore.calculateExploreKindRows
 import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu
 import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem

+ 1 - 1
app/src/main/java/io/legado/app/ui/book/group/GroupEditSheet.kt

@@ -36,7 +36,7 @@ import io.legado.app.ui.widget.components.alert.AppAlertDialog
 import io.legado.app.ui.widget.components.button.ConfirmDismissButtonsRow
 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.CoilBookCover
+import io.legado.app.ui.widget.components.image.cover.CoilBookCover
 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.CompactSwitchSettingItem

+ 2 - 2
app/src/main/java/io/legado/app/ui/book/info/BookInfoScreen.kt

@@ -91,9 +91,9 @@ import io.legado.app.ui.widget.components.AppTextField
 import io.legado.app.ui.widget.components.alert.AppAlertDialog
 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.cover.CoilBookCover
-import io.legado.app.ui.widget.components.cover.buildCoverImageRequest
 import io.legado.app.ui.widget.components.icon.AppIcon
+import io.legado.app.ui.widget.components.image.cover.CoilBookCover
+import io.legado.app.ui.widget.components.image.cover.buildCoverImageRequest
 import io.legado.app.ui.widget.components.log.AppLogSheet
 import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu
 import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem

+ 1 - 1
app/src/main/java/io/legado/app/ui/book/info/BookInfoSheets.kt

@@ -77,7 +77,7 @@ import io.legado.app.ui.widget.components.button.SmallIconButton
 import io.legado.app.ui.widget.components.card.GlassCard
 import io.legado.app.ui.widget.components.card.SelectionItemCard
 import io.legado.app.ui.widget.components.checkBox.AppCheckbox
-import io.legado.app.ui.widget.components.cover.CoilBookCover
+import io.legado.app.ui.widget.components.image.cover.CoilBookCover
 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.modalBottomSheet.AppModalBottomSheet

+ 1 - 1
app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt

@@ -50,7 +50,7 @@ import io.legado.app.model.analyzeRule.AnalyzeUrl
 import io.legado.app.model.localBook.LocalBook
 import io.legado.app.model.webBook.WebBook
 import io.legado.app.ui.config.coverConfig.CoverConfig
-import io.legado.app.ui.widget.components.cover.buildCoverImageRequest
+import io.legado.app.ui.widget.components.image.cover.buildCoverImageRequest
 import io.legado.app.utils.ArchiveUtils
 import io.legado.app.utils.GSON
 import io.legado.app.utils.ImageSaveUtils

+ 1 - 1
app/src/main/java/io/legado/app/ui/book/info/edit/BookInfoEditScreen.kt

@@ -60,7 +60,7 @@ import io.legado.app.ui.widget.components.AppScaffold
 import io.legado.app.ui.widget.components.AppTextField
 import io.legado.app.ui.widget.components.alert.AppAlertDialog
 import io.legado.app.ui.widget.components.button.MediumOutlinedIconButton
-import io.legado.app.ui.widget.components.cover.CoilBookCover
+import io.legado.app.ui.widget.components.image.cover.CoilBookCover
 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.settingItem.SwitchSettingItem

+ 1 - 1
app/src/main/java/io/legado/app/ui/book/manage/BookshelfManageScreen.kt

@@ -88,10 +88,10 @@ import io.legado.app.ui.widget.components.card.GlassCard
 import io.legado.app.ui.widget.components.card.ReorderableSelectionItem
 import io.legado.app.ui.widget.components.card.SelectionItemCard
 import io.legado.app.ui.widget.components.card.TextCard
-import io.legado.app.ui.widget.components.cover.CoilBookCover
 import io.legado.app.ui.widget.components.divider.PillDivider
 import io.legado.app.ui.widget.components.filePicker.FilePickerSheet
 import io.legado.app.ui.widget.components.icon.AppIcons
+import io.legado.app.ui.widget.components.image.cover.CoilBookCover
 import io.legado.app.ui.widget.components.list.ListScaffold
 import io.legado.app.ui.widget.components.list.ListUiState
 import io.legado.app.ui.widget.components.log.AppLogSheet

+ 1 - 1
app/src/main/java/io/legado/app/ui/book/readRecord/ReadRecordOverviewScreen.kt

@@ -56,8 +56,8 @@ 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.CoilBookCover
 import io.legado.app.ui.widget.components.heatmap.HeatmapMode
+import io.legado.app.ui.widget.components.image.cover.CoilBookCover
 import io.legado.app.ui.widget.components.tabRow.AppTabRow
 import io.legado.app.ui.widget.components.text.AppText
 import io.legado.app.ui.widget.components.topbar.GlassMediumFlexibleTopAppBar

+ 2 - 2
app/src/main/java/io/legado/app/ui/book/readRecord/ReadRecordScreen.kt

@@ -66,8 +66,8 @@ import io.legado.app.ui.theme.LegadoTheme
 import io.legado.app.ui.theme.adaptiveContentPaddingOnlyVertical
 import io.legado.app.ui.theme.adaptiveHorizontalPadding
 import io.legado.app.ui.theme.fadingEdge
-import io.legado.app.ui.widget.CollapsibleHeader
 import io.legado.app.ui.widget.components.AppScaffold
+import io.legado.app.ui.widget.components.CollapsibleHeader
 import io.legado.app.ui.widget.components.EmptyMessage
 import io.legado.app.ui.widget.components.SearchBar
 import io.legado.app.ui.widget.components.alert.AppAlertDialog
@@ -75,7 +75,6 @@ import io.legado.app.ui.widget.components.button.AppIconButton
 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.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
@@ -89,6 +88,7 @@ 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.rememberWeeks
 import io.legado.app.ui.widget.components.icon.AppIcon
+import io.legado.app.ui.widget.components.image.cover.CoilBookCover
 import io.legado.app.ui.widget.components.list.TopFloatingStickyItem
 import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
 import io.legado.app.ui.widget.components.swipe.SwipeAction

+ 15 - 3
app/src/main/java/io/legado/app/ui/book/readRecord/component/ReadRecordListItems.kt

@@ -2,8 +2,20 @@ package io.legado.app.ui.book.readRecord.component
 
 import androidx.compose.foundation.basicMarquee
 import androidx.compose.foundation.clickable
-import androidx.compose.foundation.layout.*
-import androidx.compose.runtime.*
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.text.buildAnnotatedString
@@ -16,7 +28,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.CoilBookCover
+import io.legado.app.ui.widget.components.image.cover.CoilBookCover
 import io.legado.app.ui.widget.components.text.AppText
 import io.legado.app.utils.StringUtils.formatFriendlyDate
 

+ 15 - 4
app/src/main/java/io/legado/app/ui/book/readRecord/component/ReadRecordSummary.kt

@@ -1,10 +1,21 @@
 package io.legado.app.ui.book.readRecord.component
 
-import androidx.compose.foundation.layout.*
-import androidx.compose.runtime.*
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.text.font.FontWeight
 import androidx.compose.ui.unit.dp
 import io.legado.app.ui.book.readRecord.ReadRecordFormatter
 import io.legado.app.ui.book.readRecord.ReadRecordUiState
@@ -12,7 +23,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.CoilBookCover
+import io.legado.app.ui.widget.components.image.cover.CoilBookCover
 import io.legado.app.ui.widget.components.text.AppText
 import java.time.format.DateTimeFormatter
 

+ 19 - 29
app/src/main/java/io/legado/app/ui/book/search/SearchScreen.kt

@@ -14,7 +14,6 @@ import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.foundation.lazy.items
@@ -23,17 +22,12 @@ import androidx.compose.foundation.lazy.rememberLazyListState
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.Book
 import androidx.compose.material.icons.filled.Close
-import androidx.compose.material.icons.filled.History
 import androidx.compose.material.icons.filled.Layers
 import androidx.compose.material.icons.filled.PlayArrow
 import androidx.compose.material.icons.filled.Stop
 import androidx.compose.material3.ExperimentalMaterial3Api
-import io.legado.app.ui.widget.components.progressIndicator.AppCircularProgressIndicator
 import androidx.compose.material3.Text
 import androidx.compose.material3.TopAppBarDefaults
-import io.legado.app.ui.widget.components.topbar.GlassMediumFlexibleTopAppBar
-import io.legado.app.ui.widget.components.topbar.M3GlassScrollBehavior
-import io.legado.app.ui.widget.components.topbar.GlassTopAppBarDefaults
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.LaunchedEffect
@@ -42,9 +36,9 @@ import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberUpdatedState
+import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.runtime.setValue
 import androidx.compose.runtime.snapshotFlow
-import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.input.nestedscroll.nestedScroll
@@ -58,6 +52,7 @@ import androidx.lifecycle.compose.LocalLifecycleOwner
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import io.legado.app.R
 import io.legado.app.data.entities.SearchKeyword
+import io.legado.app.ui.main.bookCoverSharedElementKey
 import io.legado.app.ui.main.bookshelf.BookShelfItem
 import io.legado.app.ui.theme.LegadoTheme
 import io.legado.app.ui.theme.ThemeResolver
@@ -68,22 +63,23 @@ import io.legado.app.ui.widget.components.AppFloatingActionButton
 import io.legado.app.ui.widget.components.AppScaffold
 import io.legado.app.ui.widget.components.SearchBar
 import io.legado.app.ui.widget.components.alert.AppAlertDialog
-import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
 import io.legado.app.ui.widget.components.book.SearchBookListItem
 import io.legado.app.ui.widget.components.button.SmallIconButton
 import io.legado.app.ui.widget.components.button.SmallTextButton
 import io.legado.app.ui.widget.components.card.NormalCard
 import io.legado.app.ui.widget.components.card.SelectionItemCard
-import io.legado.app.ui.widget.components.topbar.TopBarActionButton
-import io.legado.app.ui.widget.components.topbar.TopBarAnimatedActionButton
-import io.legado.app.ui.widget.components.topbar.TopBarNavigationButton
 import io.legado.app.ui.widget.components.icon.AppIcon
 import io.legado.app.ui.widget.components.icon.AppIcons
-import io.legado.app.ui.book.search.ScopeSelectSheet
 import io.legado.app.ui.widget.components.list.TopFloatingStickyItem
-import io.legado.app.ui.widget.components.tabRow.AppTabRow
+import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
+import io.legado.app.ui.widget.components.progressIndicator.AppCircularProgressIndicator
 import io.legado.app.ui.widget.components.text.AppText
-import io.legado.app.ui.main.bookCoverSharedElementKey
+import io.legado.app.ui.widget.components.topbar.GlassMediumFlexibleTopAppBar
+import io.legado.app.ui.widget.components.topbar.GlassTopAppBarDefaults
+import io.legado.app.ui.widget.components.topbar.M3GlassScrollBehavior
+import io.legado.app.ui.widget.components.topbar.TopBarActionButton
+import io.legado.app.ui.widget.components.topbar.TopBarAnimatedActionButton
+import io.legado.app.ui.widget.components.topbar.TopBarNavigationButton
 import io.legado.app.utils.toastOnUi
 import kotlinx.coroutines.FlowPreview
 import kotlinx.coroutines.flow.debounce
@@ -480,19 +476,17 @@ fun SearchScreen(
             viewModel.onIntent(SearchIntent.DismissEmptyScopeAction)
         },
         title = stringResource(R.string.draw),
+        textProvider = {
+            if (wasPrecisionSearch) {
+                stringResource(R.string.search_empty_scope_disable_precision, scopeDisplay)
+            } else {
+                stringResource(R.string.search_empty_scope_switch_all, scopeDisplay)
+            }
+        },
         confirmText = stringResource(R.string.ok),
         onConfirm = { viewModel.onIntent(SearchIntent.ConfirmEmptyScopeAction) },
         dismissText = stringResource(R.string.cancel),
         onDismiss = { viewModel.onIntent(SearchIntent.DismissEmptyScopeAction) },
-        content = {
-            Text(
-                text = if (it.wasPrecisionSearch) {
-                    stringResource(R.string.search_empty_scope_disable_precision, it.scopeDisplay)
-                } else {
-                    stringResource(R.string.search_empty_scope_switch_all, it.scopeDisplay)
-                }
-            )
-        }
     )
 
     ScopeSelectSheet(
@@ -681,12 +675,8 @@ private fun SearchResultFooter(
                     verticalAlignment = Alignment.CenterVertically,
                     horizontalArrangement = Arrangement.spacedBy(8.dp),
                 ) {
-                    Box(
-                        modifier = Modifier.size(18.dp),
-                        contentAlignment = Alignment.Center
-                    ) {
-                        AppCircularProgressIndicator()
-                    }
+                    AppCircularProgressIndicator()
+                    Spacer(modifier = Modifier.padding(horizontal = 8.dp))
                     AppText(text = stringResource(R.string.is_loading))
                 }
             }

+ 1 - 1
app/src/main/java/io/legado/app/ui/book/toc/TocScreen.kt

@@ -84,10 +84,10 @@ import io.legado.app.ui.replace.ReplaceEditRoute
 import io.legado.app.ui.theme.LegadoTheme
 import io.legado.app.ui.theme.adaptiveContentPaddingOnlyVertical
 import io.legado.app.ui.theme.adaptiveHorizontalPadding
-import io.legado.app.ui.widget.CollapsibleHeader
 import io.legado.app.ui.widget.components.ActionItem
 import io.legado.app.ui.widget.components.AppFloatingActionButtonMenu
 import io.legado.app.ui.widget.components.AppScaffold
+import io.legado.app.ui.widget.components.CollapsibleHeader
 import io.legado.app.ui.widget.components.EmptyMessage
 import io.legado.app.ui.widget.components.FabMenuItem
 import io.legado.app.ui.widget.components.SelectionBottomBar

+ 0 - 328
app/src/main/java/io/legado/app/ui/book/toc/rule/TxtTocRuleDialog.kt

@@ -1,328 +0,0 @@
-package io.legado.app.ui.book.toc.rule
-
-import android.annotation.SuppressLint
-import android.content.Context
-import android.os.Bundle
-import android.view.MenuItem
-import android.view.View
-import android.view.ViewGroup
-import androidx.appcompat.widget.Toolbar
-import androidx.core.os.bundleOf
-import androidx.fragment.app.viewModels
-import androidx.lifecycle.lifecycleScope
-import androidx.recyclerview.widget.DiffUtil
-import androidx.recyclerview.widget.ItemTouchHelper
-import androidx.recyclerview.widget.RecyclerView
-import io.legado.app.R
-import io.legado.app.base.BaseDialogFragment
-import io.legado.app.base.adapter.ItemViewHolder
-import io.legado.app.base.adapter.RecyclerAdapter
-import io.legado.app.constant.AppLog
-import io.legado.app.data.appDb
-import io.legado.app.data.entities.TxtTocRule
-import io.legado.app.databinding.DialogEditTextBinding
-import io.legado.app.databinding.DialogTocRegexBinding
-import io.legado.app.databinding.ItemTocRegexBinding
-import io.legado.app.lib.dialogs.alert
-//import io.legado.app.lib.theme.backgroundColor
-//import io.legado.app.lib.theme.primaryColor
-import io.legado.app.ui.association.ImportTxtTocRuleDialog
-import io.legado.app.ui.file.HandleFileContract
-import io.legado.app.ui.qrcode.QrCodeResult
-import io.legado.app.ui.widget.recycler.ItemTouchCallback
-import io.legado.app.ui.widget.recycler.VerticalDivider
-import io.legado.app.utils.ACache
-import io.legado.app.utils.isAbsUrl
-import io.legado.app.utils.launch
-import io.legado.app.utils.readText
-import io.legado.app.utils.showDialogFragment
-import io.legado.app.utils.showHelp
-import io.legado.app.utils.splitNotBlank
-import io.legado.app.utils.toastOnUi
-import io.legado.app.utils.viewbindingdelegate.viewBinding
-import kotlinx.coroutines.Dispatchers.IO
-import kotlinx.coroutines.flow.catch
-import kotlinx.coroutines.flow.conflate
-import kotlinx.coroutines.flow.flowOn
-import kotlinx.coroutines.launch
-
-/**
- * txt目录规则
- */
-class TxtTocRuleDialog() : BaseDialogFragment(R.layout.dialog_toc_regex),
-    Toolbar.OnMenuItemClickListener,
-    TxtTocRuleEditDialog.Callback {
-
-    constructor(tocRegex: String?) : this() {
-        arguments = Bundle().apply {
-            putString("tocRegex", tocRegex)
-        }
-    }
-
-    private val importTocRuleKey = "tocRuleUrl"
-    private val viewModel: TxtTocRuleViewModel by viewModels()
-    private val binding by viewBinding(DialogTocRegexBinding::bind)
-    private val adapter by lazy { TocRegexAdapter(requireContext()) }
-    var selectedName: String? = null
-    private var durRegex: String? = null
-    private val qrCodeResult = registerForActivityResult(QrCodeResult()) {
-        it ?: return@registerForActivityResult
-        showDialogFragment(ImportTxtTocRuleDialog(it))
-    }
-    private val importDoc = registerForActivityResult(HandleFileContract()) {
-        kotlin.runCatching {
-            it.uri?.readText(requireContext())?.let {
-                showDialogFragment(ImportTxtTocRuleDialog(it))
-            }
-        }.onFailure {
-            toastOnUi("readTextError:${it.localizedMessage}")
-        }
-    }
-
-    override fun onStart() {
-        super.onStart()
-    }
-
-    override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
-        //binding.toolBar.setBackgroundColor(primaryColor)
-        durRegex = arguments?.getString("tocRegex")
-        binding.toolBar.setTitle(R.string.txt_toc_rule)
-        binding.toolBar.inflateMenu(R.menu.txt_toc_rule)
-        //binding.toolBar.menu.applyTint(requireContext())
-        binding.toolBar.setOnMenuItemClickListener(this)
-        initView()
-        initData()
-    }
-
-    private fun initView() = binding.run {
-        recyclerView.addItemDecoration(VerticalDivider(requireContext()))
-        recyclerView.adapter = adapter
-        val itemTouchCallback = ItemTouchCallback(adapter)
-        itemTouchCallback.isCanDrag = true
-        ItemTouchHelper(itemTouchCallback).attachToRecyclerView(recyclerView)
-        tvCancel.setOnClickListener {
-            dismissAllowingStateLoss()
-        }
-        tvOk.setOnClickListener {
-            adapter.getItems().forEach { tocRule ->
-                if (selectedName == tocRule.name) {
-                    val callBack = activity as? CallBack
-                    callBack?.onTocRegexDialogResult(tocRule.rule)
-                    dismissAllowingStateLoss()
-                    return@setOnClickListener
-                }
-            }
-        }
-    }
-
-    private fun initData() {
-        lifecycleScope.launch {
-            appDb.txtTocRuleDao.observeAll().catch {
-                AppLog.put("TXT目录规则对话框获取数据失败\n${it.localizedMessage}", it)
-            }.flowOn(IO).conflate().collect { tocRules ->
-                initSelectedName(tocRules)
-                adapter.setItems(tocRules, adapter.diffItemCallBack)
-            }
-        }
-    }
-
-    private fun initSelectedName(tocRules: List<TxtTocRule>) {
-        if (selectedName == null && durRegex != null) {
-            tocRules.forEach {
-                if (durRegex == it.rule) {
-                    selectedName = it.name
-                    return@forEach
-                }
-            }
-            if (selectedName == null) {
-                selectedName = ""
-            }
-        }
-    }
-
-    override fun onMenuItemClick(item: MenuItem?): Boolean {
-        when (item?.itemId) {
-            R.id.menu_add -> showDialogFragment(TxtTocRuleEditDialog())
-            R.id.menu_import_local -> importDoc.launch {
-                mode = HandleFileContract.FILE
-                allowExtensions = arrayOf("txt", "json")
-            }
-            R.id.menu_import_onLine -> showImportDialog()
-            R.id.menu_import_qr -> qrCodeResult.launch()
-            R.id.menu_help -> showHelp("txtTocRuleHelp")
-        }
-        return false
-    }
-
-    override fun saveTxtTocRule(txtTocRule: TxtTocRule) {
-        //viewModel.update(txtTocRule)
-    }
-
-    @SuppressLint("InflateParams")
-    private fun showImportDialog() {
-        val aCache = ACache.get(cacheDir = false)
-        val defaultUrl = "https://gitee.com/fisher52/YueDuJson/raw/master/myTxtChapterRule.json"
-        val cacheUrls: MutableList<String> = aCache
-            .getAsString(importTocRuleKey)
-            ?.splitNotBlank(",")
-            ?.toMutableList()
-            ?: mutableListOf()
-        if (!cacheUrls.contains(defaultUrl)) {
-            cacheUrls.add(0, defaultUrl)
-        }
-        requireContext().alert(titleResource = R.string.import_on_line) {
-            val alertBinding = DialogEditTextBinding.inflate(layoutInflater).apply {
-                editView.hint = "url"
-                editView.setFilterValues(cacheUrls)
-                editView.delCallBack = {
-                    cacheUrls.remove(it)
-                    aCache.put(importTocRuleKey, cacheUrls.joinToString(","))
-                }
-            }
-            customView { alertBinding.root }
-            okButton {
-                val text = alertBinding.editView.text?.toString()
-                text?.let {
-                    if (it.isAbsUrl() && !cacheUrls.contains(it)) {
-                        cacheUrls.add(0, it)
-                        aCache.put(importTocRuleKey, cacheUrls.joinToString(","))
-                    }
-                    showDialogFragment(ImportTxtTocRuleDialog(it))
-                }
-            }
-            cancelButton()
-        }
-    }
-
-    inner class TocRegexAdapter(context: Context) :
-        RecyclerAdapter<TxtTocRule, ItemTocRegexBinding>(context),
-        ItemTouchCallback.Callback {
-
-        val diffItemCallBack = object : DiffUtil.ItemCallback<TxtTocRule>() {
-
-            override fun areItemsTheSame(oldItem: TxtTocRule, newItem: TxtTocRule): Boolean {
-                return oldItem.id == newItem.id
-            }
-
-            override fun areContentsTheSame(oldItem: TxtTocRule, newItem: TxtTocRule): Boolean {
-                if (oldItem.name != newItem.name) {
-                    return false
-                }
-                if (oldItem.enable != newItem.enable) {
-                    return false
-                }
-                if (oldItem.example != newItem.example) {
-                    return false
-                }
-                return true
-            }
-
-            override fun getChangePayload(oldItem: TxtTocRule, newItem: TxtTocRule): Any? {
-                val payload = Bundle()
-                if (oldItem.name != newItem.name) {
-                    payload.putBoolean("upName", true)
-                }
-                if (oldItem.enable != newItem.enable) {
-                    payload.putBoolean("enabled", newItem.enable)
-                }
-                if (oldItem.example != newItem.example) {
-                    payload.putBoolean("upExample", true)
-                }
-                if (payload.isEmpty) {
-                    return null
-                }
-                return payload
-            }
-        }
-
-        override fun getViewBinding(parent: ViewGroup): ItemTocRegexBinding {
-            return ItemTocRegexBinding.inflate(inflater, parent, false)
-        }
-
-        override fun convert(
-            holder: ItemViewHolder,
-            binding: ItemTocRegexBinding,
-            item: TxtTocRule,
-            payloads: MutableList<Any>
-        ) {
-            binding.apply {
-                if (payloads.isEmpty()) {
-                    //root.setBackgroundColor(context.backgroundColor)
-                    rbRegexName.text = item.name
-                    titleExample.text = item.example
-                    rbRegexName.isChecked = item.name == selectedName
-                    swtEnabled.isChecked = item.enable
-                } else {
-                    for (i in payloads.indices) {
-                        val bundle = payloads[i] as Bundle
-                        bundle.keySet().forEach {
-                            when (it) {
-                                "upName" -> rbRegexName.text = item.name
-                                "upExample" -> titleExample.text = item.example
-                                "enabled" -> swtEnabled.isChecked = item.enable
-                                "upSelect" -> rbRegexName.isChecked = item.name == selectedName
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        override fun registerListener(holder: ItemViewHolder, binding: ItemTocRegexBinding) {
-            binding.apply {
-                rbRegexName.setOnCheckedChangeListener { buttonView, isChecked ->
-                    if (buttonView.isPressed && isChecked) {
-                        selectedName = getItem(holder.layoutPosition)?.name
-                        updateItems(0, itemCount - 1, bundleOf("upSelect" to null))
-                    }
-                }
-                swtEnabled.setOnCheckedChangeListener { buttonView, isChecked ->
-                    if (buttonView.isPressed) {
-                        getItem(holder.layoutPosition)?.let {
-                            it.enable = isChecked
-                            //viewModel.update(it)
-                        }
-                    }
-                }
-                ivEdit.setOnClickListener {
-                    showDialogFragment(TxtTocRuleEditDialog(getItem(holder.layoutPosition)?.id))
-                }
-                ivDelete.setOnClickListener {
-                    getItem(holder.layoutPosition)?.let { item ->
-                        alert(R.string.draw) {
-                            setMessage(getString(R.string.sure_del) + "\n" + item.name)
-                            noButton()
-                            yesButton {
-                                //viewModel.delete(item)
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        private var isMoved = false
-
-        override fun swap(srcPosition: Int, targetPosition: Int): Boolean {
-            swapItem(srcPosition, targetPosition)
-            isMoved = true
-            return super.swap(srcPosition, targetPosition)
-        }
-
-        override fun onClearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
-            super.onClearView(recyclerView, viewHolder)
-            if (isMoved) {
-                for ((index, item) in getItems().withIndex()) {
-                    item.serialNumber = index + 1
-                }
-                //viewModel.update(*getItems().toTypedArray())
-            }
-            isMoved = false
-        }
-    }
-
-    interface CallBack {
-        fun onTocRegexDialogResult(tocRegex: String) {}
-    }
-
-}

+ 0 - 147
app/src/main/java/io/legado/app/ui/book/toc/rule/TxtTocRuleEditDialog.kt

@@ -1,147 +0,0 @@
-package io.legado.app.ui.book.toc.rule
-
-import android.app.Application
-import android.os.Bundle
-import android.view.MenuItem
-import android.view.View
-import android.view.ViewGroup
-import androidx.appcompat.widget.Toolbar
-import androidx.fragment.app.viewModels
-import io.legado.app.R
-import io.legado.app.base.BaseBottomSheetDialogFragment
-import io.legado.app.base.BaseViewModel
-import io.legado.app.constant.AppLog
-import io.legado.app.data.appDb
-import io.legado.app.data.entities.TxtTocRule
-import io.legado.app.databinding.DialogTocRegexEditBinding
-import io.legado.app.exception.NoStackTraceException
-//import io.legado.app.lib.theme.primaryColor
-import io.legado.app.utils.*
-import io.legado.app.utils.viewbindingdelegate.viewBinding
-import kotlinx.coroutines.Dispatchers
-import java.util.regex.Pattern
-import java.util.regex.PatternSyntaxException
-
-class TxtTocRuleEditDialog() : BaseBottomSheetDialogFragment(R.layout.dialog_toc_regex_edit),
-    Toolbar.OnMenuItemClickListener {
-
-    constructor(id: Long?) : this() {
-        id ?: return
-        arguments = Bundle().apply {
-            putLong("id", id)
-        }
-    }
-
-    private val binding by viewBinding(DialogTocRegexEditBinding::bind)
-    private val viewModel by viewModels<ViewModel>()
-    private val callback get() = (parentFragment as? Callback) ?: activity as? Callback
-
-    override fun onStart() {
-        super.onStart()
-        setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
-    }
-
-    override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
-        //binding.toolBar.setBackgroundColor(primaryColor)
-        initMenu()
-        viewModel.initData(arguments?.getLong("id")) {
-            upRuleView(it)
-        }
-    }
-
-    private fun initMenu() {
-        binding.toolBar.inflateMenu(R.menu.txt_toc_rule_edit)
-        //binding.toolBar.menu.applyTint(requireContext())
-        binding.toolBar.setOnMenuItemClickListener(this)
-    }
-
-    override fun onMenuItemClick(item: MenuItem?): Boolean {
-        when (item?.itemId) {
-            R.id.menu_save -> {
-                val tocRule = getRuleFromView()
-                if (checkValid(tocRule)) {
-                    callback?.saveTxtTocRule(getRuleFromView())
-                    dismissAllowingStateLoss()
-                }
-            }
-            R.id.menu_copy_rule -> context?.sendToClip(GSON.toJson(getRuleFromView()))
-            R.id.menu_paste_rule -> viewModel.pasteRule {
-                upRuleView(it)
-            }
-        }
-        return true
-    }
-
-    private fun checkValid(tocRule: TxtTocRule): Boolean {
-        if (tocRule.name.isEmpty()) {
-            toastOnUi("名称不能为空")
-            return false
-        }
-
-        try {
-            Pattern.compile(tocRule.rule, Pattern.MULTILINE)
-        } catch (ex: PatternSyntaxException) {
-            AppLog.put("正则语法错误或不支持(txt):${ex.localizedMessage}", ex, true)
-            return false
-        }
-
-        return true
-    }
-
-    private fun upRuleView(tocRule: TxtTocRule?) {
-        binding.tvRuleName.setText(tocRule?.name)
-        binding.tvRuleRegex.setText(tocRule?.rule)
-        binding.tvRuleExample.setText(tocRule?.example)
-    }
-
-    private fun getRuleFromView(): TxtTocRule {
-        val tocRule = viewModel.tocRule ?: TxtTocRule().apply {
-            viewModel.tocRule = this
-        }
-        binding.run {
-            tocRule.name = tvRuleName.text.toString()
-            tocRule.rule = tvRuleRegex.text.toString()
-            tocRule.example = tvRuleExample.text.toString()
-        }
-        return tocRule
-    }
-
-    class ViewModel(application: Application) : BaseViewModel(application) {
-
-        var tocRule: TxtTocRule? = null
-
-        fun initData(id: Long?, finally: (tocRule: TxtTocRule?) -> Unit) {
-            if (tocRule != null) return
-            execute {
-                if (id == null) return@execute
-                tocRule = appDb.txtTocRuleDao.get(id)
-            }.onFinally {
-                finally.invoke(tocRule)
-            }
-        }
-
-        fun pasteRule(success: (TxtTocRule) -> Unit) {
-            execute(context = Dispatchers.Main) {
-                val text = context.getClipText()
-                if (text.isNullOrBlank()) {
-                    throw NoStackTraceException("剪贴板为空")
-                }
-                GSON.fromJsonObject<TxtTocRule>(text).getOrNull()
-                    ?: throw NoStackTraceException("格式不对")
-            }.onSuccess {
-                success.invoke(it)
-            }.onError {
-                context.toastOnUi(it.localizedMessage ?: "Error")
-                it.printOnDebug()
-            }
-        }
-
-    }
-
-    interface Callback {
-
-        fun saveTxtTocRule(txtTocRule: TxtTocRule)
-
-    }
-
-}

+ 0 - 66
app/src/main/java/io/legado/app/ui/config/CoverRuleConfigDialog.kt

@@ -1,66 +0,0 @@
-package io.legado.app.ui.config
-
-import android.os.Bundle
-import android.util.Log
-import android.view.View
-import androidx.lifecycle.lifecycleScope
-import io.legado.app.R
-import io.legado.app.base.BaseBottomSheetDialogFragment
-import io.legado.app.databinding.DialogCoverRuleConfigBinding
-//import io.legado.app.lib.theme.primaryColor
-import io.legado.app.model.BookCover
-import io.legado.app.utils.GSON
-import io.legado.app.utils.toastOnUi
-import io.legado.app.utils.viewbindingdelegate.viewBinding
-import kotlinx.coroutines.Dispatchers.IO
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
-import splitties.views.onClick
-
-class CoverRuleConfigDialog : BaseBottomSheetDialogFragment(R.layout.dialog_cover_rule_config) {
-
-    val binding by viewBinding(DialogCoverRuleConfigBinding::bind)
-
-    override fun onStart() {
-        super.onStart()
-
-    }
-
-    override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
-        //binding.toolBar.setBackgroundColor(primaryColor)
-        initData()
-        binding.tvCancel.onClick {
-            dismissAllowingStateLoss()
-        }
-        binding.tvOk.onClick {
-            val enable = binding.cbEnable.isChecked
-            val searchUrl = binding.editSearchUrl.text?.toString()
-            val coverRule = binding.editCoverUrlRule.text?.toString()
-            if (searchUrl.isNullOrBlank() || coverRule.isNullOrBlank()) {
-                toastOnUi("搜索url和cover规则不能为空")
-            } else {
-                BookCover.CoverRule(enable, searchUrl, coverRule).let { config ->
-                    BookCover.saveCoverRule(config)
-                }
-                dismissAllowingStateLoss()
-            }
-        }
-        binding.tvFooterLeft.onClick {
-            BookCover.delCoverRule()
-            dismissAllowingStateLoss()
-        }
-    }
-
-    private fun initData() {
-        lifecycleScope.launch {
-            val rule = withContext(IO) {
-                BookCover.getCoverRule()
-            }
-            Log.e("coverRule", GSON.toJson(rule))
-            binding.cbEnable.isChecked = rule.enable
-            binding.editSearchUrl.setText(rule.searchUrl)
-            binding.editCoverUrlRule.setText(rule.coverRule)
-        }
-    }
-
-}

+ 0 - 128
app/src/main/java/io/legado/app/ui/config/DirectLinkUploadConfig.kt

@@ -1,128 +0,0 @@
-package io.legado.app.ui.config
-
-import android.os.Bundle
-import android.view.MenuItem
-import android.view.View
-import androidx.appcompat.widget.Toolbar
-import io.legado.app.R
-import io.legado.app.base.BaseBottomSheetDialogFragment
-import io.legado.app.databinding.DialogDirectLinkUploadConfigBinding
-import io.legado.app.help.DirectLinkUpload
-import io.legado.app.lib.dialogs.alert
-import io.legado.app.lib.dialogs.selector
-//import io.legado.app.lib.theme.primaryColor
-import io.legado.app.utils.GSON
-import io.legado.app.utils.applyTint
-import io.legado.app.utils.fromJsonObject
-import io.legado.app.utils.getClipText
-import io.legado.app.utils.sendToClip
-import io.legado.app.utils.toastOnUi
-import io.legado.app.utils.viewbindingdelegate.viewBinding
-import splitties.init.appCtx
-import splitties.views.onClick
-
-class DirectLinkUploadConfig : BaseBottomSheetDialogFragment(R.layout.dialog_direct_link_upload_config),
-    Toolbar.OnMenuItemClickListener {
-
-    private val binding by viewBinding(DialogDirectLinkUploadConfigBinding::bind)
-
-    override fun onStart() {
-        super.onStart()
-        //setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
-    }
-
-    override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
-        //binding.toolBar.setBackgroundColor(primaryColor)
-        binding.toolBar.inflateMenu(R.menu.direct_link_upload_config)
-        binding.toolBar.menu.applyTint(requireContext())
-        binding.toolBar.setOnMenuItemClickListener(this)
-        upView(DirectLinkUpload.getRule())
-        binding.tvCancel.onClick {
-            dismiss()
-        }
-        binding.tvFooterLeft.onClick {
-            test()
-        }
-        binding.tvOk.onClick {
-            getRule()?.let { rule ->
-                DirectLinkUpload.putConfig(rule)
-                dismiss()
-            }
-        }
-    }
-
-    override fun onMenuItemClick(item: MenuItem?): Boolean {
-        when (item?.itemId) {
-            R.id.menu_import_default -> importDefault()
-            R.id.menu_copy_rule -> getRule()?.let { rule ->
-                requireContext().sendToClip(GSON.toJson(rule))
-            }
-
-            R.id.menu_paste_rule -> runCatching {
-                requireContext().getClipText()!!.let {
-                    val rule = GSON.fromJsonObject<DirectLinkUpload.Rule>(it).getOrThrow()
-                    upView(rule)
-                }
-            }.onFailure {
-                toastOnUi("剪贴板为空或格式不对")
-            }
-        }
-        return true
-    }
-
-    private fun upView(rule: DirectLinkUpload.Rule) {
-        binding.editUploadUrl.setText(rule.uploadUrl)
-        binding.editDownloadUrlRule.setText(rule.downloadUrlRule)
-        binding.editSummary.setText(rule.summary)
-        binding.cbCompress.isChecked = rule.compress
-    }
-
-    private fun getRule(): DirectLinkUpload.Rule? {
-        val uploadUrl = binding.editUploadUrl.text?.toString()
-        val downloadUrlRule = binding.editDownloadUrlRule.text?.toString()
-        val summary = binding.editSummary.text?.toString()
-        val compress = binding.cbCompress.isChecked
-        if (uploadUrl.isNullOrBlank()) {
-            toastOnUi("上传Url不能为空")
-            return null
-        }
-        if (downloadUrlRule.isNullOrBlank()) {
-            toastOnUi("下载Url规则不能为空")
-            return null
-        }
-        if (summary.isNullOrBlank()) {
-            toastOnUi("注释不能为空")
-            return null
-        }
-        return DirectLinkUpload.Rule(uploadUrl, downloadUrlRule, summary, compress)
-    }
-
-    private fun importDefault() {
-        requireContext().selector(DirectLinkUpload.defaultRules) { _, rule, _ ->
-            upView(rule)
-        }
-    }
-
-    private fun test() {
-        val rule = getRule() ?: return
-        execute {
-            DirectLinkUpload.upLoad("test.json", "{}", "application/json", rule)
-        }.onError {
-            alertTestResult(it.localizedMessage ?: "ERROR")
-        }.onSuccess { result ->
-            alertTestResult(result)
-        }
-    }
-
-    private fun alertTestResult(result: String) {
-        alert {
-            setTitle("result")
-            setMessage(result)
-            okButton()
-            negativeButton(R.string.copy_text) {
-                appCtx.sendToClip(result)
-            }
-        }
-    }
-
-}

+ 27 - 64
app/src/main/java/io/legado/app/ui/config/coverConfig/CoverRuleConfigSheet.kt

@@ -2,105 +2,56 @@ package io.legado.app.ui.config.coverConfig
 
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.verticalScroll
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.Save
 import androidx.compose.material.icons.filled.SettingsBackupRestore
-import androidx.compose.material3.Button
-import androidx.compose.material3.Checkbox
 import androidx.compose.material3.ExperimentalMaterial3Api
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.OutlinedButton
-import androidx.compose.material3.TextButton
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
-import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.dp
 import io.legado.app.R
+import io.legado.app.help.DefaultData
 import io.legado.app.model.BookCover
-import io.legado.app.ui.theme.LegadoTheme
 import io.legado.app.ui.widget.components.AppTextField
 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.checkBox.CheckboxItem
 import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
-import io.legado.app.ui.widget.components.text.AppText
 import io.legado.app.utils.toastOnUi
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.withContext
 import splitties.init.appCtx
 
-private const val DEFAULT_SEARCH_URL = "data:;base64,{{java.base64Encode(key)}},{\"type\":\"lyc\"}"
-private val DEFAULT_COVER_RULE = """
-    @js:
-    var key = java.hexDecodeToString(result);
-    var url1 = `https://pre-api.tuishujun.com/api/searchBook?search_value=${'$'}{key}&page=1&pageSize=20`;
-    var url2 = `http://m.ypshuo.com/api/novel/search?keyword=${'$'}{key}&searchType=1&page=1`;
-    var [rr1, rr2] = java.ajaxAll([url1, url2]).map(r => r.body());
-    function jjson(str, rule) {
-        try {
-            return com.jayway.jsonpath.JsonPath.read(str, rule);
-        } catch (e) {
-            return [];
-        }
-    }
-    rr1 = jjson(rr1, '$.data.data[*]');
-    rr2 = jjson(rr2, '$.data.data[*]');
-    var na = String(book.name),
-        au = String(book.author);
-    function search() {
-        for (let char of rr1) {
-            //本地书名包含搜索结果书名
-            if (na.includes(char.title + '')) {
-                let au2 = char.author_nickname + '';
-                //作者匹配
-                if (au.includes(au2) || au2.includes(au)) {
-                    return char.cover;
-                }
-            }
-        }
-        for (let char of rr2) {
-            if (na.includes(char.novel_name + '')) {
-                let au2 = char.author_name + '';
-                if (au.includes(au2) || au2.includes(au)) {
-                    return char.novel_img;
-                }
-            }
-        }
-        return '';
-    }
-    search()
-""".trimIndent()
-
 @OptIn(ExperimentalMaterial3Api::class)
 @Composable
 fun CoverRuleConfigSheet(
     show: Boolean,
     onDismissRequest: () -> Unit
 ) {
+    var ruleState by remember { mutableStateOf<BookCover.CoverRule?>(null) }
     var enable by remember { mutableStateOf(false) }
     var searchUrl by remember { mutableStateOf("") }
     var coverRule by remember { mutableStateOf("") }
 
-    LaunchedEffect(Unit) {
-        val rule = withContext(Dispatchers.IO) {
-            BookCover.getCoverRule()
+    LaunchedEffect(show) {
+        if (show) {
+            val rule = withContext(Dispatchers.IO) {
+                BookCover.getCoverRule()
+            }
+            ruleState = rule
+            enable = rule.enable
+            searchUrl = rule.searchUrl
+            coverRule = rule.coverRule
         }
-        enable = rule.enable
-        searchUrl = rule.searchUrl
-        coverRule = rule.coverRule
     }
 
     AppModalBottomSheet(
@@ -111,8 +62,11 @@ fun CoverRuleConfigSheet(
             MediumIconButton(
                 imageVector = Icons.Default.SettingsBackupRestore,
                 onClick = {
-                    searchUrl = DEFAULT_SEARCH_URL
-                    coverRule = DEFAULT_COVER_RULE
+                    DefaultData.coverRule.let {
+                        enable = it.enable
+                        searchUrl = it.searchUrl
+                        coverRule = it.coverRule
+                    }
                     appCtx.toastOnUi(R.string.restore_default)
                 }
             )
@@ -124,8 +78,16 @@ fun CoverRuleConfigSheet(
                     if (searchUrl.isBlank() || coverRule.isBlank()) {
                         appCtx.toastOnUi("搜索url和cover规则不能为空")
                     } else {
-                        BookCover.CoverRule(enable, searchUrl, coverRule).let { config ->
-                            BookCover.saveCoverRule(config)
+                        val newConfig = ruleState?.copy(
+                            enable = enable,
+                            searchUrl = searchUrl,
+                            coverRule = coverRule
+                        ) ?: BookCover.CoverRule(enable, searchUrl, coverRule)
+
+                        if (newConfig == DefaultData.coverRule) {
+                            BookCover.delCoverRule()
+                        } else {
+                            BookCover.saveCoverRule(newConfig)
                         }
                         onDismissRequest()
                     }
@@ -136,6 +98,7 @@ fun CoverRuleConfigSheet(
         Column(
             modifier = Modifier
                 .fillMaxWidth()
+                .padding(bottom = 24.dp)
                 .verticalScroll(rememberScrollState()),
             verticalArrangement = Arrangement.spacedBy(8.dp)
         ) {

+ 5 - 5
app/src/main/java/io/legado/app/ui/main/bookshelf/BookItem.kt

@@ -50,9 +50,9 @@ import io.legado.app.ui.config.themeConfig.ThemeConfig
 import io.legado.app.ui.theme.LegadoTheme
 import io.legado.app.ui.widget.components.card.NormalCard
 import io.legado.app.ui.widget.components.card.TextCard
-import io.legado.app.ui.widget.components.cover.BookshelfCover
-import io.legado.app.ui.widget.components.cover.CoilBookCover
 import io.legado.app.ui.widget.components.icon.AppIcon
+import io.legado.app.ui.widget.components.image.cover.BookshelfCover
+import io.legado.app.ui.widget.components.image.cover.CoilBookCover
 import io.legado.app.ui.widget.components.text.AppText
 import io.legado.app.utils.toTimeAgo
 
@@ -214,7 +214,7 @@ fun BookshelfItem(
                     Column(
                         modifier = Modifier
                             .weight(1f)
-                            .padding(top = 4.dp, bottom = 4.dp, end = 4.dp),
+                            .padding(top = 4.dp, bottom = 4.dp, end = 8.dp, start = 4.dp),
                         verticalArrangement = Arrangement.Center
                     ) {
                     Row(
@@ -232,7 +232,7 @@ fun BookshelfItem(
                             modifier = Modifier.weight(1f)
                         )
                         titleEnd?.let {
-                            Box(modifier = Modifier.padding(top = 4.dp)) {
+                            Box(modifier = Modifier.padding(top = 4.dp, start = 4.dp)) {
                                 it.invoke()
                             }
                         }
@@ -523,7 +523,7 @@ fun BookGroupItemHorizontalCovers(
         ) {
             Column(
                 modifier = Modifier
-                    .padding(vertical = 4.dp),
+                    .padding(vertical = 8.dp, horizontal = 4.dp),
                 verticalArrangement = Arrangement.spacedBy(8.dp)
             ) {
                 Row(

+ 3 - 6
app/src/main/java/io/legado/app/ui/main/explore/ExploreScreen.kt

@@ -29,7 +29,6 @@ import androidx.compose.material.icons.filled.Refresh
 import androidx.compose.material.icons.filled.Search
 import androidx.compose.material.icons.filled.VerticalAlignTop
 import androidx.compose.material3.ExperimentalMaterial3Api
-import io.legado.app.ui.widget.components.progressIndicator.AppCircularProgressIndicator
 import androidx.compose.material3.Icon
 import androidx.compose.material3.ListItem
 import androidx.compose.material3.ListItemDefaults
@@ -41,8 +40,8 @@ import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
-import androidx.compose.runtime.setValue
 import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.rotate
@@ -53,23 +52,22 @@ import androidx.compose.ui.unit.dp
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import io.legado.app.R
 import io.legado.app.data.entities.BookSourcePart
+import io.legado.app.domain.usecase.ExploreKindUiUseCase
 import io.legado.app.help.source.getExploreInfoMap
-import io.legado.app.ui.widget.components.explore.ExploreKindUiUseCase
 import io.legado.app.ui.book.search.SearchActivity
 import io.legado.app.ui.book.search.SearchScope
 import io.legado.app.ui.book.source.edit.BookSourceEditActivity
 import io.legado.app.ui.login.SourceLoginActivity
-import io.legado.app.ui.config.themeConfig.ThemeConfig
 import io.legado.app.ui.theme.LegadoTheme
 import io.legado.app.ui.theme.LegadoTheme.composeEngine
 import io.legado.app.ui.theme.ThemeResolver
 import io.legado.app.ui.theme.adaptiveContentPadding
+import io.legado.app.ui.widget.components.EmptyMessage
 import io.legado.app.ui.widget.components.alert.AppAlertDialog
 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.divider.PillHeaderDivider
 import io.legado.app.ui.widget.components.explore.ExploreKindMultiTypeItem
-import io.legado.app.ui.widget.components.EmptyMessage
 import io.legado.app.ui.widget.components.lazylist.FastScrollLazyColumn
 import io.legado.app.ui.widget.components.list.ListScaffold
 import io.legado.app.ui.widget.components.list.TopFloatingStickyItem
@@ -79,7 +77,6 @@ import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
 import io.legado.app.ui.widget.components.progressIndicator.AppContainedLoadingIndicator
 import io.legado.app.ui.widget.components.text.AppText
 import io.legado.app.utils.startActivity
-import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.launch
 import org.koin.androidx.compose.koinViewModel
 import org.koin.compose.koinInject

+ 1 - 1
app/src/main/java/io/legado/app/ui/main/explore/ExploreViewModel.kt

@@ -6,10 +6,10 @@ import io.legado.app.base.BaseViewModel
 import io.legado.app.data.entities.BookSourcePart
 import io.legado.app.data.entities.rule.ExploreKind
 import io.legado.app.data.repository.ExploreRepository
+import io.legado.app.domain.usecase.ExploreKindUiUseCase
 import io.legado.app.help.source.clearExploreKindsCache
 import io.legado.app.help.source.exploreKinds
 import io.legado.app.help.source.getExploreInfoMap
-import io.legado.app.ui.widget.components.explore.ExploreKindUiUseCase
 import io.legado.app.ui.widget.components.explore.calculateExploreKindRows
 import io.legado.app.ui.widget.components.list.ListUiState
 import kotlinx.collections.immutable.ImmutableList

+ 1 - 1
app/src/main/java/io/legado/app/ui/main/rss/RssScreen.kt

@@ -52,12 +52,12 @@ import io.legado.app.ui.rss.source.edit.RssSourceEditActivity
 import io.legado.app.ui.rss.source.manage.RssSourceActivity
 import io.legado.app.ui.theme.LegadoTheme
 import io.legado.app.ui.theme.adaptiveContentPadding
-import io.legado.app.ui.widget.components.SourceIcon
 import io.legado.app.ui.widget.components.alert.AppAlertDialog
 import io.legado.app.ui.widget.components.card.GlassCard
 import io.legado.app.ui.widget.components.divider.PillDivider
 import io.legado.app.ui.widget.components.divider.PillHeaderDivider
 import io.legado.app.ui.widget.components.icon.AppIcon
+import io.legado.app.ui.widget.components.image.sourceIcon.SourceIcon
 import io.legado.app.ui.widget.components.list.ListScaffold
 import io.legado.app.ui.widget.components.menuItem.MenuItemIcon
 import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu

+ 1 - 1
app/src/main/java/io/legado/app/ui/rss/article/RssArticlesCompose.kt

@@ -61,7 +61,7 @@ import io.legado.app.ui.theme.adaptiveContentPadding
 import io.legado.app.ui.widget.components.AppPullToRefresh
 import io.legado.app.ui.widget.components.EmptyMessage
 import io.legado.app.ui.widget.components.card.GlassCard
-import io.legado.app.ui.widget.components.cover.buildCoverImageRequest
+import io.legado.app.ui.widget.components.image.cover.buildCoverImageRequest
 import io.legado.app.utils.toastOnUi
 import kotlinx.coroutines.Dispatchers.IO
 import kotlinx.coroutines.flow.catch

+ 1 - 1
app/src/main/java/io/legado/app/ui/rss/favorites/RssFavoritesScreen.kt

@@ -33,11 +33,11 @@ import io.legado.app.data.entities.RssStar
 import io.legado.app.ui.theme.adaptiveContentPadding
 import io.legado.app.ui.widget.components.ActionItem
 import io.legado.app.ui.widget.components.EmptyMessage
-import io.legado.app.ui.widget.components.SourceIcon
 import io.legado.app.ui.widget.components.button.SmallIconButton
 import io.legado.app.ui.widget.components.card.SelectionItemCard
 import io.legado.app.ui.widget.components.dialog.TextListInputDialog
 import io.legado.app.ui.widget.components.divider.PillDivider
+import io.legado.app.ui.widget.components.image.sourceIcon.SourceIcon
 import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
 import io.legado.app.ui.widget.components.rules.RuleListScaffold
 import org.koin.androidx.compose.koinViewModel

+ 1 - 2
app/src/main/java/io/legado/app/ui/widget/CollapsibleHeader.kt → app/src/main/java/io/legado/app/ui/widget/components/CollapsibleHeader.kt

@@ -1,4 +1,4 @@
-package io.legado.app.ui.widget
+package io.legado.app.ui.widget.components
 
 import androidx.compose.animation.core.animateFloatAsState
 import androidx.compose.foundation.layout.Column
@@ -10,7 +10,6 @@ import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.ExpandMore
 import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
 import androidx.compose.material3.Icon
-import androidx.compose.material3.MaterialTheme
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Alignment

+ 0 - 40
app/src/main/java/io/legado/app/ui/widget/components/SectionHeader.kt

@@ -1,40 +0,0 @@
-package io.legado.app.ui.widget.components
-
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.PaddingValues
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.unit.dp
-
-@Composable
-fun SectionHeader(
-    // 左侧主标题内容
-    titleContent: @Composable () -> Unit,
-    // 右侧或副标题内容
-    detailContent: @Composable (() -> Unit)? = null,
-    // 主要内容的排列方式
-    verticalArrangement: Arrangement.Vertical = Arrangement.Center,
-    // 定义水平对齐方式
-    horizontalArrangement: Arrangement.Horizontal = Arrangement.SpaceBetween,
-    // 根容器的 Padding
-    contentPadding: PaddingValues = PaddingValues(horizontal = 16.dp, vertical = 8.dp)
-) {
-
-    Row(
-            modifier = Modifier
-                .fillMaxWidth()
-                .padding(contentPadding),
-            verticalAlignment = Alignment.CenterVertically,
-            horizontalArrangement = horizontalArrangement
-        ) {
-            titleContent()
-            detailContent?.let {
-                it()
-            }
-        }
-
-}

+ 8 - 1
app/src/main/java/io/legado/app/ui/widget/components/alert/AppAlertDialog.kt

@@ -144,6 +144,7 @@ fun <T> AppAlertDialog(
     onDismissRequest: () -> Unit,
     title: String? = null,
     text: String? = null,
+    textProvider: @Composable (T.() -> String)? = null,
     confirmText: String = "确定",
     onConfirm: ((T) -> Unit)? = null,
     dismissText: String = "取消",
@@ -159,11 +160,17 @@ fun <T> AppAlertDialog(
 
     val currentData = cachedData
     if (currentData != null) {
+        val currentText = text ?: textProvider?.invoke(currentData)
+        var cachedText by remember { mutableStateOf(currentText) }
+        if (currentText != null) {
+            cachedText = currentText
+        }
+
         AppAlertDialog(
             show = data != null,
             onDismissRequest = onDismissRequest,
             title = title,
-            text = text,
+            text = currentText ?: cachedText,
             modifier = modifier,
             confirmText = confirmText,
             onConfirm = onConfirm?.let { { it(currentData) } },

+ 1 - 1
app/src/main/java/io/legado/app/ui/widget/components/book/SearchBookItem.kt

@@ -33,7 +33,7 @@ import io.legado.app.data.entities.SearchBook
 import io.legado.app.domain.model.BookShelfState
 import io.legado.app.ui.theme.LegadoTheme
 import io.legado.app.ui.widget.components.card.TextCard
-import io.legado.app.ui.widget.components.cover.CoilBookCover
+import io.legado.app.ui.widget.components.image.cover.CoilBookCover
 import io.legado.app.ui.widget.components.text.AppText
 
 @OptIn(ExperimentalSharedTransitionApi::class)

+ 0 - 97
app/src/main/java/io/legado/app/ui/widget/components/cover/Cover.kt

@@ -1,97 +0,0 @@
-package io.legado.app.ui.widget.components.cover
-
-import androidx.compose.foundation.background
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.RowScope
-import androidx.compose.foundation.layout.aspectRatio
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Book
-import androidx.compose.material3.Icon
-import androidx.compose.material3.Surface
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.clip
-import androidx.compose.ui.layout.ContentScale
-import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.unit.dp
-import coil.ImageLoader
-import coil.compose.AsyncImage
-import io.legado.app.ui.theme.LegadoTheme
-import org.koin.compose.koinInject
-
-@Composable
-fun Cover(
-    path: Any?,
-    modifier: Modifier = Modifier.width(48.dp),
-    badgeContent: (@Composable RowScope.() -> Unit)? = null,
-    loadOnlyWifi: Boolean = false,
-    sourceOrigin: String? = null,
-    imageLoader: ImageLoader = koinInject(),
-    onLoadFinish: (() -> Unit)? = null
-) {
-    val context = LocalContext.current
-
-    val boxModifier = modifier
-        .aspectRatio(3f / 4f)
-        .clip(RoundedCornerShape(4.dp))
-        .background(LegadoTheme.colorScheme.surfaceContainer)
-
-    Box(modifier = boxModifier) {
-        if (path == null) {
-            Icon(
-                Icons.Default.Book,
-                contentDescription = null,
-                tint = LegadoTheme.colorScheme.surfaceContainerHighest,
-                modifier = Modifier
-                    .size(24.dp)
-                    .align(Alignment.Center)
-            )
-        } else {
-            AsyncImage(
-                model = buildCoverImageRequest(
-                    context = context,
-                    data = path,
-                    sourceOrigin = sourceOrigin,
-                    loadOnlyWifi = loadOnlyWifi,
-                    crossfade = true,
-                ) {
-                    listener(
-                        onSuccess = { _, _ -> onLoadFinish?.invoke() },
-                        onError = { _, _ -> onLoadFinish?.invoke() }
-                    )
-                },
-                imageLoader = imageLoader,
-                contentDescription = null,
-                contentScale = ContentScale.Crop,
-                modifier = Modifier.fillMaxSize()
-            )
-        }
-
-        if (badgeContent != null) {
-            Surface(
-                modifier = Modifier
-                    .align(Alignment.TopEnd)
-                    .padding(2.dp),
-                shape = RoundedCornerShape(4.dp),
-                color = LegadoTheme.colorScheme.secondaryContainer,
-                contentColor = LegadoTheme.colorScheme.onSecondaryContainer,
-                tonalElevation = 2.dp
-            ) {
-                Row(
-                    modifier = Modifier.padding(2.dp),
-                    verticalAlignment = Alignment.CenterVertically,
-                    horizontalArrangement = Arrangement.Center,
-                    content = badgeContent
-                )
-            }
-        }
-    }
-}

+ 4 - 12
app/src/main/java/io/legado/app/ui/widget/components/explore/ExploreKindMultiTypeItem.kt

@@ -10,13 +10,9 @@ import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.foundation.text.BasicTextField
 import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.automirrored.filled.ArrowRight
 import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
-import androidx.compose.material.icons.automirrored.filled.OpenInNew
-import androidx.compose.material.icons.filled.KeyboardArrowDown
 import androidx.compose.material.icons.filled.Refresh
 import androidx.compose.material.icons.filled.UnfoldMore
-import androidx.compose.material3.Icon
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.LaunchedEffect
@@ -25,32 +21,28 @@ import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.runtime.setValue
-import androidx.compose.ui.platform.LocalLayoutDirection
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.clip
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.platform.LocalLayoutDirection
 import androidx.compose.ui.text.style.TextOverflow
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
-import io.legado.app.App
 import io.legado.app.data.entities.rule.ExploreKind
-import io.legado.app.ui.widget.components.explore.ExploreKindUiUseCase
+import io.legado.app.domain.usecase.ExploreKindUiUseCase
 import io.legado.app.help.source.getExploreInfoMap
-import io.legado.app.ui.config.themeConfig.ThemeConfig
+import io.legado.app.ui.theme.LegadoTheme
+import io.legado.app.ui.widget.components.icon.AppIcon
 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.text.AppText
 import io.legado.app.ui.widget.dialog.TextDialog
-import io.legado.app.ui.theme.LegadoTheme
-import io.legado.app.ui.widget.components.icon.AppIcon
 import io.legado.app.utils.showDialogFragment
 import kotlinx.coroutines.Dispatchers.IO
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.launch
-import top.yukonga.miuix.kmp.icon.MiuixIcons
-import top.yukonga.miuix.kmp.icon.basic.ArrowUpDown
 
 @Composable
 fun ExploreKindMultiTypeItem(

+ 2 - 2
app/src/main/java/io/legado/app/ui/widget/components/cover/BookshelfCover.kt → app/src/main/java/io/legado/app/ui/widget/components/image/cover/BookshelfCover.kt

@@ -1,4 +1,4 @@
-package io.legado.app.ui.widget.components.cover
+package io.legado.app.ui.widget.components.image.cover
 
 import androidx.compose.animation.AnimatedVisibilityScope
 import androidx.compose.animation.ExperimentalSharedTransitionApi
@@ -14,8 +14,8 @@ import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.unit.dp
 import io.legado.app.ui.theme.LegadoTheme
-import io.legado.app.ui.widget.components.progressIndicator.AppLinearProgressIndicator
 import io.legado.app.ui.widget.components.card.TextCard
+import io.legado.app.ui.widget.components.progressIndicator.AppLinearProgressIndicator
 
 @OptIn(ExperimentalSharedTransitionApi::class)
 @Composable

+ 7 - 4
app/src/main/java/io/legado/app/ui/widget/components/cover/CoilBookCover.kt → app/src/main/java/io/legado/app/ui/widget/components/image/cover/CoilBookCover.kt

@@ -1,4 +1,4 @@
-package io.legado.app.ui.widget.components.cover
+package io.legado.app.ui.widget.components.image.cover
 
 import android.graphics.Paint
 import android.graphics.Typeface
@@ -15,7 +15,6 @@ 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
 import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.material.icons.Icons
@@ -122,7 +121,9 @@ fun CoilBookCover(
                     contentDescription = null,
                     imageLoader = koinInject(),
                     contentScale = ContentScale.Crop,
-                    modifier = Modifier.fillMaxSize()
+                    modifier = Modifier
+                        .fillMaxSize()
+                        .clip(RoundedCornerShape(4.dp))
                 )
             }
 
@@ -138,7 +139,9 @@ fun CoilBookCover(
                     contentDescription = null,
                     imageLoader = koinInject(),
                     contentScale = ContentScale.Crop,
-                    modifier = Modifier.fillMaxSize(),
+                    modifier = Modifier
+                        .fillMaxSize()
+                        .clip(RoundedCornerShape(4.dp)),
                     onSuccess = {
                         isOnlineCoverLoaded = true
                         onLoadFinish?.invoke()

+ 1 - 1
app/src/main/java/io/legado/app/ui/widget/components/cover/CoverImageRequest.kt → app/src/main/java/io/legado/app/ui/widget/components/image/cover/CoverImageRequest.kt

@@ -1,4 +1,4 @@
-package io.legado.app.ui.widget.components.cover
+package io.legado.app.ui.widget.components.image.cover
 
 import android.content.Context
 import coil.request.ImageRequest

+ 1 - 1
app/src/main/java/io/legado/app/ui/widget/components/SourceIcon.kt → app/src/main/java/io/legado/app/ui/widget/components/image/sourceIcon/SourceIcon.kt

@@ -1,4 +1,4 @@
-package io.legado.app.ui.widget.components
+package io.legado.app.ui.widget.components.image.sourceIcon
 
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize

+ 1 - 0
app/src/main/java/io/legado/app/ui/widget/components/tabRow/AppTabRow.kt

@@ -87,6 +87,7 @@ private fun AppTab(
                 text = title,
                 maxLines = 1,
                 overflow = TextOverflow.Ellipsis,
+                style = LegadoTheme.typography.labelLargeEmphasized,
                 modifier = Modifier.padding(horizontal = 8.dp),
                 color = if (selected) LegadoTheme.colorScheme.primary else LegadoTheme.colorScheme.onSurfaceVariant
             )