package info.mechyrdia.lore
import info.mechyrdia.Configuration
+import io.ktor.server.application.*
import io.ktor.util.*
import java.io.File
import java.time.Instant
return zonedDateTime.month == Month.APRIL && zonedDateTime.dayOfMonth == 1
}
+context(ApplicationCall)
fun redirectFileOnApril1st(requestedFile: File): File? {
- if (!isApril1st()) return null
+ if (!april1stMode.isEnabled) return null
val rootDir = File(Configuration.CurrentConfiguration.rootDir)
val requestedPath = requestedFile.absoluteFile.toRelativeString(rootDir.absoluteFile)
return funnyFile.takeIf { it.exists() }
}
+context(ApplicationCall)
fun getAssetFile(requestedFile: File): File {
return redirectFileOnApril1st(requestedFile) ?: requestedFile
}
+
+suspend fun ApplicationCall.respondAsset(assetFile: File) {
+ respondCompressedFile(getAssetFile(assetFile))
+}
package info.mechyrdia.lore
import info.mechyrdia.auth.PageDoNotCacheAttributeKey
+import info.mechyrdia.route.KeyedEnumSerializer
import io.ktor.server.application.*
import kotlinx.html.*
+import kotlinx.serialization.Serializable
+
+@Serializable(PageThemeSerializer::class)
+enum class PageTheme(val attributeValue: String?) {
+ SYSTEM(null),
+ LIGHT("light"),
+ DARK("dark");
+}
+
+object PageThemeSerializer : KeyedEnumSerializer<PageTheme>(PageTheme.entries, PageTheme::attributeValue)
+
+val ApplicationCall.pageTheme: PageTheme
+ get() = when (request.cookies["FACTBOOK_THEME"]) {
+ "light" -> PageTheme.LIGHT
+ "dark" -> PageTheme.DARK
+ else -> PageTheme.SYSTEM
+ }
+
+@Serializable(with = April1stModeSerializer::class)
+enum class April1stMode {
+ DEFAULT {
+ override val isEnabled: Boolean
+ get() = isApril1st()
+ },
+ ALWAYS {
+ override val isEnabled: Boolean
+ get() = true
+ },
+ NEVER {
+ override val isEnabled: Boolean
+ get() = false
+ };
+
+ abstract val isEnabled: Boolean
+}
+
+object April1stModeSerializer : KeyedEnumSerializer<April1stMode>(April1stMode.entries)
+
+val ApplicationCall.april1stMode: April1stMode
+ get() = when (request.cookies["APRIL_1ST_MODE"]) {
+ "always" -> April1stMode.ALWAYS
+ "never" -> April1stMode.NEVER
+ else -> April1stMode.DEFAULT
+ }
suspend fun ApplicationCall.clientSettingsPage(): HTML.() -> Unit {
attributes.put(PageDoNotCacheAttributeKey, true)
- val theme = when (request.cookies["FACTBOOK_THEME"]) {
- "light" -> "light"
- "dark" -> "dark"
- else -> null
- }
+ val theme = pageTheme
+ val april1st = april1stMode
return page("Client Preferences", standardNavBar()) {
section {
h1 { +"Client Preferences" }
+ p { +"This is the place where you can adjust your client preferences. Selecting an option changes it automatically, so you don't need to click any kind of \"save\" button. Also, note that preferences are saved per-browser in your cookies, so don't expect your client-side preferences to travel with you to other devices." }
+ }
+ section {
+ h2 { +"Page Theme" }
label {
radioInput(name = "theme") {
- id = "system-theme"
value = "system"
required = true
- checked = theme == null
+ checked = theme == PageTheme.SYSTEM
}
+Entities.nbsp
+"System Choice"
br
label {
radioInput(name = "theme") {
- id = "light-theme"
value = "light"
required = true
- checked = theme == "light"
+ checked = theme == PageTheme.LIGHT
}
+Entities.nbsp
+"Light Theme"
br
label {
radioInput(name = "theme") {
- id = "dark-theme"
value = "dark"
required = true
- checked = theme == "dark"
+ checked = theme == PageTheme.DARK
}
+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
+ }
+ +Entities.nbsp
+ +"Never"
+ }
+ }
}
}
});
window.addEventListener("load", function () {
- // Set client theme when selected
+ // Set client preferences when selected
const themeChoices = document.getElementsByName("theme");
for (const themeChoice of themeChoices) {
themeChoice.addEventListener("click", e => {
const theme = e.currentTarget.value;
document.documentElement.setAttribute("data-theme", theme);
- document.cookie = "FACTBOOK_THEME=" + theme + "; secure; max-age=" + (Math.pow(2, 31) - 1).toString();
+ document.cookie = "FACTBOOK_THEME=" + theme + "; Secure; SameSite=Lax; Max-Age=" + (Math.pow(2, 31) - 1).toString();
+ });
+ }
+
+ const april1stChoices = document.getElementsByName("april1st");
+ for (const april1stChoice of april1stChoices) {
+ april1stChoice.addEventListener("click", e => {
+ const mode = e.currentTarget.value;
+ document.cookie = "APRIL_1ST_MODE=" + mode + "; Secure; SameSite=None; Max-Age=" + (Math.pow(2, 31) - 1).toString();
});
}
});