Skip to content

Commit 500d6a4

Browse files
committed
Display images in posts
1 parent 3b6e1ac commit 500d6a4

File tree

6 files changed

+196
-4
lines changed

6 files changed

+196
-4
lines changed

src/main/java/zone/nox/Site.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public class Site implements SiteConfiguration {
1818
private static final Path RESOURCES = Path.of("src/main/resources/resources").toAbsolutePath();
1919
private static final Path SOCIAL_LINKS = RESOURCES.resolve("social-icons");
2020
private static final Path VIDEOS = Path.of("src/main/resources/videos").toAbsolutePath();
21+
private static final Path IMAGES = Path.of("src/main/resources/images").toAbsolutePath();
2122

2223
private final Config config;
2324

@@ -45,6 +46,9 @@ public Outline createOutline(Outliner outliner) {
4546
outliner
4647
.sourceBinaryFiles("videos", VIDEOS)
4748
.storeResource(res -> res.file().getFileName().toString());
49+
outliner
50+
.sourceBinaryFiles("images", IMAGES)
51+
.storeResource(res -> "/" + IMAGES.getParent().relativize(res.file()));
4852

4953
outliner
5054
.sourceTextFiles("posts", POSTS)

src/main/java/zone/nox/components/Components.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
package zone.nox.components;
22

3+
import dev.nipafx.ginevra.html.Image;
34
import zone.nox.data.Post;
45

5-
import java.time.LocalDate;
6-
import java.time.LocalDateTime;
7-
import java.time.format.DateTimeFormatter;
86
import java.util.List;
97
import java.util.Optional;
108

@@ -26,4 +24,8 @@ public static PostContent postContent(Post post) {
2624
return new PostContent(post);
2725
}
2826

27+
public static PostImage postImage(Image image) {
28+
return new PostImage(image);
29+
}
30+
2931
}

src/main/java/zone/nox/components/PostContent.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import dev.nipafx.ginevra.html.Classes;
77
import dev.nipafx.ginevra.html.Component;
88
import dev.nipafx.ginevra.html.Element;
9+
import dev.nipafx.ginevra.html.Image;
910
import dev.nipafx.ginevra.html.Video.Preload;
1011
import dev.nipafx.ginevra.outline.Compose;
1112
import dev.nipafx.ginevra.outline.Resources;
@@ -15,9 +16,12 @@
1516

1617
import static dev.nipafx.ginevra.html.GmlElement.html;
1718
import static dev.nipafx.ginevra.html.GmlElement.nothing;
19+
import static dev.nipafx.ginevra.html.GmlElement.transform;
1820
import static dev.nipafx.ginevra.html.HtmlElement.div;
21+
import static dev.nipafx.ginevra.html.HtmlElement.p;
1922
import static dev.nipafx.ginevra.html.HtmlElement.video;
2023
import static zone.nox.components.Components.pageHeader;
24+
import static zone.nox.components.Components.postImage;
2125

2226
public record PostContent(Post post, boolean embedLocalVideo, boolean embedYouTubeVideo) implements Component {
2327

@@ -85,7 +89,14 @@ public List<Element> composeContent() {
8589
div.classes(STYLE.header).children(pageHeader.post(post)),
8690
embeddedLocalVideo(),
8791
embeddedYouTubeVideo(),
88-
div.classes(STYLE.content).children(post.content().elements()));
92+
div.classes(STYLE.content).children(
93+
transform
94+
.elements(post.content().elements())
95+
.with(p.getClass(), p ->
96+
p.children().size() == 1 && p.children().getFirst() instanceof Image img
97+
? postImage(img)
98+
: p)
99+
));
89100
}
90101

91102
private Element embeddedLocalVideo() {
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
package zone.nox.components;
2+
3+
import dev.nipafx.ginevra.css.Css;
4+
import dev.nipafx.ginevra.css.CssStyle;
5+
import dev.nipafx.ginevra.css.StyledWith;
6+
import dev.nipafx.ginevra.html.*;
7+
import dev.nipafx.ginevra.outline.Resources;
8+
9+
import static dev.nipafx.ginevra.html.HtmlElement.div;
10+
import static dev.nipafx.ginevra.html.HtmlElement.img;
11+
12+
public record PostImage(String src, Ratio ratio, String alt) implements Component {
13+
14+
public record Style(Classes outer, Classes inner, Classes image,
15+
Classes r_16_9, Classes r_3_2, Classes r_4_3, Classes r_1_1, Classes r_3_4, Classes r_2_3, Classes r_9_16,
16+
Css css) implements CssStyle { }
17+
18+
@StyledWith
19+
public static final Style STYLE = Css.parse(Style.class, """
20+
.outer {
21+
padding: 0 1em;
22+
}
23+
24+
.inner {
25+
position: relative;
26+
overflow: hidden;
27+
28+
border: 1px solid var(--yellow);
29+
}
30+
31+
.r_16_9 {
32+
padding-top: 56.25%;
33+
}
34+
35+
.r_3_2 {
36+
padding-top: 66.67%;
37+
}
38+
39+
.r_4_3 {
40+
padding-top: 75%;
41+
}
42+
43+
.r_1_1 {
44+
padding-top: 100%;
45+
}
46+
47+
.r_3_4 {
48+
padding-top: 133.33%;
49+
}
50+
51+
.r_2_3 {
52+
padding-top: 150%;
53+
}
54+
55+
.r_9_16 {
56+
padding-top: 177.78%;
57+
}
58+
59+
.image {
60+
display: block;
61+
position: absolute;
62+
top: 0;
63+
left: 0;
64+
width: 100%;
65+
height: 100%;
66+
}
67+
68+
.youTubeVideo {
69+
position: relative;
70+
overflow: hidden;
71+
/* 16:9 portrait aspect ratio */
72+
padding-top: 177.78%;
73+
}
74+
75+
.youTubeVideo > image {
76+
display: block;
77+
position: absolute;
78+
top: 0;
79+
left: 0;
80+
width: 100%;
81+
height: 100%;
82+
border: 0;
83+
}
84+
""");
85+
86+
public PostImage(Image image) {
87+
var src = image.src().asHtmlValue();
88+
var settings = image.alt().split(" \\| ");
89+
var ratio = Ratio.parse(settings[0]);
90+
var alt = settings.length > 1 ? settings[1] : "";
91+
this(src, ratio, alt);
92+
}
93+
94+
@Override
95+
public Element compose() {
96+
return div
97+
.classes(STYLE.outer)
98+
.children(div
99+
.classes(STYLE.inner.plus(ratio.toCssClass()))
100+
.children(img
101+
.classes(STYLE.image)
102+
.src(Resources.include(src))
103+
.alt(alt)));
104+
}
105+
106+
private enum Ratio {
107+
108+
_16_9, _3_2, _4_3, _1_1, _3_4, _2_3, _9_16;
109+
110+
static Ratio parse(String value) {
111+
return switch (value) {
112+
case "16:9" -> _16_9;
113+
case "3:2" -> _3_2;
114+
case "4:3" -> _4_3;
115+
case "1:1" -> _1_1;
116+
case "3:4" -> _3_4;
117+
case "2:3" -> _2_3;
118+
case "9:16" -> _9_16;
119+
default -> throw new IllegalArgumentException("Unknown ratio: " + value);
120+
};
121+
}
122+
123+
Classes toCssClass() {
124+
return switch (this) {
125+
case _16_9 -> STYLE.r_16_9;
126+
case _3_2 -> STYLE.r_3_2;
127+
case _4_3 -> STYLE.r_4_3;
128+
case _1_1 -> STYLE.r_1_1;
129+
case _3_4 -> STYLE.r_3_4;
130+
case _2_3 -> STYLE.r_2_3;
131+
case _9_16 -> STYLE.r_9_16;
132+
};
133+
}
134+
135+
}
136+
137+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package zone.nox.data;
2+
3+
import java.time.LocalDate;
4+
import java.time.LocalDateTime;
5+
import java.time.format.DateTimeFormatter;
6+
import java.util.List;
7+
8+
public record Neotropolis(LocalDate start, LocalDate end) {
9+
10+
private Neotropolis(LocalDate start) {
11+
var daysToSaturday = 6 /* SATURDAY */ - start.getDayOfWeek().getValue();
12+
var saturday = start.plusDays(daysToSaturday);
13+
this(start, saturday);
14+
}
15+
16+
private static final List<Neotropolis> EVENTS = List.of(
17+
new Neotropolis(LocalDate.of(2024, 4, 24)),
18+
new Neotropolis(LocalDate.of(2025, 4, 23)));
19+
private static final DateTimeFormatter ABSOLUTE_DATE = DateTimeFormatter.ofPattern("yyyy-MM-dd / HH:mm 'NTZ'");
20+
private static final DateTimeFormatter TIME = DateTimeFormatter.ofPattern("HH:mm 'NTZ'");
21+
22+
public static String format(LocalDateTime dateTime) {
23+
var neotropolis = EVENTS.stream()
24+
.filter(neo -> dateTime.toLocalDate().isBefore(neo.end().plusMonths(1)))
25+
.findFirst()
26+
.orElse(EVENTS.getLast());
27+
return formatRelativeTo(dateTime, neotropolis);
28+
}
29+
30+
private static String formatRelativeTo(LocalDateTime dateTime, Neotropolis neo) {
31+
var isNotDuring = dateTime.toLocalDate().isBefore(neo.start())
32+
|| dateTime.toLocalDate().isAfter(neo.end());
33+
return isNotDuring
34+
? ABSOLUTE_DATE.format(dateTime)
35+
: "Day " + (dateTime.getDayOfMonth() - neo.start().getDayOfMonth() + 1) + " / " + TIME.format(dateTime);
36+
}
37+
38+
}

src/main/resources/images/.gitkeep

Whitespace-only changes.

0 commit comments

Comments
 (0)