Add mobile-friendly CSS
authorTheSaminator <TheSaminator@users.noreply.github.com>
Thu, 31 Mar 2022 18:27:48 +0000 (14:27 -0400)
committerTheSaminator <TheSaminator@users.noreply.github.com>
Thu, 31 Mar 2022 18:27:48 +0000 (14:27 -0400)
src/jvmMain/kotlin/starshipfights/data/auth/user_sessions.kt
src/jvmMain/kotlin/starshipfights/data/auth/user_trophies.kt
src/jvmMain/kotlin/starshipfights/info/html_utils.kt
src/jvmMain/kotlin/starshipfights/info/view_bar.kt
src/jvmMain/kotlin/starshipfights/info/view_nav.kt
src/jvmMain/kotlin/starshipfights/info/view_tpl.kt
src/jvmMain/kotlin/starshipfights/info/views_error.kt
src/jvmMain/resources/static/style.css

index 2b79c891a407e2da2d8f13a93dafc424dc0947c2..aec6b464ae8f9fd0e2fe1ab1f01954546ba0680b 100644 (file)
@@ -65,23 +65,3 @@ data class UserSession(
                index(UserSession::user)
        })
 }
-
-/*
-@Serializable
-data class PrivateMessage(
-       @SerialName("_id")
-       override val id: Id<PrivateMessage> = Id(),
-       val sender: Id<User>,
-       val receiver: Id<User>,
-       val subject: String,
-       val message: String,
-       val sentAt: @Contextual Instant,
-       val isRead: Boolean
-) : DataDocument<PrivateMessage> {
-       companion object Table : DocumentTable<PrivateMessage> by DocumentTable.create({
-               index(PrivateMessage::sender)
-               index(PrivateMessage::receiver)
-               index(PrivateMessage::sentAt)
-       })
-}
-*/
index a498bd4ab66b2da0cdfe4e942bfa040ecdd6c6fe..9a845bd25068f413f61066f6691adbef5f1728fa 100644 (file)
@@ -6,8 +6,8 @@ import starshipfights.CurrentConfiguration
 
 @Serializable
 sealed class UserTrophy : Comparable<UserTrophy> {
-       protected abstract fun ASIDE.render()
-       fun renderInto(sidebar: ASIDE) = sidebar.render()
+       protected abstract fun TagConsumer<*>.render()
+       fun renderInto(consumer: TagConsumer<*>) = consumer.render()
        
        // Higher rank = lower on page
        protected abstract val rank: Int
@@ -16,11 +16,11 @@ sealed class UserTrophy : Comparable<UserTrophy> {
        }
 }
 
-fun ASIDE.renderTrophy(trophy: UserTrophy) = trophy.renderInto(this)
+fun TagConsumer<*>.renderTrophy(trophy: UserTrophy) = trophy.renderInto(this)
 
 @Serializable
 object SiteOwnerTrophy : UserTrophy() {
-       override fun ASIDE.render() {
+       override fun TagConsumer<*>.render() {
                p {
                        style = "text-align:center;border:2px solid #a82;padding:3px;background-color:#fc3;color:#541;font-variant:small-caps;font-family:'JetBrains Mono',monospace"
                        +"Site Owner"
@@ -33,7 +33,7 @@ object SiteOwnerTrophy : UserTrophy() {
 
 @Serializable
 object SiteDeveloperTrophy : UserTrophy() {
-       override fun ASIDE.render() {
+       override fun TagConsumer<*>.render() {
                p {
                        style = "text-align:center;border:2px solid #62a;padding:3px;background-color:#93f;color:#315;font-variant:small-caps;font-family:'JetBrains Mono',monospace"
                        title = "This person helps with coding the game"
@@ -47,7 +47,7 @@ object SiteDeveloperTrophy : UserTrophy() {
 
 @Serializable
 data class SiteJanitorTrophy(val isSenior: Boolean) : UserTrophy() {
-       override fun ASIDE.render() {
+       override fun TagConsumer<*>.render() {
                p {
                        style = "text-align:center;border:2px solid #840;padding:3px;background-color:#c60;color:#420;font-variant:small-caps;font-family:'JetBrains Mono',monospace"
                        title = "This person helps with cleaning the poo out of the site"
@@ -61,7 +61,7 @@ data class SiteJanitorTrophy(val isSenior: Boolean) : UserTrophy() {
 
 @Serializable
 data class SiteSupporterTrophy(val amountInUsCents: Int) : UserTrophy() {
-       override fun ASIDE.render() {
+       override fun TagConsumer<*>.render() {
                p {
                        style = "text-align:center;border:2px solid #694;padding:3px;background-color:#af7;color:#231;font-variant:small-caps;font-family:'JetBrains Mono',monospace"
                        title = "\"I spent money on an online game and all I got was this lousy trophy!\""
index 881ff9ed57192095fe562a703f11ff6ee9f25246..a068e85813ae93ee268ceb54969e2d6c02bb28e8 100644 (file)
@@ -1,8 +1,6 @@
 package starshipfights.info
 
-import kotlinx.html.A
-import kotlinx.html.FORM
-import kotlinx.html.hiddenInput
+import kotlinx.html.*
 import starshipfights.auth.CsrfProtector
 import starshipfights.data.Id
 import starshipfights.data.auth.UserSession
@@ -24,3 +22,16 @@ fun FORM.csrfToken(cookie: Id<UserSession>) = hiddenInput {
        name = CsrfProtector.csrfInputName
        value = CsrfProtector.newNonce(cookie, this@csrfToken.action)
 }
+
+fun interface SECTIONS {
+       fun section(classes: String?, body: SECTION.() -> Unit)
+       fun section(body: SECTION.() -> Unit) = section(null, body)
+}
+
+fun MAIN.sectioned(): SECTIONS = MainSections(this)
+
+private class MainSections(private val delegate: MAIN) : SECTIONS {
+       override fun section(classes: String?, body: SECTION.() -> Unit) {
+               delegate.section(classes, body)
+       }
+}
index 722cb3e4d9d3d93f87ae5d8caaae3db969678e5b..a9fa8eb0d726c86690e88f55f1efe68387ff9253 100644 (file)
@@ -5,16 +5,16 @@ import starshipfights.game.ShipType
 import starshipfights.game.getDefiniteShortName
 
 abstract class Sidebar {
-       protected abstract fun ASIDE.display()
-       fun displayIn(aside: ASIDE) = aside.display()
+       protected abstract fun TagConsumer<*>.display()
+       fun displayIn(aside: ASIDE) = aside.consumer.display()
 }
 
-class CustomSidebar(private val block: ASIDE.() -> Unit) : Sidebar() {
-       override fun ASIDE.display() = block()
+class CustomSidebar(private val block: TagConsumer<*>.() -> Unit) : Sidebar() {
+       override fun TagConsumer<*>.display() = block()
 }
 
 data class ShipViewSidebar(val shipType: ShipType) : Sidebar() {
-       override fun ASIDE.display() {
+       override fun TagConsumer<*>.display() {
                p {
                        img(alt = "Flag of ${shipType.faction.getDefiniteShortName()}", src = shipType.faction.flagUrl)
                }
@@ -28,7 +28,7 @@ data class ShipViewSidebar(val shipType: ShipType) : Sidebar() {
 }
 
 data class PageNavSidebar(val contents: List<NavItem>) : Sidebar() {
-       override fun ASIDE.display() {
+       override fun TagConsumer<*>.display() {
                div(classes = "list") {
                        contents.forEach {
                                div(classes = "item") {
index 47d7dbee47bdcf1a7d6e50f95e9f61135d8e0175..f7a89debd00827ccdc9ceb2f08d0716b0051681d 100644 (file)
@@ -24,9 +24,9 @@ data class NavHead(val label: String) : NavItem() {
        }
 }
 
-data class NavLink(val to: String, val text: String, val isPost: Boolean = false, val csrfUserCookie: Id<UserSession>? = null) : NavItem() {
+data class NavLink(val to: String, val text: String, val classes: String? = null, val isPost: Boolean = false, val csrfUserCookie: Id<UserSession>? = null) : NavItem() {
        override fun DIV.display() {
-               a(href = to) {
+               a(href = to, classes = classes) {
                        if (isPost)
                                method = "post"
                        csrfUserCookie?.let { csrfToken(it) }
@@ -51,17 +51,7 @@ suspend fun ApplicationCall.standardNavBar(): List<NavItem> = listOf(
                listOf(
                        NavLink("/me", user.profileName),
                        NavLink("/me/manage", "User Preferences"),
-                       /*NavLink(
-                               "/me/inbox", "Inbox (${
-                                       PrivateMessage.number(
-                                               and(
-                                                       PrivateMessage::receiver eq user.id,
-                                                       PrivateMessage::isRead eq false
-                                               )
-                                       )
-                               })"
-                       ),*/
-                       NavLink("/lobby", "Enter Game Lobby"),
+                       NavLink("/lobby", "Enter Game Lobby", classes = "desktop"),
                        NavLink("/logout", "Log Out", isPost = true, csrfUserCookie = session.id),
                )
 } + listOf(
index f45f755bf8f1f137d5758cd7975c52846e7b9006..4d34faca82f21c55ad106c7fcab66a6421efb9ff 100644 (file)
@@ -2,9 +2,10 @@ package starshipfights.info
 
 import kotlinx.html.*
 
-fun page(pageTitle: String? = null, navBar: List<NavItem>? = null, sidebar: Sidebar? = null, content: MAIN.() -> Unit): HTML.() -> Unit = {
+fun page(pageTitle: String? = null, navBar: List<NavItem>? = null, sidebar: Sidebar? = null, content: SECTIONS.() -> Unit): HTML.() -> Unit = {
        head {
                meta(charset = "utf-8")
+               meta(name = "viewport", content = "width=device-width, initial-scale=1.0")
                
                link(rel = "icon", type = "image/svg+xml", href = "/static/images/icon.svg")
                link(rel = "preconnect", href = "https://fonts.googleapis.com")
@@ -21,7 +22,7 @@ fun page(pageTitle: String? = null, navBar: List<NavItem>? = null, sidebar: Side
                div { id = "bg" }
                
                navBar?.let {
-                       nav {
+                       nav(classes = "desktop") {
                                div(classes = "list") {
                                        it.forEach {
                                                div(classes = "item") {
@@ -33,13 +34,33 @@ fun page(pageTitle: String? = null, navBar: List<NavItem>? = null, sidebar: Side
                }
                
                sidebar?.let {
-                       aside {
+                       aside(classes = "desktop") {
                                it.displayIn(this)
                        }
                }
                
                main {
-                       content()
+                       sidebar?.let {
+                               aside(classes = "mobile") {
+                                       it.displayIn(this)
+                               }
+                       }
+                       
+                       with(sectioned()) {
+                               content()
+                       }
+                       
+                       navBar?.let {
+                               nav(classes = "mobile") {
+                                       div(classes = "list") {
+                                               it.forEach {
+                                                       div(classes = "item") {
+                                                               it.displayIn(this)
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
                }
                
                script(src = "/static/init.js") {}
index d548aff0357c5d9ac4d6eb4381c631ab667bbfe0..a7eb0a93c5f16398a97f45a7c67f7991149b8ea1 100644 (file)
@@ -5,7 +5,7 @@ import io.ktor.features.*
 import kotlinx.html.*
 import starshipfights.CurrentConfiguration
 
-private fun MAIN.devModeCallId(callId: String?) {
+private fun SECTIONS.devModeCallId(callId: String?) {
        callId?.let { id ->
                section {
                        style = if (CurrentConfiguration.isDevEnv) "" else "display:none"
index 5650586c84472c9ed498523c5c2ba62fcfd7bbae..2293a2867efe72f0acac9cd15c75ff358b8174ae 100644 (file)
@@ -1,10 +1,15 @@
 html {
        margin: 0;
-       padding: 20px 5vw;
+       padding: 0;
        color: #222;
+       background-color: #aaa;
 
        font-family: 'Noto Sans', sans-serif;
        font-size: 100%;
+
+       --h1-size: 1.6em;
+       --h2-size: 1.4em;
+       --h3-size: 1.2em;
 }
 
 ::selection {
@@ -13,20 +18,7 @@ html {
 }
 
 div#bg {
-       width: 100%;
-       height: 100%;
-       position: fixed;
-       top: 0;
-       left: 0;
-
-       background-image: url("/static/images/background.svg");
-       background-attachment: fixed;
-       background-position: center;
-       background-size: cover;
-
-       filter: blur(4px);
-
-       z-index: 0;
+       display: none;
 }
 
 h1, h2, h3 {
@@ -39,60 +31,128 @@ h1, h2 {
 }
 
 h1 {
-       border: 3px solid #888;
-       box-shadow: inset 0 0 0 4px #444;
-       padding: 5px;
+       border: 0.1875rem solid #888;
+       box-shadow: inset 0 0 0 0.25rem #444;
+       padding: 0.3125rem;
 
        background-color: #aaa;
        font-variant: small-caps;
-       font-size: 2.6em;
+       font-size: var(--h1-size);
        font-weight: 800;
 }
 
 h2 {
-       border-bottom: 2px solid #666;
-       font-size: 2.2em;
+       border-bottom: 0.125rem solid #666;
+       font-size: var(--h2-size);
        font-weight: 600;
 }
 
 h3 {
        text-decoration: underline;
        text-decoration-color: #888;
-       font-size: 1.8em;
+       font-size: var(--h3-size);
        font-weight: 400;
 }
 
-main {
-       padding: 5vh 0;
+.desktop {
+       display: none;
 }
 
 /*noinspection CssOverwrittenProperties*/
-main > section, nav, aside {
+main > section, main > nav.mobile, main > aside.mobile {
        border-image-source: url("/static/images/panel.svg");
        border-image-slice: 40% fill;
-       border-image-width: 2em;
-       border-width: 2em;
+       border-image-width: 1em;
+       border-width: 1em;
+
+       padding: 1.5em 1.5em;
 
        box-sizing: border-box;
-       padding: 2.5em 3em;
+       width: 90vw;
+       margin: 5vw 5vw;
 
        position: relative;
        z-index: 1;
 }
 
-main > section {
-       width: 40%;
-       margin: 5vh auto;
-}
+@media only screen and (min-width: 8in) {
+       html {
+               padding: 1.25rem 5vw;
 
-main > section:first-child {
-       margin: 0 auto 5vh;
-}
+               --h1-size: 2.6em;
+               --h2-size: 2.2em;
+               --h3-size: 1.8em;
+       }
+
+       div#bg {
+               display: unset;
+
+               width: 100%;
+               height: 100%;
+               position: fixed;
+               top: 0;
+               left: 0;
+
+               background-image: url("/static/images/background.svg");
+               background-attachment: fixed;
+               background-position: center;
+               background-size: cover;
+
+               filter: blur(0.25rem);
+
+               z-index: 0;
+       }
 
-nav {
-       width: 20%;
-       float: left;
-       margin: 5vh 5vw;
+       main {
+               padding: 5vh 0;
+       }
+
+       /*noinspection CssOverwrittenProperties*/
+       main > section, nav.desktop, aside.desktop {
+               border-image-source: url("/static/images/panel.svg");
+               border-image-slice: 40% fill;
+               border-image-width: 2em;
+               border-width: 2em;
+
+               box-sizing: border-box;
+               padding: 2.5em 3em;
+
+               position: relative;
+               z-index: 1;
+       }
+
+       main > section {
+               width: 40%;
+               margin: 5vh auto;
+       }
+
+       main > section:first-of-type {
+               margin: 0 auto 5vh;
+       }
+
+       .mobile {
+               display: none;
+       }
+
+       .desktop {
+               display: unset;
+       }
+
+       nav.desktop {
+               width: 20%;
+               float: left;
+               margin: 5vh 5vw;
+       }
+
+       aside.desktop {
+               width: 20%;
+               float: right;
+               margin: 5vh 5vw;
+       }
+
+       aside.desktop img {
+               width: 100%;
+       }
 }
 
 div.list {
@@ -138,7 +198,7 @@ div.list > div.item > a {
        vertical-align: middle;
        text-align: center;
 
-       border-radius: 5px;
+       border-radius: 0.3em;
        color: #369;
        text-decoration: none;
 }
@@ -170,20 +230,10 @@ a[href^="http://"]::after, a[href^="https://"]::after {
        height: 1em;
 }
 
-aside {
-       width: 20%;
-       float: right;
-       margin: 5vh 5vw;
-}
-
-aside img {
-       width: 100%;
-}
-
 table {
        table-layout: fixed;
        border-collapse: collapse;
-       border: 2pt solid #036;
+       border: 0.125rem solid #036;
        background-color: #ccc;
 
        width: 100%;
@@ -200,7 +250,7 @@ table + table tr:first-child th {
 }
 
 td {
-       border: 2pt solid #036;
+       border: 0.125rem solid #036;
        background-color: #eee;
        font-size: 0.7em;
        padding: 0.15em 0;
@@ -210,7 +260,7 @@ td {
 }
 
 th {
-       border: 2pt solid #036;
+       border: 0.125rem solid #036;
        background-color: #036;
        padding: 0.15em 0;
 
@@ -231,7 +281,7 @@ textarea {
        box-sizing: border-box;
        background-color: #aaa;
        border: none;
-       border-bottom: 2px solid #222;
+       border-bottom: 0.1em solid #222;
 
        color: #024;
        font-size: 1.5em;
@@ -264,7 +314,7 @@ textarea:invalid {
 input[type=submit] {
        background-color: #06c;
        border: none;
-       border-radius: 8pt;
+       border-radius: 0.3em;
        color: #bdf;
        cursor: pointer;
        display: block;
@@ -285,7 +335,7 @@ input[type=submit]:active {
 input[type=submit].evil {
        background-color: #c66;
        border: none;
-       border-radius: 8pt;
+       border-radius: 0.3em;
        color: #fcc;
        cursor: pointer;
        display: block;