💄 Fix display on Samsung Email app
[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.style
15 import kotlinx.html.title
16 import kotlinx.html.ul
17 import kotlinx.html.unsafe
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                                 unsafe {
37                                         +"html { font-family: 'Recursive Sans Linear Static', Roboto, serif; }"
38                                         +"section.new-movies > .label { font-family: Impact, sans-serif; background-color: black; padding: 0.5ex; font-size: 200%; color: #ffffee; font-weight: bold; }"
39                                         +"section.new-movies ul { padding: 0; margin: 0; }"
40                                         +"section.new-movies li.movie { list-style: none; display: grid; margin: 1ex 1ex 1ex 0ex; grid-template-columns: 250px auto; }"
41                                         +"section.new-movies li.movie img { grid-area: 1/1/2/2; height: 353px; }"
42                                         +"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; grid-area: 1/1/2/2; height: 353px; }"
43                                         +"section.new-movies li.movie .text .name { padding: 1ex; font-size: 120%; }"
44                                         +"section.new-movies li.movie .description { padding: 1ex; }"
45
46                                         +"section.daily-programmes ol { padding: 0; }"
47                                         +"section.daily-programmes li { list-style: none; }"
48                                         +"section.daily-programmes li.day > .label { font-family: Impact, sans-serif; background-color: black; padding: 0.5ex; font-size: 200%; color: #ffffee; font-weight: bold; }"
49                                         +"section.daily-programmes li .performances { border-top: 1ex; }"
50                                         +"section.daily-programmes li.performance { display: inline-block; margin: 1ex 1ex 1ex 0ex; }"
51                                         +"section.daily-programmes li.performance a { text-decoration: none; color: white; text-shadow: 2px 2px black; font-weight: bold; font-size: 150%; display: grid; width: 250px; height: 353px; grid-template-rows: 0fr 0fr 1fr 0fr; grid-template-columns: 250px; }"
52                                         +"section.daily-programmes li.performance a img { grid-area: 1/1/5/1; z-index: -1; width: 100%; height: 100%; }"
53                                         +"section.daily-programmes li.performance a .time { grid-area: 1/1/2/1; padding: 1ex 1ex 0ex 1ex; text-align: right; }"
54                                         +"section.daily-programmes li.performance a .type { grid-area: 2/1/3/1; padding: 0ex 1ex 1ex 1ex; text-align: right; }"
55                                         +"section.daily-programmes li.performance a .name { grid-area: 4/1/5/1; padding: 1ex; font-size: 120%; }"
56                                 }
57                         }
58                 }
59                 body {
60                         if (newMovies.isNotEmpty()) {
61                                 section("new-movies") {
62                                         div("label") {
63                                                 +"New Movies"
64                                         }
65                                         ul {
66                                                 newMovies.forEach { movie ->
67                                                         li("movie") {
68                                                                 img(src = movie.imageUrl)
69                                                                 div("text") {
70                                                                         div("name") {
71                                                                                 +(movie.name)
72                                                                         }
73                                                                 }
74                                                                 div("description") {
75                                                                         +movie.description
76                                                                 }
77                                                         }
78                                                 }
79                                         }
80                                 }
81                         }
82
83                         section("daily-programmes") {
84
85                                 ol("days") {
86                                         movies.flatMap { it.performances.map(Performance::time).map(LocalDateTime::toLocalDate) }.distinct().sorted().forEach { date ->
87                                                 li("day") {
88                                                         attributes += "data-date" to "%tY-%<tm-%<td".format(date)
89                                                         div("label") {
90                                                                 +("Programme for %tA, %<tY-%<tm-%<td".format(Locale.ENGLISH, date))
91                                                         }
92                                                         ol("performances") {
93                                                                 movies
94                                                                         .flatMap { movie -> movie.performances.map { movie to it } }
95                                                                         .filter { (movie, performances) -> performances.time.toLocalDate() == date }
96                                                                         .sortedBy { (_, performance) -> performance.time }
97                                                                         .forEach { (movie, performance) ->
98                                                                                 li("performance") {
99                                                                                         a(href = performance.link) {
100                                                                                                 img(src = movie.imageUrl)
101                                                                                                 div("time") {
102                                                                                                         +("%tH:%<tM".format(performance.time))
103                                                                                                 }
104                                                                                                 div("type") {
105                                                                                                         +performance.type
106                                                                                                 }
107                                                                                                 div("name") {
108                                                                                                         +(movie.name)
109                                                                                                 }
110                                                                                         }
111                                                                                 }
112                                                                         }
113                                                         }
114                                                 }
115                                         }
116                                 }
117                         }
118                 }
119
120         }.serialize()
121
122         override fun triggered() = newMovies.isNotEmpty() || triggered
123
124         private val earliestMovie = movies.minByOrNull { it.earliestPerformance ?: LocalDateTime.MAX }
125         private val Movie.earliestPerformance: LocalDateTime? get() = performances.minOfOrNull(Performance::time)
126
127 }