From 9ccdddb365c2666ae8c830503e72687313d0fe32 Mon Sep 17 00:00:00 2001 From: Elijah Mooring Date: Wed, 30 Oct 2024 13:57:37 -0500 Subject: [PATCH] wip: stash last night's work --- .../java/com/example/objectionapp/Bridge.kt | 401 ++-- .../java/com/example/objectionapp/CardView.kt | 236 -- .../com/example/objectionapp/Constants.kt | 2 + .../com/example/objectionapp/ContentView.kt | 491 ---- .../com/example/objectionapp/Controller.kt | 6 +- .../java/com/example/objectionapp/FormView.kt | 516 ++--- .../com/example/objectionapp/GetSchema.kt | 4 +- .../java/com/example/objectionapp/Layout.kt | 156 +- .../java/com/example/objectionapp/ListView.kt | 194 +- .../com/example/objectionapp/Navigation.kt | 28 + .../java/com/example/objectionapp/ObjectId.kt | 4 +- .../java/com/example/objectionapp/Page.kt | 394 ++-- .../com/example/objectionapp/Providers.kt | 44 +- .../java/com/example/objectionapp/Schema.kt | 2 +- .../com/example/objectionapp/StandardIcon.kt | 40 - .../java/com/example/objectionapp/TabBar.kt | 714 +++--- .../java/com/example/objectionapp/View.kt | 391 ++-- .../com/example/objectionapp/view/AppBar.kt | 281 +++ .../java/com/example/objectionapp/view/Box.kt | 39 + .../com/example/objectionapp/view/Button.kt | 38 + .../com/example/objectionapp/view/Center.kt | 42 + .../com/example/objectionapp/view/Flex.kt | 243 ++ .../com/example/objectionapp/view/Icon.kt | 41 + .../com/example/objectionapp/view/Image.kt | 68 + .../com/example/objectionapp/view/Main.kt | 32 + .../example/objectionapp/view/PageToggle.kt | 87 + .../com/example/objectionapp/view/Stack.kt | 22 + .../com/example/objectionapp/view/Style.kt | 109 + .../com/example/objectionapp/view/Surface.kt | 54 + .../com/example/objectionapp/view/Text.kt | 114 + .../example/objectionapp/view/ThemeColor.kt | 65 + objection_deploy.ts | 4 + schema.json | 1988 +---------------- 33 files changed, 2710 insertions(+), 4140 deletions(-) delete mode 100644 app/src/main/java/com/example/objectionapp/CardView.kt delete mode 100644 app/src/main/java/com/example/objectionapp/ContentView.kt create mode 100644 app/src/main/java/com/example/objectionapp/Navigation.kt create mode 100644 app/src/main/java/com/example/objectionapp/view/AppBar.kt create mode 100644 app/src/main/java/com/example/objectionapp/view/Box.kt create mode 100644 app/src/main/java/com/example/objectionapp/view/Button.kt create mode 100644 app/src/main/java/com/example/objectionapp/view/Center.kt create mode 100644 app/src/main/java/com/example/objectionapp/view/Flex.kt create mode 100644 app/src/main/java/com/example/objectionapp/view/Icon.kt create mode 100644 app/src/main/java/com/example/objectionapp/view/Image.kt create mode 100644 app/src/main/java/com/example/objectionapp/view/Main.kt create mode 100644 app/src/main/java/com/example/objectionapp/view/PageToggle.kt create mode 100644 app/src/main/java/com/example/objectionapp/view/Stack.kt create mode 100644 app/src/main/java/com/example/objectionapp/view/Style.kt create mode 100644 app/src/main/java/com/example/objectionapp/view/Surface.kt create mode 100644 app/src/main/java/com/example/objectionapp/view/Text.kt create mode 100644 app/src/main/java/com/example/objectionapp/view/ThemeColor.kt diff --git a/app/src/main/java/com/example/objectionapp/Bridge.kt b/app/src/main/java/com/example/objectionapp/Bridge.kt index a2a2596..545d66e 100644 --- a/app/src/main/java/com/example/objectionapp/Bridge.kt +++ b/app/src/main/java/com/example/objectionapp/Bridge.kt @@ -26,215 +26,218 @@ import java.net.UnknownHostException import kotlin.math.log class Bridge(private var logger: Logger, private var session: Session) : CoroutineScope { - private var job: Job = Job() - override val coroutineContext: CoroutineContext - get() = Dispatchers.Main + job - - var onError = Listener(logger = logger) - var onHasInternet = Listener(logger = logger) - var onObjectSet = Listener>(logger = logger) - var onObjectRemoved = Listener(logger = logger) - - private var isOffline = false - private var url: String? = null - private var websocket: WebSocket? = null - private var isRunning = false - private val client = OkHttpClient() - private val json = Json { ignoreUnknownKeys = true; isLenient = true; encodeDefaults = true } - private val outgoingMessages = mutableMapOf Unit>() - - fun start(url: String) { - if (!isRunning) { - logger.info("starting") - - this.websocket - val fullUrl = "$url?session_id=${session.getId()}" - this.url = fullUrl - connect() - } else { - logger.info("start called again, but skipping because bridge is already running") - } - } - - fun watch(objectId: String, onComplete: () -> Unit) { - sendMessage( - OutgoingMessage.Watch( - requestId = listenForAcknowledgement(onComplete), - id = objectId, - ) - ) - } - - fun unwatch(objectId: String, onComplete: () -> Unit) { - sendMessage( - OutgoingMessage.Unwatch( - requestId = listenForAcknowledgement(onComplete), - id = objectId, - ) - ) - } - - fun emitBindingUpdate(key: String, data: JsonElement, onComplete: () -> Unit) { - sendMessage( - OutgoingMessage.EmitBindingUpdate( - requestId = listenForAcknowledgement(onComplete), - key = key, - data = data - ) - ) - } - - private fun listenForAcknowledgement(callback: () -> Unit): String { - val id = UUID.randomUUID().toString() - outgoingMessages[id] = callback - - return id - } - - private fun acknowledge(requestId: String?, error: String?) { - if (error != null) { - logger.error(error) - onError.emit(error) - } - - if (requestId != null) { - val callback = outgoingMessages[requestId] - if (callback != null) callback() - - outgoingMessages.remove(requestId) - } - } - - private fun callError(message: String) { - onError.emit(message) - } - - private fun sendMessage(message: OutgoingMessage) { - websocket?.let { ws -> - val jsonMessage = json.encodeToString(OutgoingMessage.serializer(), message) - ws.send(jsonMessage) - } ?: logger.error("must call start() before watch() or fireEvent()") - } - - private fun parseIncomingJson(data: String): List { - return try { - json.decodeFromString(data) - } catch (e: Exception) { - callError("Failed to parse information from server.") - logger.critical("failed to parse json of incoming message: ${e.message}. JSON: $data") - - emptyList() - } - } - - private fun queueRetry() { - websocket?.cancel() - - Executors.newSingleThreadScheduledExecutor().schedule({ - logger.info("retrying websocket connection") - connect() - }, 3, TimeUnit.SECONDS) - } - - private fun connect() { - logger.info("connecting") - - val url = url ?: run { - logger.error("must call .start() before .connect()") - return - } - - val request = Request.Builder().url(url).build() - websocket = client.newWebSocket(request, object : WebSocketListener() { - override fun onOpen(webSocket: WebSocket, response: Response) { - isRunning = true - - if (isOffline) { - logger.info("websocket connected") - isOffline = false - onHasInternet.emit(true) - } - } - - override fun onMessage(webSocket: WebSocket, text: String) { - parseIncomingJson(text).forEach { handleIncomingMessage(it) } - } - - override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) { - when (t) { - is ConnectException, is SocketTimeoutException, is UnknownHostException -> { - isOffline = true - onHasInternet.emit(false) - queueRetry() - } - - is EOFException -> { - logger.warn("connection was suddenly dropped") - queueRetry() - } - - else -> { - logger.warn("a socket failed: $t") - callError(t.localizedMessage ?: "Unknown error") - } - } - } - }) - } - - private fun handleIncomingMessage(message: IncomingMessage) { - when (message) { - is IncomingMessage.RemoveObject -> onObjectRemoved.emit(message.id) - is IncomingMessage.SetObject -> onObjectSet.emit(Pair(message.id, message.data)) - is IncomingMessage.Acknowledge -> acknowledge(message.requestId, message.error) - } - } + private var job: Job = Job() + override val coroutineContext: CoroutineContext + get() = Dispatchers.Main + job + + var onError = Listener(logger = logger) + var onHasInternet = Listener(logger = logger) + var onObjectSet = Listener>(logger = logger) + var onObjectRemoved = Listener(logger = logger) + + private var retryCounter = 0; + private var isOffline = false + private var url: String? = null + private var websocket: WebSocket? = null + private var isRunning = false + private val client = OkHttpClient() + private val json = Json { ignoreUnknownKeys = true; isLenient = true; encodeDefaults = true } + private val outgoingMessages = mutableMapOf Unit>() + + fun start(url: String) { + if (!isRunning) { + logger.info("starting") + + this.websocket + val fullUrl = "$url?session_id=${session.getId()}" + this.url = fullUrl + connect() + } else { + logger.info("start called again, but skipping because bridge is already running") + } + } + + fun watch(objectId: String, onComplete: () -> Unit) { + sendMessage( + OutgoingMessage.Watch( + requestId = listenForAcknowledgement(onComplete), + id = objectId, + ) + ) + } + + fun unwatch(objectId: String, onComplete: () -> Unit) { + sendMessage( + OutgoingMessage.Unwatch( + requestId = listenForAcknowledgement(onComplete), + id = objectId, + ) + ) + } + + fun emitBindingUpdate(key: String, data: JsonElement, onComplete: () -> Unit) { + sendMessage( + OutgoingMessage.EmitBindingUpdate( + requestId = listenForAcknowledgement(onComplete), key = key, data = data + ) + ) + } + + private fun listenForAcknowledgement(callback: () -> Unit): String { + val id = UUID.randomUUID().toString() + outgoingMessages[id] = callback + + return id + } + + private fun acknowledge(requestId: String?, error: String?) { + if (error != null) { + logger.error(error) + onError.emit(error) + } + + if (requestId != null) { + val callback = outgoingMessages[requestId] + if (callback != null) callback() + + outgoingMessages.remove(requestId) + } + } + + private fun callError(message: String) { + onError.emit(message) + } + + private fun sendMessage(message: OutgoingMessage) { + websocket?.let { ws -> + val jsonMessage = json.encodeToString(OutgoingMessage.serializer(), message) + ws.send(jsonMessage) + } ?: logger.error("must call start() before watch() or fireEvent()") + } + + private fun parseIncomingJson(data: String): List { + return try { + json.decodeFromString(data) + } catch (e: Exception) { + callError("Failed to parse information from server.") + logger.critical("failed to parse json of incoming message: ${e.message}. JSON: $data") + + emptyList() + } + } + + private fun queueRetry() { + websocket?.cancel() + retryCounter += 1; + + Executors.newSingleThreadScheduledExecutor().schedule({ + logger.info("retrying websocket connection") + connect() + }, 3, TimeUnit.SECONDS) + } + + private fun connect() { + logger.info("connecting") + + val url = url ?: run { + logger.error("must call .start() before .connect()") + return + } + + val request = Request.Builder().url(url).build() + websocket = client.newWebSocket(request, object : WebSocketListener() { + override fun onOpen(webSocket: WebSocket, response: Response) { + isRunning = true + + if (isOffline) { + logger.info("websocket connected") + retryCounter = 0 + isOffline = false + onHasInternet.emit(true) + } + } + + override fun onMessage(webSocket: WebSocket, text: String) { + parseIncomingJson(text).forEach { handleIncomingMessage(it) } + } + + override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) { + when (t) { + is ConnectException, is SocketTimeoutException, is UnknownHostException -> { + if (retryCounter > 5 && !isOffline) { + isOffline = true + onHasInternet.emit(false) + } + + println("retrying, counter is at $retryCounter") + queueRetry() + } + + is EOFException -> { + logger.warn("connection was suddenly dropped") + queueRetry() + } + + else -> { + logger.warn("a socket failed: $t") + callError(t.localizedMessage ?: "Unknown error") + } + } + } + }) + } + + private fun handleIncomingMessage(message: IncomingMessage) { + logger.info("received message from server: $message") + + when (message) { + is IncomingMessage.RemoveObject -> onObjectRemoved.emit(message.id) + is IncomingMessage.SetObject -> onObjectSet.emit(Pair(message.id, message.data)) + is IncomingMessage.Acknowledge -> acknowledge(message.requestId, message.error) + } + } } @OptIn(ExperimentalSerializationApi::class) @Serializable @JsonClassDiscriminator("$") sealed class OutgoingMessage { - @Serializable - @SerialName("watch") - data class Watch( - @SerialName("request_id") val requestId: String, - val id: String - ) : OutgoingMessage() - - @Serializable - @SerialName("unwatch") - data class Unwatch( - @SerialName("request_id") val requestId: String, - val id: String - ) : OutgoingMessage() - - @Serializable - @SerialName("emit_binding_update") - data class EmitBindingUpdate( - @SerialName("request_id") val requestId: String, - val key: String, - val data: JsonElement - ) : OutgoingMessage() + @Serializable + @SerialName("watch") + data class Watch( + @SerialName("request_id") val requestId: String, val id: String + ) : OutgoingMessage() + + @Serializable + @SerialName("unwatch") + data class Unwatch( + @SerialName("request_id") val requestId: String, val id: String + ) : OutgoingMessage() + + @Serializable + @SerialName("emit_binding_update") + data class EmitBindingUpdate( + @SerialName("request_id") val requestId: String, val key: String, val data: JsonElement + ) : OutgoingMessage() } @OptIn(ExperimentalSerializationApi::class) @Serializable @JsonClassDiscriminator("$") sealed class IncomingMessage { - @Serializable - @SerialName("remove_object") - data class RemoveObject(val id: String) : IncomingMessage() - - @Serializable - @SerialName("set_object") - data class SetObject(val id: String, val data: JsonElement) : IncomingMessage() - - @Serializable - @SerialName("acknowledge") - data class Acknowledge( - @SerialName("request_id") val requestId: String? = null, - val error: String? = null, - @SerialName("retry_after_seconds") val retryAfterSeconds: Int? = null - ) : IncomingMessage() + @Serializable + @SerialName("remove_object") + data class RemoveObject(val id: String) : IncomingMessage() + + @Serializable + @SerialName("set_object") + data class SetObject(val id: String, val data: JsonElement) : IncomingMessage() + + @Serializable + @SerialName("acknowledge") + data class Acknowledge( + @SerialName("request_id") val requestId: String? = null, + val error: String? = null, + @SerialName("retry_after_seconds") val retryAfterSeconds: Int? = null + ) : IncomingMessage() } diff --git a/app/src/main/java/com/example/objectionapp/CardView.kt b/app/src/main/java/com/example/objectionapp/CardView.kt deleted file mode 100644 index 7b7ad1d..0000000 --- a/app/src/main/java/com/example/objectionapp/CardView.kt +++ /dev/null @@ -1,236 +0,0 @@ -package com.example.objectionapp - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxHeight -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.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.LazyRow -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.Card -import androidx.compose.material3.CardColors -import androidx.compose.material3.CardDefaults -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.material3.TopAppBarScrollBehavior -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.input.nestedscroll.nestedScroll -import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import coil.compose.AsyncImage -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable - -@Serializable -data class CardViewOptions( - val excessiveRounding: Boolean = false, - val useCardPadding: Boolean = false, -) - -@Serializable -@SerialName("CardView") -data class CardView( - val options: CardViewOptions, - val containers: List = listOf(), -) : View() - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun CardViewRender(view: CardView, scrollBehavior: TopAppBarScrollBehavior) { - LazyColumn( - modifier = Modifier - .padding(bottom = 90.dp) - .nestedScroll(scrollBehavior.nestedScrollConnection), - verticalArrangement = Arrangement.spacedBy(10.dp), - ) { - view.containers.map { - item { - when (it) { - is CardContainer.SingularCardContainer -> CardRender( - it.objectId, - isVertical = false, - viewOptions = view.options, - ) - - is CardContainer.PluralCardContainer -> { - Box( - modifier = Modifier - .background(color = MaterialTheme.colorScheme.surfaceVariant) - .fillMaxWidth() - ) { - LazyRow( - modifier = Modifier - .nestedScroll(scrollBehavior.nestedScrollConnection) - .fillMaxWidth() - ) { - item { - it.objectIds.map { - CardRender( - it, isVertical = true, viewOptions = view.options - ) - } - } - } - } - } - - is CardContainer.CustomCardContainer -> return@item - } - } - } - } -} - -@Composable -private fun CardRender( - pageId: String, - isVertical: Boolean, - viewOptions: CardViewOptions, -) { - val page = usePage(pageId) - val navController = useNavController() - - var rootModifier: Modifier = Modifier - var bodyModifier: Modifier = Modifier - - rootModifier = if (isVertical) rootModifier.width(140.dp) else rootModifier.fillMaxWidth() - rootModifier = - if (viewOptions.excessiveRounding) rootModifier.clip(RoundedCornerShape(20.dp)) else rootModifier - rootModifier = if (viewOptions.useCardPadding) rootModifier.padding(10.dp) else rootModifier - - bodyModifier = if (viewOptions.useCardPadding) bodyModifier else bodyModifier.padding(10.dp) - - Card( - modifier = rootModifier, - elevation = CardDefaults.elevatedCardElevation(defaultElevation = 5.dp), - colors = CardColors( - containerColor = MaterialTheme.colorScheme.surfaceContainer, - contentColor = MaterialTheme.colorScheme.onSurface, - disabledContentColor = MaterialTheme.colorScheme.onSurfaceVariant, - disabledContainerColor = MaterialTheme.colorScheme.surfaceVariant - ), - onClick = { - navController.navigate(route = encodeObjectIdIntoPageRoute(pageId)) - }, - ) { - if (page == null) { - // TODO: skeleton loader here - } else { - Row( - modifier = bodyModifier - .fillMaxWidth() - .fillMaxHeight() - .padding(10.dp), - horizontalArrangement = Arrangement.spacedBy(10.dp), - verticalAlignment = Alignment.Top - ) { - if (page.type is PageType.Post) { - page.type.imageUrls?.getOrNull(0)?.let { - AsyncImage( - model = it, - contentDescription = "An image", - clipToBounds = true, - contentScale = ContentScale.Crop, - modifier = Modifier - .width(110.dp) - .height(110.dp) - .clip(RoundedCornerShape(12.dp)) - ) - } - } - - Column( - verticalArrangement = Arrangement.SpaceAround - ) { - if (page.type is PageType.Post) { - page.type.supertitle?.let { - Text( - it, - color = MaterialTheme.colorScheme.onSurface, - fontSize = 10.sp, - fontWeight = FontWeight.SemiBold, - ) - } - } - - page.title?.let { - Text( - it, - color = MaterialTheme.colorScheme.onSurface, - fontSize = 16.sp, - ) - } - - page.subtitle?.let { - Text( - it, fontSize = 12.sp, color = MaterialTheme.colorScheme.secondary - ) - } - - if (page.type is PageType.Post) { - page.type.additionalInfo?.let { - Text( - it, - fontSize = 14.sp, - color = MaterialTheme.colorScheme.onSurface, - fontWeight = FontWeight.SemiBold - ) - } - } - } - } - } - } -} - -@Preview -@Composable -fun CardViewTest() { - val controller = Controller.fromConstants() - - controller.objectStore.preload(defaultThemeId, Theme()) - controller.objectStore.preload(defaultLayoutId, Layout(currentPageId = "cards")) - - controller.objectStore.preload( - "cards", Page( - title = "Products", - type = PageType.Plain("ShoppingBasket"), - subtitle = "Hello darkness my old friend, I've come to talk with you again", - view = CardView( - options = CardViewOptions(excessiveRounding = true, useCardPadding = true), - mutableListOf( - CardContainer.SingularCardContainer("Hammer_Product"), - CardContainer.SingularCardContainer("Hammer_Product"), - CardContainer.SingularCardContainer("Hammer_Product"), - ) - ) - ) - - ) - - controller.objectStore.preload( - "Hammer_Product", Page( - title = "Premium Hammer", subtitle = "Hammers Inc.", type = PageType.Post( - supertitle = "PRODUCT", imageUrls = mutableListOf( - "https://s3-alpha-sig.figma.com/img/c791/197e/dcf10efc9fa151a9e05a3cdb5135450e?Expires=1729468800&Key-Pair-Id=APKAQ4GOSFWCVNEHN3O4&Signature=kztBpUK9GdKSU0bOVEMBlxjzUMbLAcJfGVOqOjvMdvfoLL2d0OsiVej-PUy3yoFxqVSs5L7cShG6wUL7WTQ7DlvI98fcBBPfh5Fz0Ly5Z~4LMywr-nPPV1fIhlnWkwV8ZVhUYDS-4ktZjS1AWVNj0YfnrX4SRXVB4lCgl0uIX93E8zCOkk2tsNBbNtKp2mAZX1Wd7jprwPELnp1cuX-B3lm23BRYxGsq99yI6Ez0UVXEJRoto-f3w0YoWqMSfHTZxGwxaCv8oGhWSb5bR~J4JQL19kERthz38TbtzpUG9DAwW3Mou5swhPbdgi1O7yBJdlAUqN9uv3SEEQPYNzGBGQ__" - ), additionalInfo = "$55" - ) - - ) - ) - - TestProvider(controller) -} \ No newline at end of file diff --git a/app/src/main/java/com/example/objectionapp/Constants.kt b/app/src/main/java/com/example/objectionapp/Constants.kt index 9952093..337cb62 100644 --- a/app/src/main/java/com/example/objectionapp/Constants.kt +++ b/app/src/main/java/com/example/objectionapp/Constants.kt @@ -12,6 +12,8 @@ val lightBackgroundColor = Color(red = 240, green = 240, blue = 255) val darkBackgroundColor = Color(red = 12, green = 12, blue = 20) val lightForegroundColor = Color(red = 12, green = 12, blue = 20) val darkForegroundColor = Color(red = 240, green = 240, blue = 255) +val lightLoaderColor = Color(red = 240, green = 40, blue = 40) +val darkLoaderColor = Color(red = 240, green = 40, blue = 40) val defaultThemeId = "theme_default" val defaultLayoutId = "layout_default" diff --git a/app/src/main/java/com/example/objectionapp/ContentView.kt b/app/src/main/java/com/example/objectionapp/ContentView.kt deleted file mode 100644 index da6a696..0000000 --- a/app/src/main/java/com/example/objectionapp/ContentView.kt +++ /dev/null @@ -1,491 +0,0 @@ -package com.example.objectionapp - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.LazyRow -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.Button -import androidx.compose.material3.ButtonColors -import androidx.compose.material3.Card -import androidx.compose.material3.CardColors -import androidx.compose.material3.CardDefaults -import androidx.compose.material3.LocalContentColor -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.input.nestedscroll.NestedScrollConnection -import androidx.compose.ui.input.nestedscroll.nestedScroll -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import coil.compose.AsyncImage -import kotlinx.serialization.ExperimentalSerializationApi -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable -import kotlinx.serialization.json.JsonClassDiscriminator - -@OptIn(ExperimentalSerializationApi::class) -@Serializable -@JsonClassDiscriminator("$") -@ContentKey("def") -sealed class Style { - @Serializable - @SerialName("Padding") - data class PaddingHorizontal(val def: Float) : Style() - - @Serializable - @SerialName("PaddingVertical") - data class PaddingVertical(val def: Float) : Style() - - @Serializable - @SerialName("PaddingTop") - data class PaddingTop(val def: Float) : Style() - - @Serializable - @SerialName("PaddingBottom") - data class PaddingBottom(val def: Float) : Style() - - @Serializable - @SerialName("PaddingStart") - data class PaddingStart(val def: Float) : Style() - - @Serializable - @SerialName("PaddingEnd") - data class PaddingEnd(val def: Float) : Style() - - @Serializable - @SerialName("ClipCornerRadius") - data class ClipCornerRadius(val def: Float) : Style() - - @Serializable - @SerialName("Width") - data class Width(val def: Float) : Style() - - @Serializable - @SerialName("Height") - data class Height(val def: Float) : Style() - - @Serializable - @SerialName("Size") - data class Size(val def: Float) : Style() - - @Serializable - @SerialName("FillMaxWidth") - data object FillMaxWidth : Style() - - @Serializable - @SerialName("FillMaxHeight") - data object FillMaxHeight : Style() - - @Serializable - @SerialName("FillMaxSize") - data object FillMaxSize : Style() - - fun apply(modifier: Modifier): Modifier { - return when (this) { - is PaddingHorizontal -> modifier.padding(horizontal = def.dp) - is PaddingVertical -> modifier.padding(vertical = def.dp) - is PaddingTop -> modifier.padding(top = def.dp) - is PaddingBottom -> modifier.padding(bottom = def.dp) - is PaddingStart -> modifier.padding(start = def.dp) - is PaddingEnd -> modifier.padding(end = def.dp) - is ClipCornerRadius -> modifier.clip(RoundedCornerShape(def.dp)) - is Width -> modifier.width(def.dp) - is Height -> modifier.height(def.dp) - is Size -> modifier.size(def.dp) - is FillMaxWidth -> modifier.fillMaxWidth() - is FillMaxHeight -> modifier.fillMaxHeight() - is FillMaxSize -> modifier.fillMaxSize() - } - } -} - -fun compileStyles(styles: List