Skip to content

Commit 5363d3b

Browse files
feat: add pageable message builder interface and implementation
1 parent 0fb06d9 commit 5363d3b

File tree

2 files changed

+155
-0
lines changed

2 files changed

+155
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package dev.slne.surf.surfapi.core.api.messages.builder
2+
3+
import net.kyori.adventure.audience.Audience
4+
5+
/**
6+
* DSL interface for building pageable messages using [PageableMessageBuilder].
7+
* Provides a structured way to define paginated message content and send it to an [Audience].
8+
*/
9+
@DslMarker
10+
annotation class SurfPageableMessageBuilderDsl
11+
12+
@SurfPageableMessageBuilderDsl
13+
interface SurfPageableMessageBuilder {
14+
15+
/**
16+
* The number of lines displayed per page.
17+
*/
18+
var linesPerPage: Int
19+
20+
/**
21+
* The base command used for navigating between pages.
22+
* Example: "/example page %page%"
23+
*/
24+
var pageCommand: String
25+
26+
/**
27+
* Sets the message title.
28+
*
29+
* @param block a builder block to configure the title using [SurfComponentBuilder]
30+
*/
31+
fun title(block: SurfComponentBuilder.() -> Unit)
32+
33+
/**
34+
* Adds a line of content to the message.
35+
*
36+
* @param block a builder block to configure the line using [SurfComponentBuilder]
37+
*/
38+
fun line(block: SurfComponentBuilder.() -> Unit)
39+
40+
/**
41+
* Sends the paginated message to the given [Audience] at the specified page.
42+
*
43+
* @param sender the audience receiving the message
44+
* @param page the page number to display (starting at 1)
45+
*/
46+
fun send(sender: Audience, page: Int)
47+
48+
companion object {
49+
/**
50+
* Creates a new [PageableMessageBuilder] instance and applies the provided DSL block.
51+
*
52+
* @param block the DSL configuration block
53+
* @return a fully constructed [PageableMessageBuilder] instance
54+
*/
55+
@JvmStatic
56+
operator fun invoke(linesPerPage: Int = 10, block: PageableMessageBuilder.() -> Unit): PageableMessageBuilder {
57+
return PageableMessageBuilder(linesPerPage).apply(block)
58+
}
59+
}
60+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package dev.slne.surf.surfapi.core.api.messages.builder
2+
3+
import dev.slne.surf.surfapi.core.api.font.toSmallCaps
4+
import dev.slne.surf.surfapi.core.api.messages.adventure.buildText
5+
import dev.slne.surf.surfapi.core.api.messages.adventure.clickRunsCommand
6+
import dev.slne.surf.surfapi.core.api.messages.adventure.sendText
7+
import dev.slne.surf.surfapi.core.api.util.mutableObjectListOf
8+
import net.kyori.adventure.audience.Audience
9+
import net.kyori.adventure.text.Component
10+
import kotlin.math.ceil
11+
import kotlin.math.min
12+
13+
/**
14+
* Default implementation of the [SurfPageableMessageBuilder] interface.
15+
*/
16+
@SurfPageableMessageBuilderDsl
17+
class PageableMessageBuilder(
18+
override var linesPerPage: Int = 10
19+
) : SurfPageableMessageBuilder {
20+
21+
private val lines = mutableObjectListOf<Component>()
22+
private var title: Component = Component.empty()
23+
24+
override var pageCommand: String = "An error occurred while trying to display the page."
25+
26+
override fun line(block: SurfComponentBuilder.() -> Unit) {
27+
lines.add(SurfComponentBuilder(block))
28+
}
29+
30+
override fun title(block: SurfComponentBuilder.() -> Unit) {
31+
title = SurfComponentBuilder(block)
32+
}
33+
34+
override fun send(sender: Audience, page: Int) {
35+
val totalPages = ceil(lines.size.toDouble() / linesPerPage).toInt().coerceAtLeast(1)
36+
if (page < 1 || page > totalPages) {
37+
sender.sendText {
38+
error("Seite ")
39+
variableValue(page.toString())
40+
error(" existiert nicht.")
41+
}
42+
return
43+
}
44+
45+
val start = (page - 1) * linesPerPage
46+
val end = min(start + linesPerPage, lines.size)
47+
48+
sender.sendText {
49+
if (title != Component.empty()) {
50+
append(title)
51+
appendNewline()
52+
}
53+
54+
for (i in start until end) {
55+
append(lines[i])
56+
appendNewline()
57+
}
58+
59+
if (totalPages > 1) {
60+
append(paginationComponent(page, totalPages))
61+
}
62+
}
63+
}
64+
65+
private fun navButton(label: String, targetPage: Int, enabled: Boolean): Component {
66+
return buildText {
67+
if (enabled) {
68+
success(label)
69+
clickRunsCommand(pageCommand.replace("%page%", targetPage.toString()))
70+
} else {
71+
error(label)
72+
}
73+
}
74+
}
75+
76+
private fun paginationComponent(page: Int, totalPages: Int): Component {
77+
return buildText {
78+
append(navButton("[<<] ", 1, page > 1))
79+
append(navButton("[<] ", page - 1, page > 1))
80+
darkSpacer("Seite $page/$totalPages".toSmallCaps())
81+
append(navButton(" [>] ", page + 1, page < totalPages))
82+
append(navButton(" [>>]", totalPages, page < totalPages))
83+
}
84+
}
85+
86+
companion object {
87+
/**
88+
* DSL-style builder entry point.
89+
*/
90+
@JvmStatic
91+
operator fun invoke(block: PageableMessageBuilder.() -> Unit): PageableMessageBuilder {
92+
return PageableMessageBuilder().apply(block)
93+
}
94+
}
95+
}

0 commit comments

Comments
 (0)