Compare commits

..

No commits in common. "2b290930867138ead7468294f8c43e2a27c8a962" and "dce2567160555ee7a37f5011216d80683930b239" have entirely different histories.

15 changed files with 251 additions and 255 deletions

View file

@ -99,6 +99,10 @@ kotlin {
implementation "com.soywiz:klock-jvm:$klockVersion" implementation "com.soywiz:klock-jvm:$klockVersion"
implementation "com.soywiz:klock-locale-jvm:$klockVersion" implementation "com.soywiz:klock-locale-jvm:$klockVersion"
//implementation 'com.twitter:hbc-core:2.2.0'
//implementation 'com.twitter:hbc-twitter4j:2.2.0'
api 'org.twitter4j:twitter4j-stream:4.0.1'
implementation 'com.github.uchuhimo:konf:master-SNAPSHOT' implementation 'com.github.uchuhimo:konf:master-SNAPSHOT'
implementation 'com.vladsch.flexmark:flexmark-all:0.42.10' implementation 'com.vladsch.flexmark:flexmark-all:0.42.10'
api 'io.github.microutils:kotlin-logging:1.6.23' api 'io.github.microutils:kotlin-logging:1.6.23'

View file

@ -1,6 +1,5 @@
$background-primary-color: #fff; $background-primary-color: #fff;
$background-secondary-color: #f5f5f5; $background-secondary-color: #fcfcfc;
$background-card-color: #fff;
$text-primary-color: #333; $text-primary-color: #333;
$text-secondary-color: rgba($text-primary-color, 0.5); $text-secondary-color: rgba($text-primary-color, 0.5);
@ -16,7 +15,7 @@ $input-border-color: #888;
$table-border-color: rgba($text-primary-color, 0.1); $table-border-color: rgba($text-primary-color, 0.1);
$table-header-color: rgba($text-primary-color, 0.06); $table-header-color: rgba($text-primary-color, 0.06);
$shadow-color: rgba(#000, 0.3); $shadow-color: rgba($text-primary-color, 0.8);
$icon-color-focused: rgba($text-primary-color, 0.87); $icon-color-focused: rgba($text-primary-color, 0.87);
$icon-color: rgba($text-primary-color, 0.54); $icon-color: rgba($text-primary-color, 0.54);
@ -31,7 +30,6 @@ $lever-enabled-color: $primary-color;
:root { :root {
--background-primary-color: $background-primary-color; --background-primary-color: $background-primary-color;
--background-secondary-color: $background-secondary-color; --background-secondary-color: $background-secondary-color;
--background-card-color: $background-card-color;
--text-primary-color: $text-primary-color; --text-primary-color: $text-primary-color;
--text-secondary-color: $text-secondary-color; --text-secondary-color: $text-secondary-color;

View file

@ -1 +0,0 @@

View file

@ -1,124 +1,73 @@
@import "../config"; @import "../config";
.board-header { .board-header {
line-height: 3rem; line-height: 6rem;
flex-grow: 1; display: flex;
font-family: "Bungee", sans-serif; padding: 0 2rem;
font-weight: normal;
font-size: 1.1rem;
padding-left: 0.3rem;
}
.board-card { & > div {
background-color: var(--background-card-color); flex-grow: 1;
box-shadow: 0 0.1rem 0.2rem var(--shadow-color); font-family: "Bungee", sans-serif;
border-radius: $border-radius; font-weight: normal;
overflow: hidden; font-size: 1.1rem;
text-align: center;
&:first-child {
text-align: left;
}
&:last-child {
text-align: right;
}
}
} }
.board { .board {
padding: 1rem 0.4rem 2rem; padding: 1rem 1rem 2rem;
display: flex; display: flex;
overflow: hidden; overflow: hidden;
height: calc(100vh - 0.5rem); height: calc(100vh - 6rem);
& > div { & > div {
flex-grow: 1; flex-grow: 1;
flex-basis: 0; flex-basis: 0;
padding: 0 0.4rem;
&:nth-child(1) {
flex-grow: 4;
padding-left: 0.15rem;
padding-right: 0.15rem;
}
&:nth-child(2) {
flex-grow: 3;
}
&:nth-child(3) {
flex-grow: 3;
}
} }
} }
.board-twitter {
.board-card {
height: calc(100% - 1.3rem);
& > * {
margin-top: -1px !important;
}
}
}
.board-post {
margin-bottom: 0.5rem;
.post-name {
top: 0.5rem;
}
padding-top: 2.5rem;
padding-bottom: 0.5rem;
}
.board-schedule-box {
display: flex;
flex-wrap: wrap;
}
.board-schedule { .board-schedule {
width: 100%;
position: relative; position: relative;
flex-grow: 1;
flex-basis: 0;
min-width: 15rem;
padding: 0.6rem; padding: 1rem 1rem;
margin: 0 0.25rem 0.5rem;
&:not(:last-child) { &:not(:last-child) {
border-bottom: solid 1px var(--table-border-color) border-bottom: solid 1px var(--table-border-color)
} }
} }
.board-schedule-bottom { .board-schedule-room {
display: block; display: block;
padding-left: 1rem; padding-left: 1rem;
padding-right: 0.5rem; line-height: 2rem;
line-height: 1rem; }
clear: both;
& > span { .board-schedule-time {
position: absolute;
&:first-child { top: 1rem;
float: left; right: 1rem;
} line-height: 2rem;
&:last-child {
float: right;
}
}
} }
.board-schedule-color { .board-schedule-color {
background-color: var(--primary-color); background-color: var(--primary-color);
position: absolute; position: absolute;
top: 1.25rem; top: 3.25rem;
left: 0.6rem; left: 1rem;
width: 0.8rem; width: 0.5rem;
height: 0.8rem; height: 1.5rem;
border-radius: 100%;
} }
.board-schedule-name { .board-schedule-name {
display: block; display: block;
padding-left: 1rem; padding-left: 1rem;
line-height: 1.3rem; line-height: 2rem;
font-family: 'Montserrat', sans-serif;
font-weight: 600;
padding-top: 0.35rem;
padding-bottom: 0.35rem;
} }

View file

@ -12,10 +12,12 @@
} }
.overview-side { .overview-side {
min-width: 30%; min-width: 20%;
} }
.overview-twitter { .overview-twitter {
height: 20rem;
background-color: #b3e6f9;
} }
.post { .post {
@ -32,8 +34,6 @@
color: var(--primary-color); color: var(--primary-color);
line-height: 2rem; line-height: 2rem;
padding: 0 1rem; padding: 0 1rem;
font-weight: bold;
font-family: 'Montserrat', sans-serif;
&:empty::before { &:empty::before {
display: block; display: block;

View file

@ -2,12 +2,11 @@
$background-primary-color: #2d2d2d; $background-primary-color: #2d2d2d;
$background-secondary-color: #373737; $background-secondary-color: #373737;
$background-card-color: rgba($text-primary-color, 0.06);
$text-primary-color: #fff; $text-primary-color: #fff;
$text-secondary-color: rgba($text-primary-color, 0.5); $text-secondary-color: rgba($text-primary-color, 0.5);
$primary-color: #ef5350; $primary-color: #dd213d;
$primary-text-color: #fff; $primary-text-color: #fff;
$error-color: #F00; $error-color: #F00;
@ -18,7 +17,7 @@ $input-border-color: #888;
$table-border-color: rgba($text-primary-color, 0.1); $table-border-color: rgba($text-primary-color, 0.1);
$table-header-color: rgba($text-primary-color, 0.06); $table-header-color: rgba($text-primary-color, 0.06);
$shadow-color: rgba(#000, 0.3); $shadow-color: rgba($text-primary-color, 0.8);
$icon-color-focused: rgba($text-primary-color, 1.0); $icon-color-focused: rgba($text-primary-color, 1.0);
$icon-color: rgba($text-primary-color, 0.7); $icon-color: rgba($text-primary-color, 0.7);

View file

@ -2,7 +2,6 @@
$background-primary-color: #ffc3e1; $background-primary-color: #ffc3e1;
$background-secondary-color: #ffa5d2; $background-secondary-color: #ffa5d2;
$background-card-color: rgba($text-primary-color, 0.06);
$text-primary-color: #333; $text-primary-color: #333;
$text-secondary-color: rgba($text-primary-color, 0.5); $text-secondary-color: rgba($text-primary-color, 0.5);
@ -18,7 +17,7 @@ $input-border-color: #888;
$table-border-color: rgba($text-primary-color, 0.1); $table-border-color: rgba($text-primary-color, 0.1);
$table-header-color: rgba($text-primary-color, 0.06); $table-header-color: rgba($text-primary-color, 0.06);
$shadow-color: rgba(#000, 0.3); $shadow-color: rgba($text-primary-color, 0.8);
$icon-color-focused: rgba($text-primary-color, 0.87); $icon-color-focused: rgba($text-primary-color, 0.87);
$icon-color: rgba($text-primary-color, 0.54); $icon-color: rgba($text-primary-color, 0.54);

View file

@ -14,8 +14,8 @@ body, html {
color: var(--text-primary-color); color: var(--text-primary-color);
background: var(--background-secondary-color); background: var(--background-secondary-color);
font-family: 'Raleway', 'Montserrat', Roboto, Arial, sans-serif; font-family: 'Montserrat', Roboto, Arial, sans-serif;
font-weight: 500; font-weight: 600;
width: 100%; width: 100%;
height: 100%; height: 100%;

View file

@ -91,11 +91,13 @@ object Configuration {
} }
private object TwitterSpec : ConfigSpec("twitter") { private object TwitterSpec : ConfigSpec("twitter") {
val timeline by required<String>("timeline") val username by required<String>("username")
val password by required<String>("password")
} }
object Twitter { object Twitter {
val timeline by c(TwitterSpec.timeline) val username by c(TwitterSpec.username)
val password by c(TwitterSpec.password)
} }
init { init {

View file

@ -0,0 +1,142 @@
package de.kif.backend
import twitter4j.*
import twitter4j.conf.ConfigurationBuilder
/*
fun twitter() {
val msgQueue = LinkedBlockingQueue<String>(100000)
val eventQueue = LinkedBlockingQueue<Event>(1000)
val hosts = HttpHosts(Constants.STREAM_HOST)
val filterEndpoint = StatusesFilterEndpoint()
filterEndpoint.trackTerms(listOf("kif"))
println(Configuration.Twitter.username)
println(Configuration.Twitter.password)
val authentication = BasicAuth(
Configuration.Twitter.username,
Configuration.Twitter.password
)
val builder = ClientBuilder()
.name("kif-portal")
.hosts(hosts)
.authentication(authentication)
.endpoint(filterEndpoint)
.processor(StringDelimitedProcessor(msgQueue))
.eventMessageQueue(eventQueue)
val client = builder.build()
val listener = object: StatusStreamHandler {
override fun onUnknownMessageType(msg: String?) {
println("onUnknownMessageType")
println(msg)
}
override fun onDisconnectMessage(message: DisconnectMessage?) {
println("onDisconnectMessage")
println(message?.disconnectReason)
}
override fun onStallWarningMessage(warning: StallWarningMessage?) {
println("onStallWarningMessage")
println(warning?.message)
}
override fun onTrackLimitationNotice(numberOfLimitedStatuses: Int) {
println("onTrackLimitationNotice")
println(numberOfLimitedStatuses)
}
override fun onStallWarning(warning: StallWarning?) {
println("onStallWarning")
println(warning?.message)
}
override fun onException(ex: Exception?) {
println("onException")
ex?.printStackTrace()
}
override fun onDeletionNotice(statusDeletionNotice: StatusDeletionNotice?) {
println("onDeletionNotice")
println(statusDeletionNotice?.statusId)
}
override fun onStatus(status: Status?) {
println("onStatus")
println(status?.text)
}
override fun onScrubGeo(userId: Long, upToStatusId: Long) {
println("onScrubGeo")
}
}
val t4jClient = Twitter4jStatusClient(
client,
msgQueue,
listOf(listener),
Executors.newFixedThreadPool(4)
)
t4jClient.connect()
t4jClient.process()
while (true) {
}
}
*/
fun twitter() {
val cb = ConfigurationBuilder()
cb.setDebugEnabled(true)
.setUser(Configuration.Twitter.username)
.setPassword(Configuration.Twitter.password)
val listener = object : StatusListener {
override fun onTrackLimitationNotice(numberOfLimitedStatuses: Int) {
println("onTrackLimitationNotice")
println(numberOfLimitedStatuses)
}
override fun onStallWarning(warning: StallWarning?) {
println("onStallWarning")
println(warning?.message)
}
override fun onException(ex: Exception?) {
println("onException")
ex?.printStackTrace()
}
override fun onDeletionNotice(statusDeletionNotice: StatusDeletionNotice?) {
println("onDeletionNotice")
println(statusDeletionNotice?.statusId)
}
override fun onStatus(status: Status?) {
println("onStatus")
println(status?.text)
}
override fun onScrubGeo(userId: Long, upToStatusId: Long) {
println("onScrubGeo")
}
}
val twitterStream = TwitterStreamFactory(cb.build()).instance
addTwitterStreamListener(twitterStream, listener)
twitterStream.sample()
}

View file

@ -2,15 +2,18 @@ package de.kif.backend.route
import de.kif.backend.authenticate import de.kif.backend.authenticate
import de.kif.backend.authenticateOrRedirect import de.kif.backend.authenticateOrRedirect
import de.kif.backend.util.Backup
import de.kif.backend.repository.TrackRepository import de.kif.backend.repository.TrackRepository
import de.kif.backend.repository.WorkGroupRepository import de.kif.backend.repository.WorkGroupRepository
import de.kif.backend.route.api.error import de.kif.backend.route.api.error
import de.kif.backend.util.Backup
import de.kif.backend.util.WikiImporter import de.kif.backend.util.WikiImporter
import de.kif.backend.view.MainTemplate
import de.kif.backend.view.MenuTemplate
import de.kif.backend.view.respondMain import de.kif.backend.view.respondMain
import de.kif.common.RepositoryType import de.kif.common.RepositoryType
import de.kif.common.model.Permission import de.kif.common.model.Permission
import io.ktor.application.call import io.ktor.application.call
import io.ktor.html.respondHtmlTemplate
import io.ktor.http.ContentType import io.ktor.http.ContentType
import io.ktor.http.HttpStatusCode import io.ktor.http.HttpStatusCode
import io.ktor.http.content.PartData import io.ktor.http.content.PartData
@ -32,6 +35,10 @@ private val logger = KotlinLogging.logger {}
fun Route.account() { fun Route.account() {
get("/account") { get("/account") {
authenticateOrRedirect { user -> authenticateOrRedirect { user ->
val tracks = TrackRepository.all()
val wikiSections = WikiImporter.loadSections()
respondMain { respondMain {
content { content {
h1 { +"Account" } h1 { +"Account" }
@ -47,26 +54,6 @@ fun Route.account() {
} }
} }
a(href = "/account/backup", classes = "form-btn") {
+"Backup"
}
if (user.checkPermission(Permission.ADMIN)) {
a(href = "/account/import", classes = "form-btn") {
+"Import wiki"
}
}
}
}
}
}
get("/account/backup") {
authenticateOrRedirect { user ->
respondMain {
content {
h1 { +"Backup" }
div("account-backup") { div("account-backup") {
if (user.checkPermission(Permission.ROOM)) { if (user.checkPermission(Permission.ROOM)) {
a("/account/backup/rooms.json", classes = "form-btn") { a("/account/backup/rooms.json", classes = "form-btn") {
@ -112,11 +99,9 @@ fun Route.account() {
} }
if (user.checkPermission(Permission.ADMIN)) { if (user.checkPermission(Permission.ADMIN)) {
h1 { +"Restore" }
div("account-import") { div("account-import") {
form( form(
action = "/account/restore", action = "/account/import",
method = FormMethod.post, method = FormMethod.post,
encType = FormEncType.multipartFormData encType = FormEncType.multipartFormData
) { ) {
@ -161,25 +146,12 @@ fun Route.account() {
} }
} }
} }
}
}
}
}
get("/account/import") {
authenticateOrRedirect(Permission.ADMIN) { user ->
val tracks = TrackRepository.all()
val wikiSections = WikiImporter.loadSections()
respondMain {
content {
h1 { +"Import wiki" }
if (user.checkPermission(Permission.ADMIN)) { if (user.checkPermission(Permission.ADMIN)) {
div("account-import-wiki") { div("account-import-wiki") {
span { +"Import work group data from the kif wiki" } span { +"Import work group data from the kif wiki" }
form(action = "/account/import", method = FormMethod.post) { form(action = "/account/import-wiki", method = FormMethod.post) {
for ((index, section) in wikiSections.withIndex()) { for ((index, section) in wikiSections.withIndex()) {
div("form-group") { div("form-group") {
label { label {
@ -305,7 +277,7 @@ fun Route.account() {
} }
} }
post("/account/restore") { post("/account/import") {
authenticate(Permission.ADMIN) { authenticate(Permission.ADMIN) {
var reset = false var reset = false
var import = "" var import = ""
@ -336,7 +308,7 @@ fun Route.account() {
} }
} }
post("/account/import") { post("/account/import-wiki") {
authenticate(Permission.ADMIN) { authenticate(Permission.ADMIN) {
val params = call.receiveParameters().toMap().mapValues { (_, list) -> val params = call.receiveParameters().toMap().mapValues { (_, list) ->
list.firstOrNull() list.firstOrNull()

View file

@ -4,6 +4,7 @@ import de.kif.backend.Configuration
import de.kif.backend.repository.PostRepository import de.kif.backend.repository.PostRepository
import de.kif.backend.repository.ScheduleRepository import de.kif.backend.repository.ScheduleRepository
import de.kif.backend.view.respondMain import de.kif.backend.view.respondMain
import de.kif.common.formatDateTime
import de.kif.common.model.Schedule import de.kif.common.model.Schedule
import io.ktor.routing.Route import io.ktor.routing.Route
import io.ktor.routing.get import io.ktor.routing.get
@ -11,11 +12,10 @@ import kotlinx.css.CSSBuilder
import kotlinx.css.Color import kotlinx.css.Color
import kotlinx.html.div import kotlinx.html.div
import kotlinx.html.span import kotlinx.html.span
import kotlinx.html.unsafe
import java.util.* import java.util.*
fun Route.board() { fun Route.board() {
get("/brett") { get("/board") {
val postList = PostRepository.all().asReversed() val postList = PostRepository.all().asReversed()
val scheduleList = ScheduleRepository.all().map { val scheduleList = ScheduleRepository.all().map {
it to it.getAbsoluteStartTime() * 60 it to it.getAbsoluteStartTime() * 60
@ -24,9 +24,8 @@ fun Route.board() {
val referenceTime = Configuration.Schedule.referenceDate.time / 1000 val referenceTime = Configuration.Schedule.referenceDate.time / 1000
val now = referenceTime - (Date().time / 1000) val now = referenceTime - (Date().time / 1000)
respondMain(true, true) { theme -> respondMain(true, true) {
content { content {
/*
div("board-header") { div("board-header") {
div { div {
+"KIF 47.0" +"KIF 47.0"
@ -35,96 +34,46 @@ fun Route.board() {
+formatDateTime(Date().time) +formatDateTime(Date().time)
} }
} }
*/
div("board") { div("board") {
div("board-schedules") { div("board-schedules") {
attributes["data-reference"] = referenceTime.toString() attributes["data-reference"] = referenceTime.toString()
div("board-header") { for ((schedule, time) in scheduleList) {
+"AKs" div("board-schedule") {
} attributes["data-id"] = schedule.id.toString()
div("board-schedule-box") { span("board-schedule-room") {
for ((schedule, time) in scheduleList) { +schedule.room.name
div("board-card board-schedule") { }
attributes["data-id"] = schedule.id.toString() span("board-schedule-time") {
attributes["data-time"] = time.toString()
attributes["data-duration"] = schedule.workGroup.length.toString()
span("board-schedule-color") { +Schedule.timeDifferenceToString(time + now)
attributes["style"] = CSSBuilder().apply { }
val c = schedule.workGroup.track?.color span("board-schedule-color") {
if (c != null) { attributes["style"] = CSSBuilder().apply {
backgroundColor = Color(c.toString()) val c = schedule.workGroup.track?.color
} if (c != null) {
}.toString() backgroundColor = Color(c.toString())
}
span("board-schedule-name") {
+schedule.workGroup.name
}
div("board-schedule-bottom") {
span("board-schedule-time") {
attributes["data-time"] = time.toString()
attributes["data-duration"] = schedule.workGroup.length.toString()
val startTime = (time % MINUTES_OF_DAY).let {
if (it < 0) it + MINUTES_OF_DAY else it
}
val sm = (startTime % 60).toString().padStart(2, '0')
val sh = (startTime / 60).toString().padStart(2, '0')
val startTimeString = "$sh:$sm"
val endTime = ((time + schedule.workGroup.length) % MINUTES_OF_DAY).let {
if (it < 0) it + MINUTES_OF_DAY else it
}
val em = (endTime % 60).toString().padStart(2, '0')
val eh = (endTime / 60).toString().padStart(2, '0')
val endTimeString = "$eh:$em"
+"$startTimeString - $endTimeString"
} }
}.toString()
span("board-schedule-room") { }
+schedule.room.name span("board-schedule-name") {
} +schedule.workGroup.name
}
} }
} }
} }
} }
div("board-posts") { div("board-posts") {
div("board-header") {
+"News"
}
for (post in postList) { for (post in postList) {
createPost(post, false, "board-card board-post") createPost(post, false, "board-post overview-post")
} }
} }
div("board-twitter") { div("board-twitter") {
div("board-header") {
+"Tweets"
}
div("board-card") {
unsafe {
raw(
"""
<a
class="twitter-timeline"
href="${Configuration.Twitter.timeline}"
data-chrome="transparent noheader nofooter"
data-theme="${if (theme.dark) "dark" else "light"}"
data-link-color="${theme.primaryColor}"
data-cards="hidden"
data-lang="de"
data-dnt="true"
>Tweets by kiforbiter</a>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
""".trimIndent()
)
}
}
} }
} }
} }

View file

@ -77,7 +77,7 @@ fun Route.overview() {
val postList = PostRepository.all().asReversed() val postList = PostRepository.all().asReversed()
respondMain { theme -> respondMain {
content { content {
div("overview") { div("overview") {
div("overview-main") { div("overview-main") {
@ -94,21 +94,7 @@ fun Route.overview() {
} }
} }
div("overview-twitter") { div("overview-twitter") {
unsafe { +"The Twitter Wall"
raw("""
<a
class="twitter-timeline"
href="${Configuration.Twitter.timeline}"
data-chrome="transparent"
data-theme="${if (theme.dark) "dark" else "light"}"
data-link-color="${theme.primaryColor}"
data-cards="hidden"
data-lang="de"
data-dnt="true"
>Tweets by kiforbiter</a>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
""".trimIndent())
}
} }
} }
} }

View file

@ -2,6 +2,7 @@ package de.kif.backend.view
import de.kif.backend.PortalSession import de.kif.backend.PortalSession
import de.kif.backend.Resources import de.kif.backend.Resources
import de.kif.backend.authenticate
import de.kif.common.model.User import de.kif.common.model.User
import io.ktor.application.ApplicationCall import io.ktor.application.ApplicationCall
import io.ktor.application.call import io.ktor.application.call
@ -33,7 +34,7 @@ class MainTemplate(
link(href = "/static/external/material-icons.css", type = LinkType.textCss, rel = LinkRel.stylesheet) link(href = "/static/external/material-icons.css", type = LinkType.textCss, rel = LinkRel.stylesheet)
link(href = "/static/external/font/Montserrat.css", type = LinkType.textCss, rel = LinkRel.stylesheet) link(href = "/static/external/font/Montserrat.css", type = LinkType.textCss, rel = LinkRel.stylesheet)
link( link(
href = "https://fonts.googleapis.com/css?family=Bungee|Oswald|Raleway", href = "https://fonts.googleapis.com/css?family=Bungee|Oswald",
type = LinkType.textCss, type = LinkType.textCss,
rel = LinkRel.stylesheet rel = LinkRel.stylesheet
) )
@ -49,9 +50,6 @@ class MainTemplate(
Theme.PRINCESS -> { Theme.PRINCESS -> {
link(href = "/static/style/princess.css", type = LinkType.textCss, rel = LinkRel.stylesheet) link(href = "/static/style/princess.css", type = LinkType.textCss, rel = LinkRel.stylesheet)
} }
Theme.BRETT -> {
link(href = "/static/style/board.css", type = LinkType.textCss, rel = LinkRel.stylesheet)
}
} }
script(src = "/static/require.min.js") {} script(src = "/static/require.min.js") {}
@ -81,7 +79,8 @@ class MainTemplate(
div("footer-credit") { div("footer-credit") {
} }
div("footer-theme") { div("footer-theme") {
for ((it, name) in Theme.displayThemes) { for (it in Theme.values()) {
val name = it.name.toLowerCase()
a("?theme=${it.name}", classes = if (theme == it) "selected" else "") { a("?theme=${it.name}", classes = if (theme == it) "selected" else "") {
id = "theme-$name" id = "theme-$name"
+name.capitalize() +name.capitalize()
@ -95,19 +94,14 @@ class MainTemplate(
} }
} }
enum class Theme(val display: Boolean, val dark: Boolean, val primaryColor: String) { enum class Theme {
LIGHT(true, false, "#B11D33"), LIGHT, DARK, PRINCESS;
DARK(true, true, "#ef5350"),
PRINCESS(true, false, "#B11D33"),
BRETT(false, false, "#B11D33");
companion object { companion object {
private val lookup = values().toList().associateBy { it.name } private val loopup = values().toList().associateBy { it.name }
val displayThemes = values().filter { it.display }.map { it to it.name.toLowerCase() }
fun lookup(name: String?): Theme { fun lookup(name: String?): Theme {
return lookup[(name ?: return LIGHT).toUpperCase()] ?: LIGHT return loopup[(name ?: return LIGHT).toUpperCase()] ?: LIGHT
} }
} }
} }
@ -115,7 +109,7 @@ enum class Theme(val display: Boolean, val dark: Boolean, val primaryColor: Stri
suspend fun PipelineContext<Unit, ApplicationCall>.respondMain( suspend fun PipelineContext<Unit, ApplicationCall>.respondMain(
noMenu: Boolean = false, noMenu: Boolean = false,
stretch: Boolean = false, stretch: Boolean = false,
body: MainTemplate.(Theme) -> Unit body: MainTemplate.() -> Unit
) { ) {
val param = call.request.queryParameters["theme"] val param = call.request.queryParameters["theme"]
val url = call.request.uri.substring(1) val url = call.request.uri.substring(1)
@ -130,17 +124,15 @@ suspend fun PipelineContext<Unit, ApplicationCall>.respondMain(
) )
call.respondRedirect(call.request.path()) call.respondRedirect(call.request.path())
} else { } else {
val theme = Theme.lookup(call.request.cookies["theme"])
call.respondHtmlTemplate( call.respondHtmlTemplate(
MainTemplate( MainTemplate(
theme, Theme.lookup(call.request.cookies["theme"]),
url, url,
user, user,
noMenu, noMenu,
stretch stretch
) ),
) { body = body
body(theme) )
}
} }
} }

View file

@ -0,0 +1,5 @@
package twitter4j
fun addTwitterStreamListener(stream: TwitterStream, listener: StatusListener) {
stream.addListener(listener)
}