一些优化

This commit is contained in:
HapeLee committed 2026-05-17 20:46:28 +08:00
1 parent da77e67286
commit f4ca57f972
39 files changed
+138 -1010

No files matched your search

@@ -1,5 +1,5 @@
{ {
"enable": false, "enable": true,
"searchUrl": "", "searchUrl": "data:;base64,{{java.base64Encode(key)}},{\"type\":\"lyc\"}",
"coverRule": "" "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 +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"
}
]
@@ -45,6 +45,7 @@ import io.legado.app.domain.usecase.CacheBookChaptersUseCase
import io.legado.app.domain.usecase.ChangeBookSourceUseCase import io.legado.app.domain.usecase.ChangeBookSourceUseCase
import io.legado.app.domain.usecase.ClearBookCacheUseCase import io.legado.app.domain.usecase.ClearBookCacheUseCase
import io.legado.app.domain.usecase.DeleteBooksUseCase 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.GetReadingProgressUseCase
import io.legado.app.domain.usecase.RemoveBookGroupAssignmentUseCase import io.legado.app.domain.usecase.RemoveBookGroupAssignmentUseCase
import io.legado.app.domain.usecase.ResolveBookShelfStateUseCase 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.read.ReadRssViewModel
import io.legado.app.ui.rss.source.manage.RssSourceViewModel import io.legado.app.ui.rss.source.manage.RssSourceViewModel
import io.legado.app.ui.rss.subscription.RuleSubViewModel 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.singleOf
import org.koin.core.module.dsl.viewModel import org.koin.core.module.dsl.viewModel
import org.koin.core.module.dsl.viewModelOf import org.koin.core.module.dsl.viewModelOf
@@ -1,4 +1,4 @@
package io.legado.app.ui.widget.components.explore package io.legado.app.domain.usecase
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.script.rhino.runScriptWithContext 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.help.source.getExploreInfoMap
import io.legado.app.ui.login.SourceLoginJsExtensions import io.legado.app.ui.login.SourceLoginJsExtensions
import io.legado.app.utils.InfoMap import io.legado.app.utils.InfoMap
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
@@ -76,7 +76,7 @@ class ExploreKindUiUseCase(
override fun reUiView() = onRefreshKinds() override fun reUiView() = onRefreshKinds()
} }
) )
withContext(IO) { withContext(Dispatchers.IO) {
evalButtonClick(actionText, source, effectiveInfoMap, title, sourceJsExtensions) evalButtonClick(actionText, source, effectiveInfoMap, title, sourceJsExtensions)
} }
} }
@@ -92,7 +92,7 @@ class ExploreKindUiUseCase(
private suspend fun getOrLoadBookSource(sourceUrl: String): BookSource? { private suspend fun getOrLoadBookSource(sourceUrl: String): BookSource? {
sourceCache[sourceUrl]?.let { return it } sourceCache[sourceUrl]?.let { return it }
return withContext(IO) { return withContext(Dispatchers.IO) {
bookSourceDao.getBookSource(sourceUrl) bookSourceDao.getBookSource(sourceUrl)
}?.also { source -> }?.also { source ->
sourceCache[sourceUrl] = source sourceCache[sourceUrl] = source
@@ -127,4 +127,4 @@ class ExploreKindUiUseCase(
AppLog.put("ExploreUI Button $name JavaScript error", e) AppLog.put("ExploreUI Button $name JavaScript error", e)
} }
} }
} }
@@ -61,6 +61,7 @@ import dev.chrisbanes.haze.HazeState
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
import io.legado.app.data.entities.SearchBook import io.legado.app.data.entities.SearchBook
import io.legado.app.domain.model.BookShelfState 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.main.bookCoverSharedElementKey
import io.legado.app.ui.theme.LegadoTheme import io.legado.app.ui.theme.LegadoTheme
import io.legado.app.ui.theme.ThemeResolver 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.button.AnimatedTextButton
import io.legado.app.ui.widget.components.card.TextCard import io.legado.app.ui.widget.components.card.TextCard
import io.legado.app.ui.widget.components.explore.ExploreKindMultiTypeItem 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.explore.calculateExploreKindRows
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
@@ -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.ConfirmDismissButtonsRow
import io.legado.app.ui.widget.components.button.MediumIconButton 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.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.modalBottomSheet.AppModalBottomSheet
import io.legado.app.ui.widget.components.settingItem.CompactDropdownSettingItem import io.legado.app.ui.widget.components.settingItem.CompactDropdownSettingItem
import io.legado.app.ui.widget.components.settingItem.CompactSwitchSettingItem import io.legado.app.ui.widget.components.settingItem.CompactSwitchSettingItem
@@ -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.alert.AppAlertDialog
import io.legado.app.ui.widget.components.card.GlassCard 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.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.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.log.AppLogSheet
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
@@ -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.GlassCard
import io.legado.app.ui.widget.components.card.SelectionItemCard 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.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.RoundDropdownMenu
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
@@ -50,7 +50,7 @@ import io.legado.app.model.analyzeRule.AnalyzeUrl
import io.legado.app.model.localBook.LocalBook import io.legado.app.model.localBook.LocalBook
import io.legado.app.model.webBook.WebBook import io.legado.app.model.webBook.WebBook
import io.legado.app.ui.config.coverConfig.CoverConfig 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.ArchiveUtils
import io.legado.app.utils.GSON import io.legado.app.utils.GSON
import io.legado.app.utils.ImageSaveUtils import io.legado.app.utils.ImageSaveUtils
@@ -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.AppTextField
import io.legado.app.ui.widget.components.alert.AppAlertDialog 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.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.RoundDropdownMenu
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
import io.legado.app.ui.widget.components.settingItem.SwitchSettingItem import io.legado.app.ui.widget.components.settingItem.SwitchSettingItem
@@ -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.ReorderableSelectionItem
import io.legado.app.ui.widget.components.card.SelectionItemCard 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.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.divider.PillDivider
import io.legado.app.ui.widget.components.filePicker.FilePickerSheet 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.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.ListScaffold
import io.legado.app.ui.widget.components.list.ListUiState import io.legado.app.ui.widget.components.list.ListUiState
import io.legado.app.ui.widget.components.log.AppLogSheet import io.legado.app.ui.widget.components.log.AppLogSheet
@@ -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.AppScaffold
import io.legado.app.ui.widget.components.button.MediumIconButton 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.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.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.tabRow.AppTabRow
import io.legado.app.ui.widget.components.text.AppText import io.legado.app.ui.widget.components.text.AppText
import io.legado.app.ui.widget.components.topbar.GlassMediumFlexibleTopAppBar import io.legado.app.ui.widget.components.topbar.GlassMediumFlexibleTopAppBar
@@ -66,8 +66,8 @@ import io.legado.app.ui.theme.LegadoTheme
import io.legado.app.ui.theme.adaptiveContentPaddingOnlyVertical import io.legado.app.ui.theme.adaptiveContentPaddingOnlyVertical
import io.legado.app.ui.theme.adaptiveHorizontalPadding import io.legado.app.ui.theme.adaptiveHorizontalPadding
import io.legado.app.ui.theme.fadingEdge 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.AppScaffold
import io.legado.app.ui.widget.components.CollapsibleHeader
import io.legado.app.ui.widget.components.EmptyMessage import io.legado.app.ui.widget.components.EmptyMessage
import io.legado.app.ui.widget.components.SearchBar import io.legado.app.ui.widget.components.SearchBar
import io.legado.app.ui.widget.components.alert.AppAlertDialog 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.GlassCard
import io.legado.app.ui.widget.components.card.TextCard import io.legado.app.ui.widget.components.card.TextCard
import io.legado.app.ui.widget.components.checkBox.CheckboxItem 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.HEATMAP_CALENDAR_TITLE
import io.legado.app.ui.widget.components.heatmap.HeatmapCalendarEndAction import io.legado.app.ui.widget.components.heatmap.HeatmapCalendarEndAction
import io.legado.app.ui.widget.components.heatmap.HeatmapCalendarStartAction 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.rememberDaysInRange
import io.legado.app.ui.widget.components.heatmap.rememberWeeks import io.legado.app.ui.widget.components.heatmap.rememberWeeks
import io.legado.app.ui.widget.components.icon.AppIcon 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.list.TopFloatingStickyItem
import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
import io.legado.app.ui.widget.components.swipe.SwipeAction import io.legado.app.ui.widget.components.swipe.SwipeAction
@@ -2,8 +2,20 @@ package io.legado.app.ui.book.readRecord.component
import androidx.compose.foundation.basicMarquee import androidx.compose.foundation.basicMarquee
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.Arrangement
import androidx.compose.runtime.* 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.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.text.buildAnnotatedString 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.ReadRecordViewModel
import io.legado.app.ui.book.readRecord.TimelineItem import io.legado.app.ui.book.readRecord.TimelineItem
import io.legado.app.ui.theme.LegadoTheme 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.ui.widget.components.text.AppText
import io.legado.app.utils.StringUtils.formatFriendlyDate import io.legado.app.utils.StringUtils.formatFriendlyDate
@@ -1,10 +1,21 @@
package io.legado.app.ui.book.readRecord.component package io.legado.app.ui.book.readRecord.component
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.* 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.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import io.legado.app.ui.book.readRecord.ReadRecordFormatter import io.legado.app.ui.book.readRecord.ReadRecordFormatter
import io.legado.app.ui.book.readRecord.ReadRecordUiState 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.LegadoTheme
import io.legado.app.ui.theme.adaptiveHorizontalPadding import io.legado.app.ui.theme.adaptiveHorizontalPadding
import io.legado.app.ui.widget.components.card.GlassCard 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 io.legado.app.ui.widget.components.text.AppText
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
@@ -14,7 +14,6 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items 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.Icons
import androidx.compose.material.icons.filled.Book import androidx.compose.material.icons.filled.Book
import androidx.compose.material.icons.filled.Close 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.Layers
import androidx.compose.material.icons.filled.PlayArrow import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.material.icons.filled.Stop import androidx.compose.material.icons.filled.Stop
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import io.legado.app.ui.widget.components.progressIndicator.AppCircularProgressIndicator
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults 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.Composable
import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
@@ -42,9 +36,9 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow import androidx.compose.runtime.snapshotFlow
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
@@ -58,6 +52,7 @@ import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import io.legado.app.R import io.legado.app.R
import io.legado.app.data.entities.SearchKeyword 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.main.bookshelf.BookShelfItem
import io.legado.app.ui.theme.LegadoTheme import io.legado.app.ui.theme.LegadoTheme
import io.legado.app.ui.theme.ThemeResolver 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.AppScaffold
import io.legado.app.ui.widget.components.SearchBar import io.legado.app.ui.widget.components.SearchBar
import io.legado.app.ui.widget.components.alert.AppAlertDialog 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.book.SearchBookListItem
import io.legado.app.ui.widget.components.button.SmallIconButton 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.button.SmallTextButton
import io.legado.app.ui.widget.components.card.NormalCard 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.card.SelectionItemCard
import io.legado.app.ui.widget.components.icon.AppIcon
import io.legado.app.ui.widget.components.icon.AppIcons
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.progressIndicator.AppCircularProgressIndicator
import io.legado.app.ui.widget.components.text.AppText
import io.legado.app.ui.widget.components.topbar.GlassMediumFlexibleTopAppBar
import io.legado.app.ui.widget.components.topbar.GlassTopAppBarDefaults
import io.legado.app.ui.widget.components.topbar.M3GlassScrollBehavior
import io.legado.app.ui.widget.components.topbar.TopBarActionButton 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.TopBarAnimatedActionButton
import io.legado.app.ui.widget.components.topbar.TopBarNavigationButton 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.text.AppText
import io.legado.app.ui.main.bookCoverSharedElementKey
import io.legado.app.utils.toastOnUi import io.legado.app.utils.toastOnUi
import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.debounce
@@ -480,19 +476,17 @@ fun SearchScreen(
viewModel.onIntent(SearchIntent.DismissEmptyScopeAction) viewModel.onIntent(SearchIntent.DismissEmptyScopeAction)
}, },
title = stringResource(R.string.draw), 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), confirmText = stringResource(R.string.ok),
onConfirm = { viewModel.onIntent(SearchIntent.ConfirmEmptyScopeAction) }, onConfirm = { viewModel.onIntent(SearchIntent.ConfirmEmptyScopeAction) },
dismissText = stringResource(R.string.cancel), dismissText = stringResource(R.string.cancel),
onDismiss = { viewModel.onIntent(SearchIntent.DismissEmptyScopeAction) }, 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( ScopeSelectSheet(
@@ -681,12 +675,8 @@ private fun SearchResultFooter(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp), horizontalArrangement = Arrangement.spacedBy(8.dp),
) { ) {
Box( AppCircularProgressIndicator()
modifier = Modifier.size(18.dp), Spacer(modifier = Modifier.padding(horizontal = 8.dp))
contentAlignment = Alignment.Center
) {
AppCircularProgressIndicator()
}
AppText(text = stringResource(R.string.is_loading)) AppText(text = stringResource(R.string.is_loading))
} }
} }
@@ -84,10 +84,10 @@ import io.legado.app.ui.replace.ReplaceEditRoute
import io.legado.app.ui.theme.LegadoTheme import io.legado.app.ui.theme.LegadoTheme
import io.legado.app.ui.theme.adaptiveContentPaddingOnlyVertical import io.legado.app.ui.theme.adaptiveContentPaddingOnlyVertical
import io.legado.app.ui.theme.adaptiveHorizontalPadding 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.ActionItem
import io.legado.app.ui.widget.components.AppFloatingActionButtonMenu import io.legado.app.ui.widget.components.AppFloatingActionButtonMenu
import io.legado.app.ui.widget.components.AppScaffold 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.EmptyMessage
import io.legado.app.ui.widget.components.FabMenuItem import io.legado.app.ui.widget.components.FabMenuItem
import io.legado.app.ui.widget.components.SelectionBottomBar import io.legado.app.ui.widget.components.SelectionBottomBar
@@ -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) {}
}
}
@@ -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)
}
}
@@ -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)
}
}
}
@@ -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)
}
}
}
}
@@ -2,105 +2,56 @@ package io.legado.app.ui.config.coverConfig
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column 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.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Save import androidx.compose.material.icons.filled.Save
import androidx.compose.material.icons.filled.SettingsBackupRestore 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.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.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import io.legado.app.R import io.legado.app.R
import io.legado.app.help.DefaultData
import io.legado.app.model.BookCover 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.AppTextField
import io.legado.app.ui.widget.components.button.MediumIconButton 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.checkBox.CheckboxItem
import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet 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 io.legado.app.utils.toastOnUi
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import splitties.init.appCtx 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) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun CoverRuleConfigSheet( fun CoverRuleConfigSheet(
show: Boolean, show: Boolean,
onDismissRequest: () -> Unit onDismissRequest: () -> Unit
) { ) {
var ruleState by remember { mutableStateOf<BookCover.CoverRule?>(null) }
var enable by remember { mutableStateOf(false) } var enable by remember { mutableStateOf(false) }
var searchUrl by remember { mutableStateOf("") } var searchUrl by remember { mutableStateOf("") }
var coverRule by remember { mutableStateOf("") } var coverRule by remember { mutableStateOf("") }
LaunchedEffect(Unit) { LaunchedEffect(show) {
val rule = withContext(Dispatchers.IO) { if (show) {
BookCover.getCoverRule() 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( AppModalBottomSheet(
@@ -111,8 +62,11 @@ fun CoverRuleConfigSheet(
MediumIconButton( MediumIconButton(
imageVector = Icons.Default.SettingsBackupRestore, imageVector = Icons.Default.SettingsBackupRestore,
onClick = { onClick = {
searchUrl = DEFAULT_SEARCH_URL DefaultData.coverRule.let {
coverRule = DEFAULT_COVER_RULE enable = it.enable
searchUrl = it.searchUrl
coverRule = it.coverRule
}
appCtx.toastOnUi(R.string.restore_default) appCtx.toastOnUi(R.string.restore_default)
} }
) )
@@ -124,8 +78,16 @@ fun CoverRuleConfigSheet(
if (searchUrl.isBlank() || coverRule.isBlank()) { if (searchUrl.isBlank() || coverRule.isBlank()) {
appCtx.toastOnUi("搜索url和cover规则不能为空") appCtx.toastOnUi("搜索url和cover规则不能为空")
} else { } else {
BookCover.CoverRule(enable, searchUrl, coverRule).let { config -> val newConfig = ruleState?.copy(
BookCover.saveCoverRule(config) enable = enable,
searchUrl = searchUrl,
coverRule = coverRule
) ?: BookCover.CoverRule(enable, searchUrl, coverRule)
if (newConfig == DefaultData.coverRule) {
BookCover.delCoverRule()
} else {
BookCover.saveCoverRule(newConfig)
} }
onDismissRequest() onDismissRequest()
} }
@@ -136,6 +98,7 @@ fun CoverRuleConfigSheet(
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(bottom = 24.dp)
.verticalScroll(rememberScrollState()), .verticalScroll(rememberScrollState()),
verticalArrangement = Arrangement.spacedBy(8.dp) verticalArrangement = Arrangement.spacedBy(8.dp)
) { ) {
@@ -50,9 +50,9 @@ import io.legado.app.ui.config.themeConfig.ThemeConfig
import io.legado.app.ui.theme.LegadoTheme import io.legado.app.ui.theme.LegadoTheme
import io.legado.app.ui.widget.components.card.NormalCard 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.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.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.ui.widget.components.text.AppText
import io.legado.app.utils.toTimeAgo import io.legado.app.utils.toTimeAgo
@@ -214,7 +214,7 @@ fun BookshelfItem(
Column( Column(
modifier = Modifier modifier = Modifier
.weight(1f) .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 verticalArrangement = Arrangement.Center
) { ) {
Row( Row(
@@ -232,7 +232,7 @@ fun BookshelfItem(
modifier = Modifier.weight(1f) modifier = Modifier.weight(1f)
) )
titleEnd?.let { titleEnd?.let {
Box(modifier = Modifier.padding(top = 4.dp)) { Box(modifier = Modifier.padding(top = 4.dp, start = 4.dp)) {
it.invoke() it.invoke()
} }
} }
@@ -523,7 +523,7 @@ fun BookGroupItemHorizontalCovers(
) { ) {
Column( Column(
modifier = Modifier modifier = Modifier
.padding(vertical = 4.dp), .padding(vertical = 8.dp, horizontal = 4.dp),
verticalArrangement = Arrangement.spacedBy(8.dp) verticalArrangement = Arrangement.spacedBy(8.dp)
) { ) {
Row( Row(
@@ -29,7 +29,6 @@ import androidx.compose.material.icons.filled.Refresh
import androidx.compose.material.icons.filled.Search import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.filled.VerticalAlignTop import androidx.compose.material.icons.filled.VerticalAlignTop
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import io.legado.app.ui.widget.components.progressIndicator.AppCircularProgressIndicator
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.ListItem import androidx.compose.material3.ListItem
import androidx.compose.material3.ListItemDefaults import androidx.compose.material3.ListItemDefaults
@@ -41,8 +40,8 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate import androidx.compose.ui.draw.rotate
@@ -53,23 +52,22 @@ import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import io.legado.app.R import io.legado.app.R
import io.legado.app.data.entities.BookSourcePart 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.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.SearchActivity
import io.legado.app.ui.book.search.SearchScope import io.legado.app.ui.book.search.SearchScope
import io.legado.app.ui.book.source.edit.BookSourceEditActivity import io.legado.app.ui.book.source.edit.BookSourceEditActivity
import io.legado.app.ui.login.SourceLoginActivity 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
import io.legado.app.ui.theme.LegadoTheme.composeEngine import io.legado.app.ui.theme.LegadoTheme.composeEngine
import io.legado.app.ui.theme.ThemeResolver import io.legado.app.ui.theme.ThemeResolver
import io.legado.app.ui.theme.adaptiveContentPadding 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.alert.AppAlertDialog
import io.legado.app.ui.widget.components.card.GlassCard 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.card.TextCard
import io.legado.app.ui.widget.components.divider.PillHeaderDivider 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.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.lazylist.FastScrollLazyColumn
import io.legado.app.ui.widget.components.list.ListScaffold import io.legado.app.ui.widget.components.list.ListScaffold
import io.legado.app.ui.widget.components.list.TopFloatingStickyItem 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.progressIndicator.AppContainedLoadingIndicator
import io.legado.app.ui.widget.components.text.AppText import io.legado.app.ui.widget.components.text.AppText
import io.legado.app.utils.startActivity import io.legado.app.utils.startActivity
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.koin.androidx.compose.koinViewModel import org.koin.androidx.compose.koinViewModel
import org.koin.compose.koinInject import org.koin.compose.koinInject
@@ -6,10 +6,10 @@ import io.legado.app.base.BaseViewModel
import io.legado.app.data.entities.BookSourcePart import io.legado.app.data.entities.BookSourcePart
import io.legado.app.data.entities.rule.ExploreKind import io.legado.app.data.entities.rule.ExploreKind
import io.legado.app.data.repository.ExploreRepository 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.clearExploreKindsCache
import io.legado.app.help.source.exploreKinds import io.legado.app.help.source.exploreKinds
import io.legado.app.help.source.getExploreInfoMap 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.explore.calculateExploreKindRows
import io.legado.app.ui.widget.components.list.ListUiState import io.legado.app.ui.widget.components.list.ListUiState
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
@@ -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.rss.source.manage.RssSourceActivity
import io.legado.app.ui.theme.LegadoTheme import io.legado.app.ui.theme.LegadoTheme
import io.legado.app.ui.theme.adaptiveContentPadding 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.alert.AppAlertDialog
import io.legado.app.ui.widget.components.card.GlassCard 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.PillDivider
import io.legado.app.ui.widget.components.divider.PillHeaderDivider 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.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.list.ListScaffold
import io.legado.app.ui.widget.components.menuItem.MenuItemIcon import io.legado.app.ui.widget.components.menuItem.MenuItemIcon
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu
@@ -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.AppPullToRefresh
import io.legado.app.ui.widget.components.EmptyMessage import io.legado.app.ui.widget.components.EmptyMessage
import io.legado.app.ui.widget.components.card.GlassCard 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 io.legado.app.utils.toastOnUi
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.catch
@@ -33,11 +33,11 @@ import io.legado.app.data.entities.RssStar
import io.legado.app.ui.theme.adaptiveContentPadding import io.legado.app.ui.theme.adaptiveContentPadding
import io.legado.app.ui.widget.components.ActionItem import io.legado.app.ui.widget.components.ActionItem
import io.legado.app.ui.widget.components.EmptyMessage 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.button.SmallIconButton
import io.legado.app.ui.widget.components.card.SelectionItemCard 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.dialog.TextListInputDialog
import io.legado.app.ui.widget.components.divider.PillDivider 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.menuItem.RoundDropdownMenuItem
import io.legado.app.ui.widget.components.rules.RuleListScaffold import io.legado.app.ui.widget.components.rules.RuleListScaffold
import org.koin.androidx.compose.koinViewModel import org.koin.androidx.compose.koinViewModel
@@ -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.animation.core.animateFloatAsState
import androidx.compose.foundation.layout.Column 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.material.icons.filled.ExpandMore
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
@@ -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()
}
}
}
@@ -144,6 +144,7 @@ fun <T> AppAlertDialog(
onDismissRequest: () -> Unit, onDismissRequest: () -> Unit,
title: String? = null, title: String? = null,
text: String? = null, text: String? = null,
textProvider: @Composable (T.() -> String)? = null,
confirmText: String = "确定", confirmText: String = "确定",
onConfirm: ((T) -> Unit)? = null, onConfirm: ((T) -> Unit)? = null,
dismissText: String = "取消", dismissText: String = "取消",
@@ -159,11 +160,17 @@ fun <T> AppAlertDialog(
val currentData = cachedData val currentData = cachedData
if (currentData != null) { if (currentData != null) {
val currentText = text ?: textProvider?.invoke(currentData)
var cachedText by remember { mutableStateOf(currentText) }
if (currentText != null) {
cachedText = currentText
}
AppAlertDialog( AppAlertDialog(
show = data != null, show = data != null,
onDismissRequest = onDismissRequest, onDismissRequest = onDismissRequest,
title = title, title = title,
text = text, text = currentText ?: cachedText,
modifier = modifier, modifier = modifier,
confirmText = confirmText, confirmText = confirmText,
onConfirm = onConfirm?.let { { it(currentData) } }, onConfirm = onConfirm?.let { { it(currentData) } },
@@ -33,7 +33,7 @@ import io.legado.app.data.entities.SearchBook
import io.legado.app.domain.model.BookShelfState import io.legado.app.domain.model.BookShelfState
import io.legado.app.ui.theme.LegadoTheme import io.legado.app.ui.theme.LegadoTheme
import io.legado.app.ui.widget.components.card.TextCard import io.legado.app.ui.widget.components.card.TextCard
import io.legado.app.ui.widget.components.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.ui.widget.components.text.AppText
@OptIn(ExperimentalSharedTransitionApi::class) @OptIn(ExperimentalSharedTransitionApi::class)
@@ -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
)
}
}
}
}
@@ -10,13 +10,9 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField import androidx.compose.foundation.text.BasicTextField
import androidx.compose.material.icons.Icons 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.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.Refresh
import androidx.compose.material.icons.filled.UnfoldMore import androidx.compose.material.icons.filled.UnfoldMore
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
@@ -25,32 +21,28 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import io.legado.app.App
import io.legado.app.data.entities.rule.ExploreKind 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.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.RoundDropdownMenu
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
import io.legado.app.ui.widget.components.text.AppText import io.legado.app.ui.widget.components.text.AppText
import io.legado.app.ui.widget.dialog.TextDialog 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 io.legado.app.utils.showDialogFragment
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import top.yukonga.miuix.kmp.icon.MiuixIcons
import top.yukonga.miuix.kmp.icon.basic.ArrowUpDown
@Composable @Composable
fun ExploreKindMultiTypeItem( fun ExploreKindMultiTypeItem(
@@ -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.AnimatedVisibilityScope
import androidx.compose.animation.ExperimentalSharedTransitionApi import androidx.compose.animation.ExperimentalSharedTransitionApi
@@ -14,8 +14,8 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import io.legado.app.ui.theme.LegadoTheme 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.card.TextCard
import io.legado.app.ui.widget.components.progressIndicator.AppLinearProgressIndicator
@OptIn(ExperimentalSharedTransitionApi::class) @OptIn(ExperimentalSharedTransitionApi::class)
@Composable @Composable
@@ -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.Paint
import android.graphics.Typeface import android.graphics.Typeface
@@ -15,7 +15,6 @@ import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
@@ -122,7 +121,9 @@ fun CoilBookCover(
contentDescription = null, contentDescription = null,
imageLoader = koinInject(), imageLoader = koinInject(),
contentScale = ContentScale.Crop, contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize() modifier = Modifier
.fillMaxSize()
.clip(RoundedCornerShape(4.dp))
) )
} }
@@ -138,7 +139,9 @@ fun CoilBookCover(
contentDescription = null, contentDescription = null,
imageLoader = koinInject(), imageLoader = koinInject(),
contentScale = ContentScale.Crop, contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize(), modifier = Modifier
.fillMaxSize()
.clip(RoundedCornerShape(4.dp)),
onSuccess = { onSuccess = {
isOnlineCoverLoaded = true isOnlineCoverLoaded = true
onLoadFinish?.invoke() onLoadFinish?.invoke()
@@ -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 android.content.Context
import coil.request.ImageRequest import coil.request.ImageRequest
@@ -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.Box
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
@@ -87,6 +87,7 @@ private fun AppTab(
text = title, text = title,
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
style = LegadoTheme.typography.labelLargeEmphasized,
modifier = Modifier.padding(horizontal = 8.dp), modifier = Modifier.padding(horizontal = 8.dp),
color = if (selected) LegadoTheme.colorScheme.primary else LegadoTheme.colorScheme.onSurfaceVariant color = if (selected) LegadoTheme.colorScheme.primary else LegadoTheme.colorScheme.onSurfaceVariant
) )