From b9a392f5f1db5c7de60e929fdcc0ca42885f81ca Mon Sep 17 00:00:00 2001 From: Marvin Borner Date: Mon, 13 May 2019 18:18:57 +0200 Subject: Added user based dark theme support Co-authored-by: LarsVomMars --- src/main/kotlin/App.kt | 11 ++++-- src/main/kotlin/DatabaseController.kt | 36 ++++++++++++++++-- src/main/kotlin/FileController.kt | 9 +++-- src/main/kotlin/UserHandler.kt | 54 +++++++++++++++++++-------- src/main/resources/css/darkLayout.css | 32 ++++++++++++++++ src/main/resources/css/index.css | 6 +++ src/main/resources/js/index.js | 6 +++ src/main/resources/views/admin.rocker.html | 5 ++- src/main/resources/views/files.rocker.html | 5 ++- src/main/resources/views/fileview.rocker.html | 5 ++- src/main/resources/views/index.rocker.html | 17 ++++++++- src/main/resources/views/layout.rocker.html | 10 ++++- src/main/resources/views/login.rocker.html | 5 ++- src/main/resources/views/register.rocker.html | 5 ++- src/main/resources/views/setup.rocker.html | 5 ++- 15 files changed, 172 insertions(+), 39 deletions(-) create mode 100644 src/main/resources/css/darkLayout.css create mode 100644 src/main/resources/css/index.css create mode 100644 src/main/resources/js/index.js (limited to 'src') diff --git a/src/main/kotlin/App.kt b/src/main/kotlin/App.kt index df845a1..3607702 100644 --- a/src/main/kotlin/App.kt +++ b/src/main/kotlin/App.kt @@ -14,11 +14,11 @@ import java.util.logging.* import kotlin.system.* // TODO: Add abstract and secure file home support for windows/BSD/macOS -val fileHome = if (System.getProperty("os.name") != "Linux") "files" else "/usr/share/kloud/files" +const val debug = true +val fileHome = if (System.getProperty("os.name") != "Linux" || debug) "files" else "/usr/share/kloud/files" val databaseController = DatabaseController() val userHandler = UserHandler() val fileController = FileController() -const val debug = false private val log = Logger.getLogger("App.kt") fun main(args: Array) { @@ -82,7 +82,7 @@ fun main(args: Array) { get("/", { ctx -> ctx.render( "index.rocker.html", - model("username", databaseController.getUsername(userHandler.getVerifiedUserId(ctx))) + model("username", databaseController.getUsername(userHandler.getVerifiedUserId(ctx)), "ctx", ctx) ) }, roles(Roles.GUEST, Roles.USER)) @@ -101,6 +101,11 @@ fun main(args: Array) { */ get("/user/logout", userHandler::logout, roles(Roles.USER)) + /** + * Toggles the users theme + */ + post("/user/theme", userHandler::toggleTheme, roles(Roles.USER)) + /** * Renders the registration page */ diff --git a/src/main/kotlin/DatabaseController.kt b/src/main/kotlin/DatabaseController.kt index 24a8278..bdbdc18 100644 --- a/src/main/kotlin/DatabaseController.kt +++ b/src/main/kotlin/DatabaseController.kt @@ -35,6 +35,7 @@ class DatabaseController { val username = varchar("username", 24).uniqueIndex() val password = varchar("password", 64) val verification = varchar("verification", 64).uniqueIndex() + val darkTheme = bool("darkTheme").default(false) } /** @@ -161,13 +162,14 @@ class DatabaseController { } } - if (error) ctx.render("admin.rocker.html", model("message", "User already exists!")) + if (error) ctx.render("admin.rocker.html", model("message", "User already exists!", "ctx", ctx)) else ctx.render( "admin.rocker.html", model( - "message", "http://${ctx.host()}/user/register?username=$usernameString&token=$tokenString" + "message", "http://${ctx.host()}/user/register?username=$usernameString&token=$tokenString", + "ctx", ctx ) ) - } else ctx.render("admin.rocker.html", model("message", "Please only use alphabetical characters!")) + } else ctx.render("admin.rocker.html", model("message", "Please only use alphabetical characters!", "ctx", ctx)) } /** @@ -220,6 +222,34 @@ class DatabaseController { } } + /** + * Returns true when user uses dark theme + */ + fun isDarkTheme(userId: Int): Boolean { + return transaction { + try { + UserData.select { UserData.id eq userId }.map { it[UserData.darkTheme] }[0] + } catch (_: Exception) { + false + } + } + } + + /** + * Toggles the dark theme + */ + fun toggleDarkTheme(userId: Int) { + return transaction { + try { + UserData.update({ (UserData.id eq userId) }) { + it[darkTheme] = !isDarkTheme(userId) + } + } catch (_: Exception) { + // + } + } + } + /** * Returns the corresponding verification id using [usernameString] */ diff --git a/src/main/kotlin/FileController.kt b/src/main/kotlin/FileController.kt index 32c9369..d62b4b4 100644 --- a/src/main/kotlin/FileController.kt +++ b/src/main/kotlin/FileController.kt @@ -36,7 +36,8 @@ class FileController { "files.rocker.html", model( "files", files, "path", (if (firstParam.firstOrNull() == '/') firstParam.drop(1) else firstParam), - "isShared", false + "isShared", false, + "ctx", ctx ) ) } @@ -178,7 +179,8 @@ class FileController { "files.rocker.html", model( "files", files, "path", (if (fileLocation.firstOrNull() == '/') fileLocation.drop(1) else fileLocation), - "isShared", true + "isShared", true, + "ctx", ctx ) ) } @@ -216,7 +218,8 @@ class FileController { Charsets.UTF_8 ).joinToString(separator = "\n"), "filename", File(filePath).name, - "extension", File(filePath).extension + "extension", File(filePath).extension, + "ctx", ctx ) ) } diff --git a/src/main/kotlin/UserHandler.kt b/src/main/kotlin/UserHandler.kt index 33f3f14..e9d0ada 100644 --- a/src/main/kotlin/UserHandler.kt +++ b/src/main/kotlin/UserHandler.kt @@ -14,7 +14,7 @@ class UserHandler { */ fun renderLogin(ctx: Context) { if (userHandler.getVerifiedUserId(ctx) > 0 || !databaseController.isSetup()) ctx.redirect("/") - else ctx.render("login.rocker.html", model("message", "", "counter", 0)) + else ctx.render("login.rocker.html", model("message", "", "counter", 0, "ctx", ctx)) } /** @@ -54,7 +54,8 @@ class UserHandler { model( "message", "Login failed!", - "counter", if (nextThreshold / 60 > 60) 3600 else nextThreshold.toInt() + "counter", if (nextThreshold / 60 > 60) 3600 else nextThreshold.toInt(), + "ctx", ctx ) ) } @@ -65,7 +66,8 @@ class UserHandler { model( "message", "Too many request.", - "counter", if (nextThreshold / 60 > 60) 3600 else nextThreshold.toInt() + "counter", if (nextThreshold / 60 > 60) 3600 else nextThreshold.toInt(), + "ctx", ctx ) ) } @@ -80,11 +82,20 @@ class UserHandler { ctx.redirect("/") } + /** + * Toggles the users dark theme + */ + fun toggleTheme(ctx: Context) { + databaseController.toggleDarkTheme(userHandler.getVerifiedUserId(ctx)) + val dark = databaseController.isDarkTheme(userHandler.getVerifiedUserId(ctx)) + ctx.json(mapOf("dark" to dark)) + } + /** * Renders the admin interface */ fun renderAdmin(ctx: Context) { - ctx.render("admin.rocker.html", model("message", "")) + ctx.render("admin.rocker.html", model("message", "", "ctx", ctx)) } /** @@ -92,7 +103,7 @@ class UserHandler { */ fun renderSetup(ctx: Context) { if (databaseController.isSetup()) ctx.redirect("/user/login") - else ctx.render("setup.rocker.html", model("message", "")) + else ctx.render("setup.rocker.html", model("message", "", "ctx", ctx)) } /** @@ -105,21 +116,30 @@ class UserHandler { val verifyPassword = ctx.formParam("verifyPassword").toString() // TODO: Clean up ugly if statements in validation - if (!username.matches("[a-zA-Z0-9]+".toRegex()) || username.length <= 3) { + if (username.matches("[a-zA-Z0-9]+".toRegex()) && username.length > 3) { if (password == verifyPassword) { if (password.length >= 8) if (databaseController.createUser(username, password, "ADMIN")) { databaseController.toggleSetup() ctx.redirect("/user/login") - } else ctx.status(400).render("setup.rocker.html", model("message", "User already exists!")) - else ctx.status(400).render("setup.rocker.html", model("message", "Password is too short!")) - } else ctx.status(400).render("setup.rocker.html", model("message", "Passwords do not match!")) + } else ctx.status(400).render( + "setup.rocker.html", + model("message", "User already exists!", "ctx", ctx) + ) + else ctx.status(400).render( + "setup.rocker.html", + model("message", "Password is too short!", "ctx", ctx) + ) + } else ctx.status(400).render( + "setup.rocker.html", + model("message", "Passwords do not match!", "ctx", ctx) + ) } else ctx.status(400).render( "setup.rocker.html", - model("message", "Username must only use alphabetical characters!") + model("message", "Username must only use alphabetical characters!", "ctx", ctx) ) } catch (err: Exception) { - ctx.status(400).render("setup.rocker.html", model("message", "An error occurred!")) + ctx.status(400).render("setup.rocker.html", model("message", "An error occurred!", "ctx", ctx)) error(err) } } @@ -135,7 +155,10 @@ class UserHandler { else if (token.isNullOrEmpty()) throw ForbiddenResponse("Please provide a valid token!") else { if (databaseController.isUserRegistrationValid(username, token)) - ctx.render("register.rocker.html", model("username", username, "token", token, "message", "")) + ctx.render( + "register.rocker.html", + model("username", username, "token", token, "message", "", "ctx", ctx) + ) else ctx.redirect("/user/login") } } @@ -158,19 +181,20 @@ class UserHandler { ctx.redirect("/user/login") } else ctx.render( "register.rocker.html", - model("username", username, "token", token, "message", "Not authorized!") + model("username", username, "token", token, "message", "Not authorized!", "ctx", ctx) ) else ctx.render( "register.rocker.html", model( "username", username, "token", token, - "message", "Please make sure that your password is at least 8 digits long!" + "message", "Please make sure that your password is at least 8 digits long!", + "ctx", ctx ) ) } else ctx.render( "register.rocker.html", - model("username", username, "token", token, "message", "The passwords don't match!") + model("username", username, "token", token, "message", "The passwords don't match!", "ctx", ctx) ) } catch (err: Exception) { throw BadRequestResponse() diff --git a/src/main/resources/css/darkLayout.css b/src/main/resources/css/darkLayout.css new file mode 100644 index 0000000..71172a5 --- /dev/null +++ b/src/main/resources/css/darkLayout.css @@ -0,0 +1,32 @@ +/** + Main + */ +body { + background-color: #181a1b; +} + +.main { + color: #d0cdc6 +} + +button { + text-decoration-color: initial; + background-color: #3d4043; + border-top-color: #595959; + border-right-color: #595959; + border-bottom-color: #595959; + border-left-color: #595959; + color: #ffffff; +} + +input { + border-color: #575757; + color: #e8e6e3; +} + +/** + Other stuff + */ +tr:hover { + background-color: #121516 !important; +} diff --git a/src/main/resources/css/index.css b/src/main/resources/css/index.css new file mode 100644 index 0000000..5cf3c78 --- /dev/null +++ b/src/main/resources/css/index.css @@ -0,0 +1,6 @@ +.toggle { + position: absolute; + margin: 20px; + right: 0; + top: 0; +} diff --git a/src/main/resources/js/index.js b/src/main/resources/js/index.js new file mode 100644 index 0000000..4939622 --- /dev/null +++ b/src/main/resources/js/index.js @@ -0,0 +1,6 @@ +document.querySelector("#toggle").addEventListener("click", () => { + const request = new XMLHttpRequest(); + request.open("POST", "/user/theme"); + request.onload = () => location.reload(); + request.send(); +}); diff --git a/src/main/resources/views/admin.rocker.html b/src/main/resources/views/admin.rocker.html index 26859eb..7bd25a9 100644 --- a/src/main/resources/views/admin.rocker.html +++ b/src/main/resources/views/admin.rocker.html @@ -1,6 +1,7 @@ -@args (String message) +@import io.javalin.* +@args (String message, Context ctx) -@layout.template("Index", RockerContent.NONE, RockerContent.NONE) -> { +@layout.template("Index", ctx, RockerContent.NONE, RockerContent.NONE) -> {

Add new user

diff --git a/src/main/resources/views/files.rocker.html b/src/main/resources/views/files.rocker.html index 43a3488..8e75ac2 100644 --- a/src/main/resources/views/files.rocker.html +++ b/src/main/resources/views/files.rocker.html @@ -1,5 +1,6 @@ @import java.util.ArrayList -@args (ArrayList files, String path, Boolean isShared) +@import io.javalin.* +@args (ArrayList files, String path, Boolean isShared, Context ctx) @css => { @@ -14,7 +15,7 @@ } -@layout.template(files.size() + " Files", css, js) -> { +@layout.template(files.size() + " Files", ctx, css, js) -> {