🚸 Improve mail with movies from the Savoy
[rhynodge.git] / src / main / kotlin / net / pterodactylus / rhynodge / filters / webpages / savoy / MovieState.kt
1 package net.pterodactylus.rhynodge.filters.webpages.savoy
2
3 import com.fasterxml.jackson.annotation.JsonProperty
4 import kotlinx.html.a
5 import kotlinx.html.body
6 import kotlinx.html.div
7 import kotlinx.html.dom.serialize
8 import kotlinx.html.head
9 import kotlinx.html.html
10 import kotlinx.html.img
11 import kotlinx.html.li
12 import kotlinx.html.ol
13 import kotlinx.html.section
14 import kotlinx.html.span
15 import kotlinx.html.style
16 import kotlinx.html.title
17 import kotlinx.html.ul
18 import net.pterodactylus.rhynodge.Reaction
19 import net.pterodactylus.rhynodge.states.AbstractState
20 import java.time.LocalDateTime
21 import java.util.Locale
22
23 class MovieState(@JsonProperty val movies: Collection<Movie>, val newMovies: Collection<Movie> = emptySet(), private val triggered: Boolean = false) : AbstractState() {
24
25         private constructor() : this(emptySet())
26
27         override fun summary(reaction: Reaction) =
28                 "%s â€“ Programme for %tY-%<tm-%<td".format(reaction.name(), earliestMovie?.earliestPerformance)
29
30         override fun plainText() = ""
31
32         override fun htmlText() = kotlinx.html.dom.createHTMLDocument().html {
33                 head {
34                         title { +"Savoy Programme" }
35                         style("text/css") {
36                                 +"html { font-family: Roboto; }"
37                                 +"section.new-movies > .label { font-family: Impact; background-color: black; padding: 0.5ex; font-size: 200%; color: #ffffee; font-weight: bold; }"
38                                 +"section.new-movies ul { padding: 0; margin: 0; }"
39                                 +"section.new-movies li.movie { list-style: none; width: 250px; height: 353px; display: inline-block; position: relative; margin: 1ex 1ex 1ex 0ex; }"
40                                 +"section.new-movies li.movie img { width: 100%; height: 100%; display: block; position: absolute; z-index: -1; }"
41                                 +"section.new-movies li.movie .text { color: white; text-shadow: 2px 2px black; font-weight: bold; font-size: 150%; display: flex; flex-direction: column; justify-content: end; width: 100%; height: 100%; }"
42                                 +"section.new-movies li.movie .text .name { padding: 1ex; font-size: 120%; }"
43
44                                 +"section.daily-programmes ol { padding: 0; }"
45                                 +"section.daily-programmes li { list-style: none; }"
46                                 +"section.daily-programmes li.day > .label { font-family: Impact; background-color: black; padding: 0.5ex; font-size: 200%; color: #ffffee; font-weight: bold; }"
47                                 +"section.daily-programmes li .performances { display: flex; border-top: 1ex; }"
48                                 +"section.daily-programmes li.performance { width: 250px; height: 353px; display: inline-block; margin: 1ex 1ex 1ex 0ex; position: relative; }"
49                                 +"section.daily-programmes li.performance a { width: 100%; height: 100%; display: block; text-decoration: none; }"
50                                 +"section.daily-programmes li.performance a img { width: 100%; height: 100%; display: block; position: absolute; z-index: -1; }"
51                                 +"section.daily-programmes li.performance a .text { color: white; text-shadow: 2px 2px black; font-weight: bold; font-size: 150%; display: flex; flex-direction: column; justify-content: space-between; width: 100%; height: 100%; }"
52                                 +"section.daily-programmes li.performance a .text .time { padding: 1ex; text-align: right; }"
53                                 +"section.daily-programmes li.performance a .text .name { padding: 1ex; font-size: 120%; }"
54                         }
55                 }
56                 body {
57                         if (newMovies.isNotEmpty()) {
58                                 section("new-movies") {
59                                         div("label") {
60                                                 +"New Movies"
61                                         }
62                                         ul {
63                                                 newMovies.forEach { movie ->
64                                                         li("movie") {
65                                                                 img(src = movie.imageUrl)
66                                                                 div("text") {
67                                                                         div("name") {
68                                                                                 +(movie.name)
69                                                                         }
70                                                                 }
71                                                         }
72                                                 }
73                                         }
74                                 }
75                         }
76
77                         section("daily-programmes") {
78
79                                 ol("days") {
80                                         movies.flatMap { it.performances.map(Performance::getTime).map(LocalDateTime::toLocalDate) }.distinct().sorted().forEach { date ->
81                                                 li("day") {
82                                                         attributes += "data-date" to "%tY-%<tm-%<td".format(date)
83                                                         div("label") {
84                                                                 +("Programme for %tA, %<tY-%<tm-%<td".format(Locale.ENGLISH, date))
85                                                         }
86                                                         ol("performances") {
87                                                                 movies
88                                                                         .flatMap { movie -> movie.performances.map { movie to it } }
89                                                                         .filter { (movie, performances) -> performances.time.toLocalDate() == date }
90                                                                         .sortedBy { (_, performance) -> performance.time }
91                                                                         .forEach { (movie, performance) ->
92                                                                                 li("performance") {
93                                                                                         a(href = performance.link) {
94                                                                                                 img(src = movie.imageUrl)
95                                                                                                 div("text") {
96                                                                                                         div("time") {
97                                                                                                                 +("%tH:%<tM".format(performance.time))
98                                                                                                         }
99                                                                                                         div("name") {
100                                                                                                                 +(movie.name)
101                                                                                                         }
102                                                                                                 }
103                                                                                         }
104                                                                                 }
105                                                                         }
106                                                         }
107                                                 }
108                                         }
109                                 }
110                         }
111                 }
112
113         }.serialize()
114
115         override fun triggered() = newMovies.isNotEmpty() || triggered
116
117         private val earliestMovie = movies.minByOrNull { it.earliestPerformance ?: LocalDateTime.MAX }
118         private val Movie.earliestPerformance: LocalDateTime? get() = performances.minOfOrNull(Performance::getTime)
119
120 }