CICD: сборка APK на release → F-Droid; versionCode из тега; фикс скролла каталога и масштаба
build / Android APK to F-Droid (release) Failing after 36s
build / Android APK to F-Droid (release) Failing after 36s
This commit is contained in:
@@ -37,8 +37,8 @@ android {
|
||||
applicationId = "pw.binom.viewmate.phone"
|
||||
minSdk = 26
|
||||
targetSdk = 34
|
||||
versionCode = 1
|
||||
versionName = "0.1.0"
|
||||
versionCode = (project.findProperty("versionCode") as? String)?.toIntOrNull() ?: 1
|
||||
versionName = (project.findProperty("versionName") as? String) ?: "0.1.0"
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
// LLM-ключ из local.properties (llm.apiKey) — вне git; пустой = ещё не задан
|
||||
|
||||
@@ -14,6 +14,7 @@ import androidx.compose.foundation.layout.padding
|
||||
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.lazy.grid.rememberLazyGridState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Text
|
||||
@@ -26,6 +27,7 @@ import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.snapshotFlow
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
@@ -77,6 +79,13 @@ class CatalogState(
|
||||
private set
|
||||
var path by mutableStateOf<Path>(Path.Root)
|
||||
private set
|
||||
var scrollIndex by mutableStateOf(0)
|
||||
private set
|
||||
var scrollOffset by mutableStateOf(0)
|
||||
private set
|
||||
|
||||
/** Сохранённые позиции скролла по пути (ключ — путь, из которого ушли). */
|
||||
private val savedScroll = mutableMapOf<CatalogState.Path, Pair<Int, Int>>()
|
||||
|
||||
private var searchJob: Job? = null
|
||||
private var loadJob: Job? = null
|
||||
@@ -102,16 +111,19 @@ class CatalogState(
|
||||
}
|
||||
|
||||
fun openSeries(item: JellyfinItem) {
|
||||
savedScroll[path] = scrollIndex to scrollOffset
|
||||
path = Path.Series(item)
|
||||
refresh()
|
||||
}
|
||||
|
||||
fun openSeason(season: JellyfinItem, series: JellyfinItem) {
|
||||
savedScroll[path] = scrollIndex to scrollOffset
|
||||
path = Path.Season(season, series)
|
||||
refresh()
|
||||
}
|
||||
|
||||
fun openFolder(item: JellyfinItem) {
|
||||
savedScroll[path] = scrollIndex to scrollOffset
|
||||
path = Path.Folder(item)
|
||||
refresh()
|
||||
}
|
||||
@@ -123,9 +135,27 @@ class CatalogState(
|
||||
is Path.Season -> Path.Series(p.series)
|
||||
is Path.Folder -> Path.Root
|
||||
}
|
||||
val (idx, off) = savedScroll[path] ?: Pair(0, 0)
|
||||
scrollIndex = idx
|
||||
scrollOffset = off
|
||||
refresh()
|
||||
}
|
||||
|
||||
/** Сохранить позицию для текущего пути (перед уходом на детали и т.п.). */
|
||||
fun saveScrollNow() {
|
||||
savedScroll[path] = scrollIndex to scrollOffset
|
||||
}
|
||||
|
||||
/** Сохранить текущую позицию (вызывается при скролле из UI). */
|
||||
fun setScroll(index: Int, offset: Int) {
|
||||
scrollIndex = index
|
||||
scrollOffset = offset
|
||||
}
|
||||
|
||||
/** Позиция скролла для текущего пути (восстановление после навигации). */
|
||||
fun savedScrollForCurrentPath(): Pair<Int, Int> =
|
||||
savedScroll[path] ?: Pair(0, 0)
|
||||
|
||||
/** Заголовок под шапкой: текущая вкладка или хлебная крошка внутри сериала/папки. */
|
||||
fun subtitle(): String = when (val p = path) {
|
||||
Path.Root -> when (tab) {
|
||||
@@ -200,6 +230,21 @@ fun CatalogScreen(
|
||||
) {
|
||||
val app = PhoneApp.instance
|
||||
val userId by app.userId.collectAsState()
|
||||
|
||||
// Позиция скролла каталога: сохраняется/восстанавливается между навигациями.
|
||||
val gridState = rememberLazyGridState()
|
||||
|
||||
// Восстановить позицию при смене пути/загрузке.
|
||||
LaunchedEffect(state.path, state.items) {
|
||||
val (idx, off) = state.savedScrollForCurrentPath()
|
||||
gridState.scrollToItem(idx, off)
|
||||
}
|
||||
// Сохранять позицию при скролле.
|
||||
LaunchedEffect(gridState) {
|
||||
snapshotFlow {
|
||||
gridState.firstVisibleItemIndex to gridState.firstVisibleItemScrollOffset
|
||||
}.collect { (idx, off) -> state.setScroll(idx, off) }
|
||||
}
|
||||
val downloaded by app.state.downloadedFlow.collectAsState()
|
||||
val localDownloaded by app.downloadManager.downloadedIds.collectAsState()
|
||||
|
||||
@@ -218,6 +263,7 @@ fun CatalogScreen(
|
||||
ScreenPlaceholder(if (state.query.isNotBlank()) "ничего не нашлось" else "пусто")
|
||||
} else {
|
||||
LazyVerticalGrid(
|
||||
state = gridState,
|
||||
columns = GridCells.Fixed(2),
|
||||
contentPadding = PaddingValues(14.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
@@ -246,7 +292,11 @@ fun CatalogScreen(
|
||||
state.openSeason(item, series)
|
||||
}
|
||||
item.Type in CatalogState.CONTAINER_TYPES -> state.openFolder(item)
|
||||
else -> onItemClick(item)
|
||||
else -> {
|
||||
// уходим на детали — сохранить позицию, чтобы назад вернул скролл
|
||||
state.saveScrollNow()
|
||||
onItemClick(item)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
@@ -213,6 +213,15 @@ fun GlassesScreen(
|
||||
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
|
||||
RemoteButton(
|
||||
text = "⏻ Выключить очки",
|
||||
big = true,
|
||||
onClick = {
|
||||
scope.launch { app.actions?.shutdownGlasses() }
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth().padding(horizontal = 14.dp),
|
||||
)
|
||||
|
||||
RemoteButton(
|
||||
text = "Скачанное на очках →",
|
||||
big = true,
|
||||
|
||||
Reference in New Issue
Block a user