This commit is contained in:
Hartmut Bauer committed 2026-10-19 01:04:50 +02:00
1 parent 8976e25499
commit e08a941c62
15 files changed
+223 -103

No files matched your search

+2 -2
View File
@@ -1,3 +1,3 @@
#auto generated #auto generated
#Sun Oct 11 23:49:03 CEST 2026 #Mon Oct 19 00:45:22 CEST 2026
build.number=582 build.number=633
+6 -6
View File
@@ -4,13 +4,13 @@ import net.keinekekse.version.Version
object VERSION: Version { object VERSION: Version {
override val version = "0.2.0-alpha" override val version = "0.2.0-alpha"
override val fullVersion = "0.2.0-alpha+build.582" override val fullVersion = "0.2.0-alpha+build.633"
override val major = 0 override val major = 0
override val minor = 2 override val minor = 2
override val patch = 0 override val patch = 0
override val preRelease = "alpha" override val preRelease = "alpha"
override val buildMetadata = "build.582" override val buildMetadata = "build.633"
override val buildDate = "2026-10-11T21:49:03.887757Z" override val buildDate = "2026-10-18T22:45:22.165046Z"
override val displayName = "Application Server" override val displayName = "Application Server"
override val copyright = "Copyright (c) 2026 Hartmut Bauer" override val copyright = "Copyright (c) 2026 Hartmut Bauer"
override val name = "server" override val name = "server"
@@ -18,13 +18,13 @@ object VERSION: Version {
} }
object VERSION_books: Version { object VERSION_books: Version {
override val version = "0.2.0-alpha" override val version = "0.2.0-alpha"
override val fullVersion = "0.2.0-alpha+build.582" override val fullVersion = "0.2.0-alpha+build.633"
override val major = 0 override val major = 0
override val minor = 2 override val minor = 2
override val patch = 0 override val patch = 0
override val preRelease = "alpha" override val preRelease = "alpha"
override val buildMetadata = "build.582" override val buildMetadata = "build.633"
override val buildDate = "2026-10-11T21:49:03.887855Z" override val buildDate = "2026-10-18T22:45:22.165140Z"
override val displayName = "Books-DB" override val displayName = "Books-DB"
override val copyright = "Copyright (c) 2026 Hartmut Bauer" override val copyright = "Copyright (c) 2026 Hartmut Bauer"
override val name = "books" override val name = "books"
+58 -5
View File
@@ -17,6 +17,8 @@ import kotlinx.html.span
import net.keinekekse.app.books.db.BookRepository import net.keinekekse.app.books.db.BookRepository
import net.keinekekse.app.books.db.CalibreBookInfoRepositoryImpl import net.keinekekse.app.books.db.CalibreBookInfoRepositoryImpl
import net.keinekekse.app.books.db.schema.emptyBook import net.keinekekse.app.books.db.schema.emptyBook
import net.keinekekse.app.books.edit.getSession
import net.keinekekse.app.books.edit.newSession
import net.keinekekse.app.books.scan.getCover import net.keinekekse.app.books.scan.getCover
import net.keinekekse.app.books.scan.getIsbnFromCalibre import net.keinekekse.app.books.scan.getIsbnFromCalibre
import net.keinekekse.app.books.ui.* import net.keinekekse.app.books.ui.*
@@ -75,8 +77,9 @@ fun Application.configureRouting(
get("${navBooksScanEdit.second}/{id}") { get("${navBooksScanEdit.second}/{id}") {
val id = call.parameters["id"]?.toInt() ?: 0 val id = call.parameters["id"]?.toInt() ?: 0
val bookInfo = CalibreBookInfoRepositoryImpl(db).readBookInfo(id) val bookInfo = CalibreBookInfoRepositoryImpl(db).readBookInfo(id)
val session = newSession(call.principal.userId, sessions)
call.respondHtml { call.respondHtml {
importEditPage(bookInfo, booksRepo) importEditPage(bookInfo, booksRepo, session)
} }
} }
delete("${navBooksScanEdit.second}/{id}") { delete("${navBooksScanEdit.second}/{id}") {
@@ -108,21 +111,71 @@ fun Application.configureRouting(
get("${navBooksEdit.second}/{id}") { get("${navBooksEdit.second}/{id}") {
val id = call.parameters["id"]?.toInt() ?: 0 val id = call.parameters["id"]?.toInt() ?: 0
val book = booksRepo.bookById(id) ?: emptyBook() val book = booksRepo.bookById(id) ?: emptyBook()
call.respondHtml { bookEditPage(book, booksRepo) } val session = newSession(call.principal.userId, sessions)
session.data.book = book
call.respondHtml { bookEditPage(booksRepo, session) }
} }
hx.post(navBooksEdit.second) { hx.post(navBooksEdit.second) {
call.application.log.info("## Edit book form OK") call.application.log.info("## Edit book form OK ")
call.receiveParameters().forEach { param: String, value: List<String> -> val receiveParameters = call.receiveParameters()
receiveParameters.forEach { param: String, value: List<String> ->
call.application.log.info("## param: $param, value: $value") call.application.log.info("## param: $param, value: $value")
} }
val session =
getSession(receiveParameters["sessionId"], sessions, call.principal.userId)
call.respondHtmlPartial(HttpStatusCode.OK) { call.respondHtmlPartial(HttpStatusCode.OK) {
p { p {
+"OK" +"OK ${session.sessionId}"
} }
} }
} }
hx.post("${navBooksEdit.second}/addpublisher") {
val receiveParameters = call.receiveParameters()
val session =
getSession(receiveParameters["sessionId"], sessions, call.principal.userId)
}
hx.post("${navBooksEdit.second}/addseries") {
val receiveParameters = call.receiveParameters()
val session =
getSession(receiveParameters["sessionId"], sessions, call.principal.userId)
}
hx.post("${navBooksEdit.second}/addtag") {
val receiveParameters = call.receiveParameters()
val session =
getSession(receiveParameters["sessionId"], sessions, call.principal.userId)
}
hx.post("${navBooksEdit.second}/addnewtag") {
val receiveParameters = call.receiveParameters()
val session =
getSession(receiveParameters["sessionId"], sessions, call.principal.userId)
}
hx.post("${navBooksEdit.second}/rmtag") {
val receiveParameters = call.receiveParameters()
val session =
getSession(receiveParameters["sessionId"], sessions, call.principal.userId)
}
hx.post("${navBooksEdit.second}/addartist") {
val receiveParameters = call.receiveParameters()
val session =
getSession(receiveParameters["sessionId"], sessions, call.principal.userId)
}
hx.post("${navBooksEdit.second}/addnewartist") {
val receiveParameters = call.receiveParameters()
val session =
getSession(receiveParameters["sessionId"], sessions, call.principal.userId)
}
hx.post("${navBooksEdit.second}/rmartist") {
val receiveParameters = call.receiveParameters()
val session =
getSession(receiveParameters["sessionId"], sessions, call.principal.userId)
}
hx.post("${navBooksEdit.second}/changeartistrole") {
val receiveParameters = call.receiveParameters()
val session =
getSession(receiveParameters["sessionId"], sessions, call.principal.userId)
}
// hx.get("/isbnlookupreset") { // hx.get("/isbnlookupreset") {
// call.respondHtmlPartial(HttpStatusCode.OK) { // call.respondHtmlPartial(HttpStatusCode.OK) {
@@ -1,14 +1,43 @@
package net.keinekekse.app.books.edit package net.keinekekse.app.books.edit
import net.keinekekse.app.books.model.*
import net.keinekekse.app.core.auth.Sessions
import net.keinekekse.app.core.auth.UserSession import net.keinekekse.app.core.auth.UserSession
class BooksSession( fun newSession(uid: String, sessions: Sessions): UserSession<BooksSessionData> {
override val userId: String, val session = UserSession(uid, Sessions.newSessionId(), BooksSessionData())
override val sessionId: String, sessions.newSession(session)
override var data: BooksSessionData return session
) : UserSession<BooksSessionData> }
fun getSession(sessionId: String?, sessions: Sessions, uid: String): UserSession<BooksSessionData> {
// TODO besseres Fehlerhandling
if (sessionId == null) throw IllegalArgumentException("sessionId is required")
val session =
sessions.getSession(sessionId)
?: throw IllegalArgumentException("no session for sessionId ${sessionId}")
if (session.userId != uid) throw IllegalArgumentException("session is invalid: userId != ${uid}")
if (session.data is BooksSessionData) {
return session as UserSession<BooksSessionData>
}
throw IllegalArgumentException("session.data has wrong type")
}
class BooksSessionData() { class BooksSessionData {
lateinit var book: Book
lateinit var publisherList: List<Publisher>
lateinit var seriesList: List<Series>
lateinit var artistList: List<Artist>
lateinit var tagList: List<Tag>
var newPublisherList: MutableList<Publisher> = mutableListOf()
var newSeriesList: MutableList<Series> = mutableListOf()
var addTagList: MutableList<Tag> = mutableListOf()
var newTagList: MutableList<Tag> = mutableListOf()
var rmTagList: MutableList<Tag> = mutableListOf()
var addArtistList: MutableList<ArtistForBook> = mutableListOf()
var newArtistList: MutableList<ArtistForBook> = mutableListOf()
var rmArtistList: MutableList<ArtistForBook> = mutableListOf()
var changeArtistRoleList: MutableList<ArtistForBook> = mutableListOf()
} }
+53 -29
View File
@@ -4,21 +4,26 @@ import io.ktor.htmx.html.*
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.html.* import kotlinx.html.*
import net.keinekekse.app.books.db.BookRepository import net.keinekekse.app.books.db.BookRepository
import net.keinekekse.app.books.model.* import net.keinekekse.app.books.edit.BooksSessionData
import net.keinekekse.app.books.model.Tag import net.keinekekse.app.books.model.BookType
import net.keinekekse.app.books.model.Languages
import net.keinekekse.app.core.auth.UserSession
fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) { fun FlowContent.bookEdit(
booksRepo: BookRepository,
session: UserSession<BooksSessionData>,
) {
val publishers: List<Publisher> = runBlocking { session.data.publisherList = runBlocking {
booksRepo.allPublishers() booksRepo.allPublishers()
} }
val series: List<Series> = runBlocking { session.data.seriesList = runBlocking {
booksRepo.allSeries() booksRepo.allSeries()
} }
val artists: List<Artist> = runBlocking { session.data.artistList = runBlocking {
booksRepo.allArtists() booksRepo.allArtists()
} }
val tags: List<Tag> = runBlocking { session.data.tagList = runBlocking {
booksRepo.allTags() booksRepo.allTags()
} }
@@ -29,8 +34,11 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) {
target = "#response" target = "#response"
swap = "innerHTML" swap = "innerHTML"
} }
input(type = InputType.hidden, name = "sessionId") {
value = session.sessionId
}
input(type = InputType.hidden, name = "bookId") { input(type = InputType.hidden, name = "bookId") {
value = book.id.toString() value = session.data.book.id.toString()
} }
div(classes = "flex flex-row mb-5 h-9") { div(classes = "flex flex-row mb-5 h-9") {
@@ -43,7 +51,7 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) {
for (type in BookType.entries) { for (type in BookType.entries) {
option { option {
if (type == book.type) if (type == session.data.book.type)
selected = true selected = true
value = type.ordinal.toString() value = type.ordinal.toString()
+type.name +type.name
@@ -57,7 +65,7 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) {
} }
input(classes = "$cssTextInput w-150") { input(classes = "$cssTextInput w-150") {
name = "title" name = "title"
value = book.title value = session.data.book.title
} }
} }
@@ -67,7 +75,7 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) {
} }
input(classes = "$cssTextInput w-150") { input(classes = "$cssTextInput w-150") {
name = "originaltitle" name = "originaltitle"
value = book.originalTitle ?: "" value = session.data.book.originalTitle ?: ""
} }
} }
@@ -77,7 +85,7 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) {
} }
input(classes = "$cssTextInput w-150") { input(classes = "$cssTextInput w-150") {
name = "subtitle" name = "subtitle"
value = book.subTitle ?: "" value = session.data.book.subTitle ?: ""
} }
} }
div(classes = "flex flex-row mb-5 h-9") { div(classes = "flex flex-row mb-5 h-9") {
@@ -89,7 +97,7 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) {
for (language in Languages.entries) { for (language in Languages.entries) {
option { option {
if (language == book.language) if (language == session.data.book.language)
selected = true selected = true
value = language.ordinal.toString() value = language.ordinal.toString()
+language.name +language.name
@@ -102,7 +110,7 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) {
} }
input(classes = "$cssTextInput w-16") { input(classes = "$cssTextInput w-16") {
name = "year" name = "year"
value = book.year.toString() value = session.data.book.year.toString()
} }
div(classes = "ml-8 pr-3 content-center") { div(classes = "ml-8 pr-3 content-center") {
@@ -110,7 +118,7 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) {
} }
input(classes = "$cssTextInput w-16") { input(classes = "$cssTextInput w-16") {
name = "originalyear" name = "originalyear"
value = book.originalYear?.toString() ?: "" value = session.data.book.originalYear?.toString() ?: ""
} }
} }
@@ -120,7 +128,7 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) {
} }
input(classes = "$cssTextInput w-40") { input(classes = "$cssTextInput w-40") {
name = "isbn" name = "isbn"
value = book.isbn value = session.data.book.isbn
} }
div(classes = "ml-14 pr-3 content-center") { div(classes = "ml-14 pr-3 content-center") {
@@ -133,9 +141,9 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) {
value = "0" value = "0"
+"---" +"---"
} }
for (pub in publishers) { for (pub in session.data.publisherList) {
option { option {
if (pub.id == book.publisher.id) if (pub.id == session.data.book.publisher.id)
selected = true selected = true
value = pub.id.toString() value = pub.id.toString()
+pub.name +pub.name
@@ -145,9 +153,15 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) {
button(classes = cssFormIcon) { button(classes = cssFormIcon) {
title = "Verlag hinzufügen" title = "Verlag hinzufügen"
// TODO // TODO
onClick = "document.getElementById('publisherDialog').showModal()" /*
// attributes["command"] = "show-modal" type = ButtonType.button -> sonst wird "submit" angenommen und dann funktioniert
// attributes["commandfor"] = "publisherDialog" //id es nicht!
*/
type = ButtonType.button
// attributes["popovertarget"] = "publisherDialog"
// onClick = "document.getElementById('publisherDialog').showModal()"
attributes["command"] = "show-modal"
attributes["commandfor"] = "publisherDialog" //id
materialSymbolFormButton("add_box") materialSymbolFormButton("add_box")
} }
@@ -165,9 +179,9 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) {
value = "0" value = "0"
+"---" +"---"
} }
for (serie in series) { for (serie in session.data.seriesList) {
option { option {
if (book.series != null && serie.id == book.series.id) if (session.data.book.series != null && serie.id == session.data.book.series!!.id)
selected = true selected = true
value = serie.id.toString() value = serie.id.toString()
+serie.name +serie.name
@@ -184,7 +198,7 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) {
} }
input(classes = "$cssTextInput w-16") { input(classes = "$cssTextInput w-16") {
name = "seriesindex" name = "seriesindex"
value = book.seriesIndex?.toString() ?: "" value = session.data.book.seriesIndex?.toString() ?: ""
} }
} }
@@ -209,7 +223,7 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) {
} }
tbody { tbody {
for ((artist, role) in book.artists) { for ((artist, role) in session.data.book.artists) {
tr { tr {
td(classes = "border-t-1 $borderTable px-2 py-1") { td(classes = "border-t-1 $borderTable px-2 py-1") {
+"${artist.name}, ${artist.givenName}" +"${artist.name}, ${artist.givenName}"
@@ -249,7 +263,7 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) {
} }
} }
tbody { tbody {
for (tag in book.tags) { for (tag in session.data.book.tags) {
tr { tr {
td(classes = "border-t-1 $borderTable px-2 py-1") { td(classes = "border-t-1 $borderTable px-2 py-1") {
+tag.tag +tag.tag
@@ -273,7 +287,7 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) {
textArea(classes = "$cssTextInput w-150 h-18 resize-none") { textArea(classes = "$cssTextInput w-150 h-18 resize-none") {
name = "comment" name = "comment"
spellCheck = true spellCheck = true
+book.comments +session.data.book.comments
} }
} }
// TODO parent // TODO parent
@@ -295,13 +309,23 @@ fun FlowContent.bookEdit(book: Book, booksRepo: BookRepository) {
} }
// TODO // TODO
dialog { dialog(
classes = "m-auto h-fit w-full max-w-md rounded-lg shadow-2xl border-2 " +
"$borderTable px-3 py-2 $bgBody $textBody $bgDialogBackdrop"
) {
id = "publisherDialog" id = "publisherDialog"
attributes["closedby"] = "any" // attributes["popover"] = "true"
// attributes["closedby"] = "any"
div { div {
+"Test-Verlag" +"Test-Verlag"
} }
button(classes = cssButtonStd) {
attributes["commandfor"] = "publisherDialog"
attributes["command"] = "close"
+"Close"
}
} }
output { }
div { div {
id = "response" id = "response"
} }
@@ -2,16 +2,20 @@ package net.keinekekse.app.books.ui
import kotlinx.html.HTML import kotlinx.html.HTML
import net.keinekekse.app.books.db.BookRepository import net.keinekekse.app.books.db.BookRepository
import net.keinekekse.app.books.model.Book import net.keinekekse.app.books.edit.BooksSessionData
import net.keinekekse.app.core.auth.UserSession
fun HTML.bookEditPage(book: Book, booksRepo: BookRepository) { fun HTML.bookEditPage(
booksRepo: BookRepository,
session: UserSession<BooksSessionData>,
) {
booksPage( booksPage(
navBooksEdit.first, navBooksEdit.first,
mapOf(navBooks, Pair(navBooksEdit.first, "")) mapOf(navBooks, Pair(navBooksEdit.first, ""))
) { ) {
bookEdit(book, booksRepo) bookEdit(booksRepo, session)
} }
} }
+1 -1
View File
@@ -15,7 +15,7 @@ fun HTML.booksPage(booksRepo: BookRepository) {
} }
// content right // content right
div(classes = "flex flex-auto $bgRightColumn") { div(classes = "flex flex-auto $bgBody") {
div(classes = "flex flex-col flex-auto") { div(classes = "flex flex-col flex-auto") {
// div { // div {
// +"some heading" // +"some heading"
@@ -43,7 +43,7 @@ fun HTML.booksPage(title: String, breadcrumbs: Map<String, String>, block: DIV.(
body { body {
// page // page
div(classes = "$cssFullscreen flex-col") { div(classes = "$cssFullscreen flex-col $textBody") {
// header // header
div(classes = "flex flex-row justify-between py-2 px-5 $bgHeader $textHeader") { div(classes = "flex flex-row justify-between py-2 px-5 $bgHeader $textHeader") {
// header left // header left
@@ -80,7 +80,7 @@ fun HTML.booksPage(title: String, breadcrumbs: Map<String, String>, block: DIV.(
// } // }
} }
div(classes = "flex flex-row flex-auto overflow-hidden") { div(classes = "flex flex-row flex-auto overflow-hidden $bgBody") {
block() block()
+8 -7
View File
@@ -14,15 +14,16 @@ const val textHeader = "text-orange-100"
const val bgHeaderIcon = "bg-orange-100" const val bgHeaderIcon = "bg-orange-100"
const val bgFooter = "bg-orange-50" const val bgFooter = "bg-orange-50"
const val bgLeftColumn = "bg-slate-200" const val bgBody = "bg-taupe-100"
const val bgRightColumn = "bg-slate-50" const val textBody = "text-taupe-900"
const val bgThead = "bg-slate-200" const val bgLeftColumn = "bg-taupe-200"
const val bgThead = "bg-taupe-200"
const val bgDialogBackdrop = "backdrop:bg-taupe-400/40"
const val borderTable = "border-slate-400" const val borderTable = "border-slate-400"
const val bgStdButton = "bg-taupe-500" const val bgStdButton = "bg-taupe-400"
const val bgStdButtonHover = "hover:bg-taupe-700" const val bgStdButtonHover = "hover:bg-taupe-500"
const val bgStdButtonFocus = "focus:bg-taupe-700" const val bgStdButtonFocus = "focus:bg-taupe-500"
const val bgPriButton = "bg-orange-400" const val bgPriButton = "bg-orange-400"
const val bgPriButtonHover = "hover:bg-orange-600" const val bgPriButtonHover = "hover:bg-orange-600"
const val bgPriButtonFocus = "focus:bg-orange-600" const val bgPriButtonFocus = "focus:bg-orange-600"
@@ -5,12 +5,18 @@ import kotlinx.html.div
import kotlinx.html.img import kotlinx.html.img
import kotlinx.html.span import kotlinx.html.span
import net.keinekekse.app.books.db.BookRepository import net.keinekekse.app.books.db.BookRepository
import net.keinekekse.app.books.edit.BooksSessionData
import net.keinekekse.app.books.model.Book import net.keinekekse.app.books.model.Book
import net.keinekekse.app.books.model.CalibreBookInfo import net.keinekekse.app.books.model.CalibreBookInfo
import net.keinekekse.app.books.model.Languages import net.keinekekse.app.books.model.Languages
import net.keinekekse.app.books.model.Publisher import net.keinekekse.app.books.model.Publisher
import net.keinekekse.app.core.auth.UserSession
fun HTML.importEditPage(bookInfo: CalibreBookInfo?, booksRepo: BookRepository) { fun HTML.importEditPage(
bookInfo: CalibreBookInfo?,
booksRepo: BookRepository,
session: UserSession<BooksSessionData>,
) {
val dataUrl = embedImageURL(bookInfo?.cover) val dataUrl = embedImageURL(bookInfo?.cover)
booksPage( booksPage(
navBooksScanEdit.first, navBooksScanEdit.first,
@@ -18,15 +24,14 @@ fun HTML.importEditPage(bookInfo: CalibreBookInfo?, booksRepo: BookRepository) {
) { ) {
div(classes = "flex flex-row flex-auto") { div(classes = "flex flex-row flex-auto") {
bookEdit( session.data.book = Book(
Book( title = bookInfo?.title ?: "",
title = bookInfo?.title ?: "", year = bookInfo?.published ?: 0,
year = bookInfo?.published ?: 0, isbn = bookInfo?.isbn ?: "",
isbn = bookInfo?.isbn ?: "", publisher = Publisher(0, ""),
publisher = Publisher(0, ""), language = bookInfo?.language ?: Languages.DE
language = bookInfo?.language ?: Languages.DE
), booksRepo
) )
bookEdit(booksRepo, session)
div(classes = "flex flex-col m-8 ") { div(classes = "flex flex-col m-8 ") {
div(classes = "text-2xl mx-8") { div(classes = "text-2xl mx-8") {
@@ -23,7 +23,7 @@ fun HTML.importPage(books: List<CalibreBookInfo>) {
} }
// content right // content right
div(classes = "flex flex-auto $bgRightColumn") { div(classes = "flex flex-auto $bgBody") {
div(classes = "flex flex-col flex-auto") { div(classes = "flex flex-col flex-auto") {
div { div {
// +"some heading" // +"some heading"
+1 -1
View File
@@ -211,7 +211,7 @@ fun HTML.testPage(booksDb: Database) {
} }
} }
// content right // content right
div(classes = "flex flex-auto $bgRightColumn") { div(classes = "flex flex-auto $bgBody") {
div(classes = "flex flex-col flex-auto") { div(classes = "flex flex-col flex-auto") {
// div { // div {
// +"some heading" // +"some heading"
+15 -26
View File
@@ -5,40 +5,29 @@ import kotlin.uuid.Uuid
class Sessions { class Sessions {
var sessions: MutableMap<String, MutableMap<String, UserSession<*>>> = mutableMapOf() var sessions: MutableMap<String, UserSession<*>> = mutableMapOf()
@OptIn(ExperimentalUuidApi::class) companion object {
fun newSessionId(): String { @OptIn(ExperimentalUuidApi::class)
return Uuid.generateV4().toHexDashString() fun newSessionId(): String {
return Uuid.generateV4().toHexDashString()
}
} }
fun getUserSessions(uid: String): MutableMap<String, UserSession<*>>? { fun getSession(sessionId: String): UserSession<*>? {
return sessions.get(uid) return sessions[sessionId]
} }
fun getUserSession(uid: String, sessionId: String): UserSession<*>? { fun newSession(data: UserSession<*>) {
return sessions.get(uid)?.get(sessionId) sessions[data.sessionId] = data
} }
fun setUserSession(uid: String, sessionId: String, data: UserSession<*>) {
sessions.getOrPut(uid) { mutableMapOf() }[sessionId] = data
}
// fun addSession(uid: String) {
// sessions[uid] = BooksSession(uid, "", "")
// }
//
// fun addTestSession(uid: String) {
// sessions[uid] = TestSession(uid, "", 0)
// }
} }
interface UserSession<T> { data class UserSession<T>(
val userId: String val userId: String,
val sessionId: String val sessionId: String,
var data: T,
var data: T )
}
//data class BooksSession( //data class BooksSession(
// override val userId: String, // override val userId: String,
+19 -4
View File
@@ -7,6 +7,7 @@ import kotlinx.html.*
import net.keinekekse.app.VERSION_books import net.keinekekse.app.VERSION_books
import net.keinekekse.app.books.ui.materialSymbol import net.keinekekse.app.books.ui.materialSymbol
import net.keinekekse.app.books.ui.navBooks import net.keinekekse.app.books.ui.navBooks
import net.keinekekse.app.books.ui.navBooksScan
import net.keinekekse.app.core.config.LOGIN_PATH import net.keinekekse.app.core.config.LOGIN_PATH
import net.keinekekse.app.core.config.LOGOUT_PATH import net.keinekekse.app.core.config.LOGOUT_PATH
@@ -37,11 +38,25 @@ fun HTML.landingPage(loggedOn: Boolean) {
} }
} }
} }
}
div(classes = "lg:hidden p-8") {
a(classes = "inline-flex $cssMainLink") {
href = navBooksScan.second
// a(classes = "p-2 text-xl font-bold text-sky-500 hover:text-sky-700 hover:bg-sky-100 cursor-pointer") { img(classes = "h-14 my-2") {
// href = "/books" src = "/static/books.png"
// +VERSION_books.description }
// } div(classes = "self-center text-right ml-3") {
div {
div {
+"Scan Books"
}
// div(classes = "text-xs") {
// +VERSION_books.version
// }
}
}
}
} }
div(classes = "m-8") { div(classes = "m-8") {
a(classes = cssMainLink) { a(classes = cssMainLink) {
@@ -47,7 +47,7 @@ fun HTML.mainApplicationPage(block: DIV.() -> Unit = {}) {
} }
} }
// body // body
div(classes = "inline-flex $bgMainBody py-10 h-full overflow-hidden w-full rounded-b-lg") { div(classes = "inline-flex $bgMainBody py-10 h-full overflow-auto w-full rounded-b-lg") {
block() block()