[feat] Comic Tags

This commit is contained in:
acite
2025-09-07 13:09:25 +08:00
parent 514e99d7db
commit aacd226260
7 changed files with 243 additions and 53 deletions

View File

@@ -5,5 +5,6 @@ data class ComicResponse(
val page_count: Int,
val bookmarks: List<BookMark>,
val list: List<String>,
val tags: List<String>,
val author: String
)

View File

@@ -56,27 +56,44 @@ fun ComicGridView(comicId: String, navController: NavHostController, comicGridVi
.background(Color.White.copy(alpha = 0.65f), shape = RoundedCornerShape(12.dp))
)
{
Column {
Text(
text = comic!!.comic.comic_name,
fontSize = 18.sp,
fontWeight = FontWeight.Bold,
color = Color.Black,
maxLines = 1,
modifier = Modifier.padding(4.dp)
)
Text(
text = comic!!.comic.author,
fontSize = 16.sp,
fontWeight = FontWeight.Bold,
color = Color.Black,
maxLines = 1,
modifier = Modifier.padding(4.dp).fillMaxWidth()
)
}
Text(
text = comic!!.comic.comic_name,
fontSize = 18.sp,
fontWeight = FontWeight.Bold,
color = Color.Black,
maxLines = 1,
modifier = Modifier.padding(4.dp)
)
}
Box(
Modifier
.padding(horizontal = 16.dp).padding(top = 4.dp)
.background(Color.White.copy(alpha = 0.65f), shape = RoundedCornerShape(12.dp))
) {
Text(
text = comic!!.comic.author,
fontSize = 16.sp,
fontWeight = FontWeight.Bold,
color = Color.Black,
maxLines = 1,
modifier = Modifier.padding(4.dp)
)
}
Box(
Modifier
.padding(horizontal = 16.dp).padding(top = 4.dp)
.background(Color.White.copy(alpha = 0.65f), shape = RoundedCornerShape(12.dp))
) {
Text(
text = "Tags : ${comic!!.comic.tags.joinToString(", ")}",
fontSize = 16.sp,
fontWeight = FontWeight.Bold,
color = Color.Black,
maxLines = 5,
modifier = Modifier.padding(4.dp)
)
}
LazyColumn(modifier = Modifier.fillMaxWidth().weight(1f).padding(top = 6.dp).clip(RoundedCornerShape(6.dp)))
{
items(comicGridViewModel.chapterList)

View File

@@ -9,6 +9,7 @@ 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.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
@@ -224,14 +225,14 @@ fun ComicPageView(comicId: String, page: String, navController: NavHostControll
Card(
shape = RoundedCornerShape(12.dp),
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight()
.wrapContentHeight()
.padding(horizontal = 6.dp).padding(vertical = 6.dp),
onClick = {
pagerState.requestScrollToPage(page = r)
}
){
Box(Modifier.fillMaxSize())
Box()
{
AsyncImage(
model = ImageRequest.Builder(LocalContext.current)
@@ -242,7 +243,7 @@ fun ComicPageView(comicId: String, page: String, navController: NavHostControll
contentDescription = null,
imageLoader = comicPageViewModel.imageLoader!!,
modifier = Modifier
.fillMaxSize()
.fillMaxHeight()
.clip(RoundedCornerShape(12.dp))
.align(Alignment.Center),
contentScale = ContentScale.Fit,
@@ -260,7 +261,7 @@ fun ComicPageView(comicId: String, page: String, navController: NavHostControll
fontWeight = FontWeight.Bold,
color = Color.White,
maxLines = 1,
modifier = Modifier.padding(4.dp).align(Alignment.CenterVertically)
modifier = Modifier.padding(2.dp).widthIn(max = 100.dp).align(Alignment.CenterVertically)
)
Text(
@@ -269,7 +270,7 @@ fun ComicPageView(comicId: String, page: String, navController: NavHostControll
fontWeight = FontWeight.Bold,
color = Color.White,
maxLines = 1,
modifier = Modifier.padding(4.dp).fillMaxWidth().align(Alignment.CenterVertically)
modifier = Modifier.padding(2.dp).align(Alignment.CenterVertically)
)
}
}

View File

@@ -1,6 +1,8 @@
package com.acitelight.aether.view
import android.nfc.Tag
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -12,13 +14,17 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Card
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Tab
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@@ -35,11 +41,16 @@ import coil3.compose.AsyncImage
import com.acitelight.aether.model.Video
import com.acitelight.aether.viewModel.VideoScreenViewModel
import androidx.compose.material3.ScrollableTabRow
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Alignment
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.layout.Placeable
import androidx.compose.ui.modifier.modifierLocalOf
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.Dp
import androidx.navigation.NavHostController
import coil3.request.ImageRequest
import com.acitelight.aether.Global
@@ -48,40 +59,166 @@ import com.acitelight.aether.viewModel.ComicScreenViewModel
import java.nio.charset.Charset
@Composable
fun ComicScreen(navController: NavHostController, comicScreenViewModel: ComicScreenViewModel = viewModel())
{
comicScreenViewModel.SetupClient()
fun VariableGrid(
modifier: Modifier = Modifier,
rowHeight: Dp,
horizontalSpacing: Dp = 4.dp,
verticalSpacing: Dp = 4.dp,
content: @Composable () -> Unit
) {
val scrollState = rememberScrollState()
LazyVerticalGrid(
columns = GridCells.Adaptive(128.dp),
contentPadding = PaddingValues(8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp)
)
{
items(comicScreenViewModel.comics) { comic ->
ComicCard(comic, navController, comicScreenViewModel)
Layout(
modifier = modifier
.verticalScroll(scrollState), // ✅ 支持垂直滚动
content = content
) { measurables, constraints ->
val rowHeightPx = rowHeight.roundToPx()
val hSpacePx = horizontalSpacing.roundToPx()
val vSpacePx = verticalSpacing.roundToPx()
val placeables = measurables.map { measurable ->
measurable.measure(
constraints.copy(
minWidth = 0,
minHeight = rowHeightPx,
maxHeight = rowHeightPx
)
)
}
val rows = mutableListOf<List<Placeable>>()
var currentRow = mutableListOf<Placeable>()
var currentWidth = 0
val maxWidth = constraints.maxWidth
for (placeable in placeables) {
if (currentRow.isNotEmpty() && currentWidth + placeable.width + hSpacePx > maxWidth) {
rows.add(currentRow)
currentRow = mutableListOf()
currentWidth = 0
}
currentRow.add(placeable)
currentWidth += placeable.width + hSpacePx
}
if (currentRow.isNotEmpty()) {
rows.add(currentRow)
}
val layoutHeight = if (rows.isEmpty()) {
0
} else {
rows.size * rowHeightPx + (rows.size - 1) * vSpacePx
}
layout(
width = constraints.maxWidth.coerceAtLeast(constraints.minWidth),
height = layoutHeight.coerceAtLeast(constraints.minHeight)
) {
var y = 0
for (row in rows) {
var x = 0
for (placeable in row) {
placeable.placeRelative(x, y)
x += placeable.width + hSpacePx
}
y += rowHeightPx + vSpacePx
}
}
}
}
@Composable
fun ComicScreen(
navController: NavHostController,
comicScreenViewModel: ComicScreenViewModel = viewModel()
) {
comicScreenViewModel.SetupClient()
val included = comicScreenViewModel.included
Column {
VariableGrid(
modifier = Modifier
.heightIn(max = 120.dp)
.padding(8.dp),
rowHeight = 32.dp
)
{
for (i in comicScreenViewModel.tags) {
Box(
Modifier
.background(
if (included.contains(i)) Color.Green.copy(alpha = 0.65f) else Color.White.copy(
alpha = 0.65f
),
shape = RoundedCornerShape(4.dp)
)
.height(32.dp).widthIn(max = 72.dp)
.clickable {
if (included.contains(i))
included.remove(i)
else
included.add(i)
}
) {
Text(
text = i,
fontWeight = FontWeight.Bold,
fontSize = 16.sp,
maxLines = 1,
modifier = Modifier
.padding(2.dp)
.align(Alignment.Center),
color = Color.Black
)
}
}
}
HorizontalDivider(thickness = 1.5.dp)
LazyVerticalGrid(
columns = GridCells.Adaptive(128.dp),
contentPadding = PaddingValues(8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp)
)
{
items(comicScreenViewModel.comics.filter { x ->
included.all { y -> y in x.comic.tags } || included.isEmpty()
})
{ comic ->
ComicCard(comic, navController, comicScreenViewModel)
}
}
}
}
@Composable
fun ComicCard(comic: Comic, navController: NavHostController, comicScreenViewModel: ComicScreenViewModel) {
fun ComicCard(
comic: Comic,
navController: NavHostController,
comicScreenViewModel: ComicScreenViewModel
) {
Card(
shape = RoundedCornerShape(6.dp),
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight(),
onClick = {
val route = "comic_grid_route/${"${comic.id}".toHex() }"
val route = "comic_grid_route/${"${comic.id}".toHex()}"
navController.navigate(route)
}
) {
Column(
modifier = Modifier
.fillMaxWidth()
) {
Box(modifier = Modifier.fillMaxSize()){
) {
Box(modifier = Modifier.fillMaxSize()) {
AsyncImage(
model = ImageRequest.Builder(LocalContext.current)
.data(comic.getPage(0))
@@ -95,25 +232,30 @@ fun ComicCard(comic: Comic, navController: NavHostController, comicScreenViewMod
contentScale = ContentScale.Crop,
)
Box(
Modifier
.fillMaxWidth()
.height(24.dp)
.background( brush = Brush.verticalGradient(
colors = listOf(
Color.Transparent,
Color.Black.copy(alpha = 0.45f)
.background(
brush = Brush.verticalGradient(
colors = listOf(
Color.Transparent,
Color.Black.copy(alpha = 0.45f)
)
)
))
.align(Alignment.BottomCenter))
)
.align(Alignment.BottomCenter)
)
{
Text(
modifier = Modifier.align(Alignment.BottomEnd).padding(2.dp),
modifier = Modifier
.align(Alignment.BottomEnd)
.padding(2.dp),
fontSize = 12.sp,
text = "${comic.comic.list.size} Pages",
fontWeight = FontWeight.Bold,
color = Color.White)
color = Color.White
)
}
}
Text(
@@ -121,7 +263,10 @@ fun ComicCard(comic: Comic, navController: NavHostController, comicScreenViewMod
fontSize = 14.sp,
fontWeight = FontWeight.Bold,
maxLines = 2,
modifier = Modifier.padding(8.dp).background(Color.Transparent).heightIn(48.dp)
modifier = Modifier
.padding(8.dp)
.background(Color.Transparent)
.heightIn(48.dp)
)
}
}

View File

@@ -487,7 +487,6 @@ fun PortalCorePlayer(modifier: Modifier, videoPlayerViewModel: VideoPlayerViewMo
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 2.dp)
.align(Alignment.BottomCenter).background(
brush = Brush.verticalGradient(
colors = listOf(

View File

@@ -20,6 +20,28 @@ class ComicScreenViewModel : ViewModel() {
var imageLoader: ImageLoader? = null;
val comics = mutableStateListOf<Comic>()
val excluded = mutableStateListOf<String>()
val included = mutableStateListOf<String>()
val tags = mutableStateListOf<String>()
private val counter = mutableMapOf<String, Int>()
fun insertItem(newItem: String) {
val newCount = (counter[newItem] ?: 0) + 1
counter[newItem] = newCount
if (newItem !in tags) {
val insertIndex = tags.indexOfFirst { counter[it]!! < newCount }
.takeIf { it >= 0 } ?: tags.size
tags.add(insertIndex, newItem)
} else {
var currentIndex = tags.indexOf(newItem)
while (currentIndex > 0 && counter[tags[currentIndex - 1]]!! < newCount) {
tags[currentIndex] = tags[currentIndex - 1]
tags[currentIndex - 1] = newItem
currentIndex--
}
}
}
@Composable
fun SetupClient()
@@ -38,8 +60,13 @@ class ComicScreenViewModel : ViewModel() {
for(i in l)
{
val m = MediaManager.queryComicInfo(i)
if(m != null)
if(m != null) {
comics.add(m)
for(i in m.comic.tags)
{
insertItem(i)
}
}
}
}
}

View File

@@ -1,5 +1,5 @@
[versions]
agp = "8.12.1"
agp = "8.13.0"
bcprovJdk15on = "1.70"
bcprovJdk18on = "1.81"
coilCompose = "3.3.0"