add web appbar color customization

This commit is contained in:
2026-08-15 08:30:39 +03:00
parent d6a0d058ed
commit 036aeba245
7 changed files with 43 additions and 7 deletions
@@ -38,4 +38,3 @@ class SearchSyncStrategy(
} }
} }
} }
@@ -39,14 +39,14 @@ fun Modifier.imePaddingInternal(): Modifier {
} }
@Composable @Composable
fun Nav(root: VBoxRootComponent) { fun Nav(root: VBoxRootComponent, onAttach: @Composable () -> Unit = {}) {
CompositionLocalProvider( CompositionLocalProvider(
LocalImageLoader provides remember { root.imageLoaderProvider.provide() } LocalImageLoader provides remember { root.imageLoaderProvider.provide() }
) { NavInternal(root) } ) { NavInternal(root, onAttach) }
} }
@Composable @Composable
private fun NavInternal(root: VBoxRootComponent) { private fun NavInternal(root: VBoxRootComponent, onAttach: @Composable () -> Unit) {
Nav(root) { child, route -> Nav(root) { child, route ->
when (child) { when (child) {
is Child.PlayerChild -> PlayerScreen(child.component) is Child.PlayerChild -> PlayerScreen(child.component)
@@ -76,6 +76,7 @@ private fun NavInternal(root: VBoxRootComponent) {
} }
} }
} }
onAttach()
UpdateDialog(root.updateComponent) UpdateDialog(root.updateComponent)
} }
} }
@@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawing import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@@ -26,6 +27,7 @@ import ru.shadowsparky.domain.AppConfig
import ru.shadowsparky.koin import ru.shadowsparky.koin
import ru.shadowsparky.ui.hideElementById import ru.shadowsparky.ui.hideElementById
import ru.shadowsparky.ui.hookPageVisibility import ru.shadowsparky.ui.hookPageVisibility
import ru.shadowsparky.ui.updateBrowserAppBarColor
import ru.shadowsparky.vbox.shared.presentation.Nav import ru.shadowsparky.vbox.shared.presentation.Nav
import ru.shadowsparky.vbox.shared.presentation.RootFactory import ru.shadowsparky.vbox.shared.presentation.RootFactory
import ru.shadowsparky.vbox.shared.presentation.UrlRoutes import ru.shadowsparky.vbox.shared.presentation.UrlRoutes
@@ -71,7 +73,10 @@ fun mainInternal() {
root.onFinish.collect { root.navReplace(Route.Loading()) } root.onFinish.collect { root.navReplace(Route.Loading()) }
} }
Box(Modifier.fillMaxSize().padding(WindowInsets.safeDrawing.asPaddingValues())) { Box(Modifier.fillMaxSize().padding(WindowInsets.safeDrawing.asPaddingValues())) {
Nav(root) Nav(root) {
val color = MaterialTheme.colorScheme.background
LaunchedEffect(color) { updateBrowserAppBarColor(color) }
}
} }
} }
} }
@@ -1,6 +1,7 @@
package ru.shadowsparky.backend.data package ru.shadowsparky.backend.data
import com.auth0.jwt.JWT import com.auth0.jwt.JWT
import com.auth0.jwt.JWTVerifier
import com.auth0.jwt.algorithms.Algorithm import com.auth0.jwt.algorithms.Algorithm
import com.auth0.jwt.interfaces.DecodedJWT import com.auth0.jwt.interfaces.DecodedJWT
import org.koin.core.annotation.Factory import org.koin.core.annotation.Factory
@@ -14,7 +15,7 @@ class TokenVerifier(
private val loginVerifier: LoginVerifier private val loginVerifier: LoginVerifier
) { ) {
val verifier = JWT.require(Algorithm.HMAC512(jwtInfo.secret)) val verifier: JWTVerifier = JWT.require(Algorithm.HMAC512(jwtInfo.secret))
.withAudience(jwtInfo.audience) .withAudience(jwtInfo.audience)
.withIssuer(jwtInfo.issuer) .withIssuer(jwtInfo.issuer)
.build() .build()
@@ -7,6 +7,8 @@ import androidx.compose.foundation.layout.Spacer
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.lazy.LazyItemScope import androidx.compose.foundation.lazy.LazyItemScope
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Card import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults import androidx.compose.material3.CardDefaults
@@ -89,11 +91,13 @@ fun ExpressiveLazyColumn(
reverseLayout: Boolean = false, reverseLayout: Boolean = false,
verticalArrangement: Arrangement.Vertical = verticalArrangement: Arrangement.Vertical =
if (!reverseLayout) Arrangement.Top else Arrangement.Bottom, if (!reverseLayout) Arrangement.Top else Arrangement.Bottom,
state: LazyListState = rememberLazyListState(),
content: ExpressiveLazyListScope.() -> Unit content: ExpressiveLazyListScope.() -> Unit
) { ) {
val scope = ExpressiveLazyListScope().apply(content) val scope = ExpressiveLazyListScope().apply(content)
LazyColumn( LazyColumn(
state = state,
modifier = modifier, modifier = modifier,
contentPadding = contentPadding, contentPadding = contentPadding,
reverseLayout = reverseLayout, reverseLayout = reverseLayout,
@@ -9,6 +9,7 @@ import com.arkivanov.decompose.extensions.compose.stack.animation.fade
import com.arkivanov.decompose.extensions.compose.stack.animation.predictiveback.androidPredictiveBackAnimatableV2 import com.arkivanov.decompose.extensions.compose.stack.animation.predictiveback.androidPredictiveBackAnimatableV2
import com.arkivanov.decompose.extensions.compose.stack.animation.predictiveback.predictiveBackAnimation import com.arkivanov.decompose.extensions.compose.stack.animation.predictiveback.predictiveBackAnimation
import com.arkivanov.decompose.extensions.compose.stack.animation.stackAnimation import com.arkivanov.decompose.extensions.compose.stack.animation.stackAnimation
import ru.shadowsparky.ui.theme.getColorScheme
@OptIn(ExperimentalDecomposeApi::class, ExperimentalMaterial3ExpressiveApi::class) @OptIn(ExperimentalDecomposeApi::class, ExperimentalMaterial3ExpressiveApi::class)
@Composable @Composable
@@ -16,7 +17,7 @@ fun Nav(
rootComponent: RootComponent<*>, rootComponent: RootComponent<*>,
process: @Composable (child: Child, route: Route) -> Unit process: @Composable (child: Child, route: Route) -> Unit
) { ) {
MaterialExpressiveTheme { MaterialExpressiveTheme(getColorScheme()) {
Children( Children(
stack = rootComponent.valueChildStack, stack = rootComponent.valueChildStack,
animation = predictiveBackAnimation( animation = predictiveBackAnimation(
@@ -1,5 +1,6 @@
package ru.shadowsparky.ui package ru.shadowsparky.ui
import androidx.compose.ui.graphics.Color
import com.arkivanov.essenty.lifecycle.Lifecycle import com.arkivanov.essenty.lifecycle.Lifecycle
import com.arkivanov.essenty.lifecycle.LifecycleRegistry import com.arkivanov.essenty.lifecycle.LifecycleRegistry
import com.arkivanov.essenty.lifecycle.create import com.arkivanov.essenty.lifecycle.create
@@ -8,6 +9,7 @@ import com.arkivanov.essenty.lifecycle.resume
import com.arkivanov.essenty.lifecycle.start import com.arkivanov.essenty.lifecycle.start
import com.arkivanov.essenty.lifecycle.stop import com.arkivanov.essenty.lifecycle.stop
import kotlinx.browser.document import kotlinx.browser.document
import org.w3c.dom.HTMLMetaElement
import org.w3c.dom.events.Event import org.w3c.dom.events.Event
@JsFun( @JsFun(
@@ -80,3 +82,26 @@ fun hookPageVisibility(lifecycle: LifecycleRegistry) {
document.addEventListener("pagehide", { onPageHideShow(true) }) document.addEventListener("pagehide", { onPageHideShow(true) })
document.addEventListener("pageshow", { onPageHideShow(false) }) document.addEventListener("pageshow", { onPageHideShow(false) })
} }
fun updateBrowserAppBarColor(color: Color) {
var metaElement = document.querySelector("meta[name='theme-color']") as? HTMLMetaElement
if (metaElement == null) {
val meta = document.createElement("meta") as HTMLMetaElement
meta.setAttribute("name", "theme-color")
document.head?.appendChild(meta)
metaElement = meta
}
metaElement.setAttribute("content", color.toHexStr())
}
private fun Color.toHexStr(): String {
val r = (this.red * 255).toInt()
val g = (this.green * 255).toInt()
val b = (this.blue * 255).toInt()
return "#${r.toHexString()}${g.toHexString()}${b.toHexString()}"
}
private fun Int.toHexString(): String {
val hex = this.toString(16)
return if (hex.length == 1) "0$hex" else hex
}