val errorMsg = call.request.queryParameters["error"]
- call.respondHtml(HttpStatusCode.OK, page("Authentication Test", call.standardNavBar(), CustomSidebar {
+ call.respondHtml(HttpStatusCode.OK, call.page("Authentication Test", call.standardNavBar(), CustomSidebar {
p {
- +"This instance does not have Discord OAuth login set up. As a fallback, this authentication mode is used for testing."
+ +"This instance does not have Discord OAuth login set up. As a fallback, this authentication mode is used for testing only."
}
}) {
section {
get("/login") {
val errorMsg = call.request.queryParameters["error"]
- call.respondHtml(HttpStatusCode.OK, page("Login with Discord", call.standardNavBar(), null) {
+ call.respondHtml(HttpStatusCode.OK, call.page("Login with Discord", call.standardNavBar()) {
section {
p {
style = "text-align:center"
--- /dev/null
+package starshipfights.info
+
+import kotlinx.html.HEAD
+
+data class PageMetadata(
+ val title: String,
+ val description: String,
+ val type: PageMetadataType,
+) {
+ companion object {
+ val default = PageMetadata(
+ title = "Starship Fights",
+ description = "Starship Fights is a space fleet battle game. Choose your allegiance, create your admiral, build up your fleet, and destroy your enemies' fleets with it!",
+ type = PageMetadataType.Website,
+ )
+ }
+}
+
+sealed class PageMetadataType {
+ object Website : PageMetadataType()
+
+ data class Profile(
+ val name: String,
+ val isFemale: Boolean?,
+ ) : PageMetadataType()
+}
+
+fun HEAD.metadata(pageMetadata: PageMetadata, url: String) {
+ when (pageMetadata.type) {
+ is PageMetadataType.Profile -> {
+ metaOG("og:type", "profile")
+ metaOG("og:profile:username", pageMetadata.type.name)
+ pageMetadata.type.isFemale?.let {
+ metaOG("og:profile:gender", if (it) "female" else "male")
+ }
+ }
+ PageMetadataType.Website -> {
+ metaOG("og:type", "website")
+ }
+ }
+
+ metaOG("og:title", pageMetadata.title)
+ metaOG("og:description", pageMetadata.description)
+ metaOG("og:site_name", "Starship Fights")
+ metaOG("og:url", url)
+ metaOG("og:image", "https://starshipfights.net/static/images/embed-logo.png")
+}
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
name = CsrfProtector.csrfInputName
value = CsrfProtector.newNonce(cookie, this@csrfToken.action)
}
+
+var META.property: String?
+ get() = attributes["property"]
+ set(value) {
+ if (value != null)
+ attributes["property"] = value
+ else
+ attributes.remove("property")
+ }
+
+fun HEAD.metaOG(property: String, content: String) = meta {
+ this.property = property
+ this.content = content
+}
package starshipfights.info
+import io.ktor.application.*
+import io.ktor.util.*
import kotlinx.html.*
-fun page(pageTitle: String?, navBar: List<NavItem>?, sidebar: Sidebar?, content: MAIN.() -> Unit): HTML.() -> Unit = {
+fun ApplicationCall.page(pageTitle: String? = null, navBar: List<NavItem>? = null, sidebar: Sidebar? = null, pageData: PageMetadata = PageMetadata.default, content: MAIN.() -> Unit): HTML.() -> Unit = {
head {
meta(charset = "utf-8")
+ metadata(pageData, url())
+
link(rel = "icon", type = "image/svg+xml", href = "/static/images/icon.svg")
link(rel = "preconnect", href = "https://fonts.googleapis.com")
link(rel = "preconnect", href = "https://fonts.gstatic.com") { attributes["crossorigin"] = "anonymous" }
}
}
-suspend fun ApplicationCall.error400(): HTML.() -> Unit = page("Bad Request", standardNavBar(), IndexSidebar) {
+suspend fun ApplicationCall.error400(): HTML.() -> Unit = page("Bad Request", standardNavBar()) {
section {
h1 { +"Bad Request" }
p { +"The request your browser sent was improperly formatted." }
devModeCallId(callId)
}
-suspend fun ApplicationCall.error403(): HTML.() -> Unit = page("Not Allowed", standardNavBar(), IndexSidebar) {
+suspend fun ApplicationCall.error403(): HTML.() -> Unit = page("Not Allowed", standardNavBar()) {
section {
h1 { +"Not Allowed" }
p { +"You are not allowed to do that." }
devModeCallId(callId)
}
-suspend fun ApplicationCall.error404(): HTML.() -> Unit = page("Not Found", standardNavBar(), IndexSidebar) {
+suspend fun ApplicationCall.error404(): HTML.() -> Unit = page("Not Found", standardNavBar()) {
section {
h1 { +"Not Found" }
p { +"Unfortunately, we could not find what you were looking for." }
devModeCallId(callId)
}
-suspend fun ApplicationCall.error503(): HTML.() -> Unit = page("Internal Error", standardNavBar(), IndexSidebar) {
+suspend fun ApplicationCall.error503(): HTML.() -> Unit = page("Internal Error", standardNavBar()) {
section {
h1 { +"Internal Error" }
p { +"The servers made a bit of a mistake. Please be patient while we fix our mess." }
suspend fun ApplicationCall.newUsersPage(): HTML.() -> Unit {
val newUsers = User.sorted(descending(User::registeredAt)).take(20).toList()
- return page("New Users", standardNavBar(), IndexSidebar) {
+ return page("New Users", standardNavBar()) {
section {
h1 { +"New Users" }
div {
}
}
-suspend fun ApplicationCall.shipPage(shipType: ShipType): HTML.() -> Unit = page(shipType.fullerDisplayName, standardNavBar(), ShipViewSidebar(shipType)) {
+suspend fun ApplicationCall.shipPage(shipType: ShipType): HTML.() -> Unit = page(
+ shipType.fullerDisplayName,
+ standardNavBar(),
+ ShipViewSidebar(shipType),
+ PageMetadata(
+ shipType.displayName + "-class Starship",
+ "A ${shipType.weightClass.displayName} of the ${shipType.faction.navyName}",
+ PageMetadataType.Website
+ )
+) {
section {
h1 { +shipType.fullDisplayName }
}
}
}*/
- }
+ },
+ PageMetadata(
+ user.profileName,
+ user.profileBio,
+ PageMetadataType.Profile(
+ user.profileName,
+ null
+ )
+ )
) {
section {
h1 { +user.profileName }
NavLink("/admiral/${admiral.id}/manage", "Manage Admiral")
)
else emptyList()
+ ),
+ PageMetadata(
+ admiral.name,
+ "${admiral.rank.getDisplayName(admiral.faction)} of the ${admiral.faction.navyName}",
+ PageMetadataType.Profile(
+ admiral.name,
+ admiral.isFemale
+ )
)
) {
section {
val redirectUrlOrigin: String,
val clientId: String,
+ //val clientPubKey: String,
val clientSecret: String,
val ownerId: String,