From 7f2874f45ce66ad5392d3bd88574af9a916e40ac Mon Sep 17 00:00:00 2001 From: Lanius Trolling Date: Sat, 6 Apr 2024 13:43:03 -0400 Subject: [PATCH] Create joinToHtml construct --- .../kotlin/info/mechyrdia/data/data.kt | 2 +- .../kotlin/info/mechyrdia/lore/fonts.kt | 6 +- .../kotlin/info/mechyrdia/lore/views_lore.kt | 16 +-- .../kotlin/info/mechyrdia/lore/views_prefs.kt | 103 +++++++++--------- .../info/mechyrdia/route/resource_handler.kt | 2 +- 5 files changed, 62 insertions(+), 67 deletions(-) diff --git a/src/jvmMain/kotlin/info/mechyrdia/data/data.kt b/src/jvmMain/kotlin/info/mechyrdia/data/data.kt index d9c7b24..bccfe32 100644 --- a/src/jvmMain/kotlin/info/mechyrdia/data/data.kt +++ b/src/jvmMain/kotlin/info/mechyrdia/data/data.kt @@ -61,7 +61,7 @@ object ConnectionHolder { fun initialize(conn: String, db: String) { if (clientDeferred.isCompleted) - error("Cannot initialize database twice!") + error("Cannot initialize database twice") MongoClient.create( MongoClientSettings.builder() diff --git a/src/jvmMain/kotlin/info/mechyrdia/lore/fonts.kt b/src/jvmMain/kotlin/info/mechyrdia/lore/fonts.kt index 9ddcf3e..d23d750 100644 --- a/src/jvmMain/kotlin/info/mechyrdia/lore/fonts.kt +++ b/src/jvmMain/kotlin/info/mechyrdia/lore/fonts.kt @@ -226,10 +226,10 @@ object MechyrdiaSansFont { val iterator = getPathIterator(null) val coords = DoubleArray(6) - var isInitial = true + var isFirst = true while (!iterator.isDone) { - if (isInitial) - isInitial = false + if (isFirst) + isFirst = false else append(' ') diff --git a/src/jvmMain/kotlin/info/mechyrdia/lore/views_lore.kt b/src/jvmMain/kotlin/info/mechyrdia/lore/views_lore.kt index 0be3fd5..4cfb5b1 100644 --- a/src/jvmMain/kotlin/info/mechyrdia/lore/views_lore.kt +++ b/src/jvmMain/kotlin/info/mechyrdia/lore/views_lore.kt @@ -42,16 +42,16 @@ suspend fun ApplicationCall.loreIntroPage(): HTML.() -> Unit { } } +private val Tag.breadCrumbArrow: Unit + get() { + +Entities.nbsp + +Entities.gt + +Entities.nbsp + } + context(ApplicationCall) private fun FlowContent.breadCrumbs(links: List>) = p { - var isNext = false - for ((url, text) in links) { - if (isNext) { - +Entities.nbsp - +Entities.gt - +Entities.nbsp - } else isNext = true - + links.joinToHtml(Tag::breadCrumbArrow) { (url, text) -> a(href = href(url)) { +text } } } diff --git a/src/jvmMain/kotlin/info/mechyrdia/lore/views_prefs.kt b/src/jvmMain/kotlin/info/mechyrdia/lore/views_prefs.kt index 0f37c19..99307e9 100644 --- a/src/jvmMain/kotlin/info/mechyrdia/lore/views_prefs.kt +++ b/src/jvmMain/kotlin/info/mechyrdia/lore/views_prefs.kt @@ -5,6 +5,7 @@ import info.mechyrdia.route.KeyedEnumSerializer import io.ktor.server.application.* import kotlinx.html.* import kotlinx.serialization.Serializable +import kotlinx.serialization.serializer @Serializable(PageThemeSerializer::class) enum class PageTheme(val attributeValue: String?) { @@ -49,6 +50,44 @@ val ApplicationCall.april1stMode: April1stMode else -> April1stMode.DEFAULT } +class JoinToHtmlConsumer(val iterator: Iterator) { + inline fun T.invokeReceiver(separator: T.() -> Unit, body: T.(E) -> Unit) { + var isFirst = true + for (item in iterator) { + if (isFirst) + isFirst = false + else + separator() + body(item) + } + } + + context(T) + inline operator fun invoke(separator: T.() -> Unit, body: T.(E) -> Unit) { + invokeReceiver(separator, body) + } +} + +val Iterable.joinToHtml: JoinToHtmlConsumer + get() = JoinToHtmlConsumer(iterator()) + +inline fun > FlowOrInteractiveOrPhrasingContent.preference(inputName: String, current: E, crossinline localize: (E) -> String) { + val serializer = serializer() as? KeyedEnumSerializer ?: throw UnsupportedOperationException("Serializer for ${E::class.simpleName} has not been declared as KeyedEnumSerializer") + val entries = serializer.entries + + entries.joinToHtml(Tag::br) { option -> + label { + radioInput(name = inputName) { + value = serializer.getKey(option) ?: "null" + required = true + checked = current == option + } + +Entities.nbsp + +localize(option) + } + } +} + suspend fun ApplicationCall.clientSettingsPage(): HTML.() -> Unit { attributes.put(PageDoNotCacheAttributeKey, true) @@ -62,66 +101,22 @@ suspend fun ApplicationCall.clientSettingsPage(): HTML.() -> Unit { } section { h2 { +"Page Theme" } - label { - radioInput(name = "theme") { - value = "null" - required = true - checked = theme == PageTheme.SYSTEM - } - +Entities.nbsp - +"System Choice" - } - br - label { - radioInput(name = "theme") { - value = "light" - required = true - checked = theme == PageTheme.LIGHT - } - +Entities.nbsp - +"Light Theme" - } - br - label { - radioInput(name = "theme") { - value = "dark" - required = true - checked = theme == PageTheme.DARK + preference("theme", theme) { + when (it) { + PageTheme.SYSTEM -> "Chosen by Browser/System" + PageTheme.LIGHT -> "Light Theme" + PageTheme.DARK -> "Dark Theme" } - +Entities.nbsp - +"Dark Theme" } } section { h2 { +"April Fools' Day Mode" } - label { - radioInput(name = "april1st") { - value = "default" - required = true - checked = april1st == April1stMode.DEFAULT - } - +Entities.nbsp - +"Only on April 1st" - } - br - label { - radioInput(name = "april1st") { - value = "always" - required = true - checked = april1st == April1stMode.ALWAYS - } - +Entities.nbsp - +"Always" - } - br - label { - radioInput(name = "april1st") { - value = "never" - required = true - checked = april1st == April1stMode.NEVER + preference("april1st", april1st) { + when (it) { + April1stMode.DEFAULT -> "Only on April 1st" + April1stMode.ALWAYS -> "Always" + April1stMode.NEVER -> "Never" } - +Entities.nbsp - +"Never" } } } diff --git a/src/jvmMain/kotlin/info/mechyrdia/route/resource_handler.kt b/src/jvmMain/kotlin/info/mechyrdia/route/resource_handler.kt index fab7c34..83c8565 100644 --- a/src/jvmMain/kotlin/info/mechyrdia/route/resource_handler.kt +++ b/src/jvmMain/kotlin/info/mechyrdia/route/resource_handler.kt @@ -47,7 +47,7 @@ inline fun , reified P : Any> Route.post() { } abstract class KeyedEnumSerializer>(val entries: EnumEntries, val getKey: (E) -> String? = { it.name }) : KSerializer { - override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("EnumSerializer<${entries.first()::class.qualifiedName}>", PrimitiveKind.STRING) + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("KeyedEnumSerializer<${entries.first()::class.qualifiedName}>", PrimitiveKind.STRING) private val inner = String.serializer().nullable private val keyMap = entries.associateBy { getKey(it)?.lowercase() } -- 2.25.1