V1 von Brett her

This commit is contained in:
Lars Westermann 2019-06-08 18:12:01 +02:00
parent 17f81e8952
commit 2b29093086
Signed by: lars.westermann
GPG key ID: 9D417FA5BB9D5E1D
15 changed files with 223 additions and 244 deletions

View file

@ -99,10 +99,6 @@ kotlin {
implementation "com.soywiz:klock-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.vladsch.flexmark:flexmark-all:0.42.10'
api 'io.github.microutils:kotlin-logging:1.6.23'

View file

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

View file

@ -0,0 +1 @@

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,142 +0,0 @@
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

@ -50,6 +50,8 @@ 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"
}
@ -57,6 +59,7 @@ fun Route.account() {
}
}
}
}
get("/account/backup") {
authenticateOrRedirect { user ->
@ -164,7 +167,7 @@ fun Route.account() {
}
get("/account/import") {
authenticateOrRedirect { user ->
authenticateOrRedirect(Permission.ADMIN) { user ->
val tracks = TrackRepository.all()
val wikiSections = WikiImporter.loadSections()

View file

@ -4,7 +4,6 @@ import de.kif.backend.Configuration
import de.kif.backend.repository.PostRepository
import de.kif.backend.repository.ScheduleRepository
import de.kif.backend.view.respondMain
import de.kif.common.formatDateTime
import de.kif.common.model.Schedule
import io.ktor.routing.Route
import io.ktor.routing.get
@ -12,10 +11,11 @@ import kotlinx.css.CSSBuilder
import kotlinx.css.Color
import kotlinx.html.div
import kotlinx.html.span
import kotlinx.html.unsafe
import java.util.*
fun Route.board() {
get("/board") {
get("/brett") {
val postList = PostRepository.all().asReversed()
val scheduleList = ScheduleRepository.all().map {
it to it.getAbsoluteStartTime() * 60
@ -24,8 +24,9 @@ fun Route.board() {
val referenceTime = Configuration.Schedule.referenceDate.time / 1000
val now = referenceTime - (Date().time / 1000)
respondMain(true, true) {
respondMain(true, true) { theme ->
content {
/*
div("board-header") {
div {
+"KIF 47.0"
@ -34,23 +35,20 @@ fun Route.board() {
+formatDateTime(Date().time)
}
}
*/
div("board") {
div("board-schedules") {
attributes["data-reference"] = referenceTime.toString()
div("board-header") {
+"AKs"
}
div("board-schedule-box") {
for ((schedule, time) in scheduleList) {
div("board-schedule") {
div("board-card board-schedule") {
attributes["data-id"] = schedule.id.toString()
span("board-schedule-room") {
+schedule.room.name
}
span("board-schedule-time") {
attributes["data-time"] = time.toString()
attributes["data-duration"] = schedule.workGroup.length.toString()
+Schedule.timeDifferenceToString(time + now)
}
span("board-schedule-color") {
attributes["style"] = CSSBuilder().apply {
val c = schedule.workGroup.track?.color
@ -62,18 +60,71 @@ fun Route.board() {
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"
}
span("board-schedule-room") {
+schedule.room.name
}
}
}
}
}
}
div("board-posts") {
div("board-header") {
+"News"
}
for (post in postList) {
createPost(post, false, "board-post overview-post")
createPost(post, false, "board-card board-post")
}
}
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()
respondMain {
respondMain { theme ->
content {
div("overview") {
div("overview-main") {
@ -94,7 +94,21 @@ fun Route.overview() {
}
}
div("overview-twitter") {
+"The Twitter Wall"
unsafe {
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,7 +2,6 @@ package de.kif.backend.view
import de.kif.backend.PortalSession
import de.kif.backend.Resources
import de.kif.backend.authenticate
import de.kif.common.model.User
import io.ktor.application.ApplicationCall
import io.ktor.application.call
@ -34,7 +33,7 @@ class MainTemplate(
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 = "https://fonts.googleapis.com/css?family=Bungee|Oswald",
href = "https://fonts.googleapis.com/css?family=Bungee|Oswald|Raleway",
type = LinkType.textCss,
rel = LinkRel.stylesheet
)
@ -50,6 +49,9 @@ class MainTemplate(
Theme.PRINCESS -> {
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") {}
@ -79,8 +81,7 @@ class MainTemplate(
div("footer-credit") {
}
div("footer-theme") {
for (it in Theme.values()) {
val name = it.name.toLowerCase()
for ((it, name) in Theme.displayThemes) {
a("?theme=${it.name}", classes = if (theme == it) "selected" else "") {
id = "theme-$name"
+name.capitalize()
@ -94,14 +95,19 @@ class MainTemplate(
}
}
enum class Theme {
LIGHT, DARK, PRINCESS;
enum class Theme(val display: Boolean, val dark: Boolean, val primaryColor: String) {
LIGHT(true, false, "#B11D33"),
DARK(true, true, "#ef5350"),
PRINCESS(true, false, "#B11D33"),
BRETT(false, false, "#B11D33");
companion object {
private val loopup = values().toList().associateBy { it.name }
private val lookup = values().toList().associateBy { it.name }
val displayThemes = values().filter { it.display }.map { it to it.name.toLowerCase() }
fun lookup(name: String?): Theme {
return loopup[(name ?: return LIGHT).toUpperCase()] ?: LIGHT
return lookup[(name ?: return LIGHT).toUpperCase()] ?: LIGHT
}
}
}
@ -109,7 +115,7 @@ enum class Theme {
suspend fun PipelineContext<Unit, ApplicationCall>.respondMain(
noMenu: Boolean = false,
stretch: Boolean = false,
body: MainTemplate.() -> Unit
body: MainTemplate.(Theme) -> Unit
) {
val param = call.request.queryParameters["theme"]
val url = call.request.uri.substring(1)
@ -124,15 +130,17 @@ suspend fun PipelineContext<Unit, ApplicationCall>.respondMain(
)
call.respondRedirect(call.request.path())
} else {
val theme = Theme.lookup(call.request.cookies["theme"])
call.respondHtmlTemplate(
MainTemplate(
Theme.lookup(call.request.cookies["theme"]),
theme,
url,
user,
noMenu,
stretch
),
body = body
)
) {
body(theme)
}
}
}

View file

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