Skip to content
Merged
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled
javaVersion=25
mcVersion=1.21.11
group=dev.slne.surf
version=1.21.11-2.57.0
version=1.21.11-2.58.0
relocationPrefix=dev.slne.surf.surfapi.libs
snapshot=false
105 changes: 105 additions & 0 deletions surf-api-bukkit/surf-api-bukkit-api/api/surf-api-bukkit-api.api
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,111 @@ public final class dev/slne/surf/surfapi/bukkit/api/dialog/builder/DialogTypeBui
public static final fun dialogType (Lkotlin/jvm/functions/Function1;)Lio/papermc/paper/registry/data/dialog/type/DialogType;
}

public final class dev/slne/surf/surfapi/bukkit/api/dialog/composition/DialogScope {
public fun <init> (Ldev/slne/surf/surfapi/bukkit/api/dialog/composition/DialogStore;Lkotlinx/coroutines/CoroutineScope;)V
public final fun remember ([Ljava/lang/Object;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public final fun setState (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public final fun state ()Ldev/slne/surf/surfapi/bukkit/api/dialog/state/DialogState;
}

public final class dev/slne/surf/surfapi/bukkit/api/dialog/composition/DialogStore {
public fun <init> (Ldev/slne/surf/surfapi/bukkit/api/dialog/state/DialogState;Ljava/util/UUID;Lkotlin/jvm/functions/Function2;Lkotlinx/coroutines/CoroutineScope;)V
public final fun getState ()Ldev/slne/surf/surfapi/bukkit/api/dialog/state/DialogState;
public final fun open (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public final fun recall (Ljava/util/List;)Ljava/lang/Object;
public final fun remember (Ljava/util/List;Ljava/lang/Object;)V
public final fun update (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public final class dev/slne/surf/surfapi/bukkit/api/dialog/composition/DslKt {
public static final fun composableDialog (Lorg/bukkit/entity/Player;Ldev/slne/surf/surfapi/bukkit/api/dialog/state/DialogState;Lkotlinx/coroutines/CoroutineScope;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public final class dev/slne/surf/surfapi/bukkit/api/dialog/pagination/PaginatedDialogKt {
public static final fun paginatedDialog (Lorg/bukkit/entity/Player;Ldev/slne/surf/surfapi/bukkit/api/dialog/query/DialogQuery;Lkotlin/jvm/functions/Function3;ZLkotlin/jvm/functions/Function1;Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun paginatedDialog$default (Lorg/bukkit/entity/Player;Ldev/slne/surf/surfapi/bukkit/api/dialog/query/DialogQuery;Lkotlin/jvm/functions/Function3;ZLkotlin/jvm/functions/Function1;Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
}

public abstract interface class dev/slne/surf/surfapi/bukkit/api/dialog/query/CursorDialogQuery {
public abstract fun execute (Ldev/slne/surf/surfapi/bukkit/api/dialog/state/DialogState;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public final class dev/slne/surf/surfapi/bukkit/api/dialog/query/CursorResult {
public fun <init> (Ljava/util/List;Ljava/lang/String;Z)V
public final fun component1 ()Ljava/util/List;
public final fun component2 ()Ljava/lang/String;
public final fun component3 ()Z
public final fun copy (Ljava/util/List;Ljava/lang/String;Z)Ldev/slne/surf/surfapi/bukkit/api/dialog/query/CursorResult;
public static synthetic fun copy$default (Ldev/slne/surf/surfapi/bukkit/api/dialog/query/CursorResult;Ljava/util/List;Ljava/lang/String;ZILjava/lang/Object;)Ldev/slne/surf/surfapi/bukkit/api/dialog/query/CursorResult;
public fun equals (Ljava/lang/Object;)Z
public final fun getHasMore ()Z
public final fun getItems ()Ljava/util/List;
public final fun getNextCursor ()Ljava/lang/String;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public final class dev/slne/surf/surfapi/bukkit/api/dialog/query/CursorState : dev/slne/surf/surfapi/bukkit/api/dialog/state/DialogState {
public fun <init> ()V
public fun <init> (Ljava/lang/String;Ljava/util/List;ILjava/lang/String;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/util/List;ILjava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Ljava/lang/String;
public final fun component2 ()Ljava/util/List;
public final fun component3 ()I
public final fun component4 ()Ljava/lang/String;
public final fun copy (Ljava/lang/String;Ljava/util/List;ILjava/lang/String;)Ldev/slne/surf/surfapi/bukkit/api/dialog/query/CursorState;
public static synthetic fun copy$default (Ldev/slne/surf/surfapi/bukkit/api/dialog/query/CursorState;Ljava/lang/String;Ljava/util/List;ILjava/lang/String;ILjava/lang/Object;)Ldev/slne/surf/surfapi/bukkit/api/dialog/query/CursorState;
public fun equals (Ljava/lang/Object;)Z
public final fun getCursor ()Ljava/lang/String;
public final fun getHistory ()Ljava/util/List;
public final fun getLimit ()I
public final fun getSearch ()Ljava/lang/String;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public abstract interface class dev/slne/surf/surfapi/bukkit/api/dialog/query/DialogQuery {
public abstract fun execute (Ldev/slne/surf/surfapi/bukkit/api/dialog/state/DialogState;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public final class dev/slne/surf/surfapi/bukkit/api/dialog/query/PageResult {
public fun <init> (Ljava/util/List;IILjava/lang/Integer;)V
public synthetic fun <init> (Ljava/util/List;IILjava/lang/Integer;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Ljava/util/List;
public final fun component2 ()I
public final fun component3 ()I
public final fun component4 ()Ljava/lang/Integer;
public final fun copy (Ljava/util/List;IILjava/lang/Integer;)Ldev/slne/surf/surfapi/bukkit/api/dialog/query/PageResult;
public static synthetic fun copy$default (Ldev/slne/surf/surfapi/bukkit/api/dialog/query/PageResult;Ljava/util/List;IILjava/lang/Integer;ILjava/lang/Object;)Ldev/slne/surf/surfapi/bukkit/api/dialog/query/PageResult;
public fun equals (Ljava/lang/Object;)Z
public final fun getItems ()Ljava/util/List;
public final fun getPage ()I
public final fun getTotalItems ()Ljava/lang/Integer;
public final fun getTotalPages ()I
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public final class dev/slne/surf/surfapi/bukkit/api/dialog/query/PageState : dev/slne/surf/surfapi/bukkit/api/dialog/state/DialogState {
public fun <init> ()V
public fun <init> (IILjava/lang/String;)V
public synthetic fun <init> (IILjava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()I
public final fun component2 ()I
public final fun component3 ()Ljava/lang/String;
public final fun copy (IILjava/lang/String;)Ldev/slne/surf/surfapi/bukkit/api/dialog/query/PageState;
public static synthetic fun copy$default (Ldev/slne/surf/surfapi/bukkit/api/dialog/query/PageState;IILjava/lang/String;ILjava/lang/Object;)Ldev/slne/surf/surfapi/bukkit/api/dialog/query/PageState;
public fun equals (Ljava/lang/Object;)Z
public final fun getLimit ()I
public final fun getPage ()I
public final fun getSearch ()Ljava/lang/String;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public abstract interface class dev/slne/surf/surfapi/bukkit/api/dialog/state/DialogState {
}

public final class dev/slne/surf/surfapi/bukkit/api/event/Listener_extensionKt {
public static final fun cancel (Lorg/bukkit/event/Cancellable;)V
public static final fun listen (Lorg/bukkit/plugin/Plugin;Lkotlin/reflect/KClass;Lorg/bukkit/event/EventPriority;ZZLkotlin/jvm/functions/Function1;)Ldev/slne/surf/surfapi/bukkit/api/event/SingleListener;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
@file:Suppress("UnstableApiUsage")

package dev.slne.surf.surfapi.bukkit.api.dialog.composition

import dev.slne.surf.surfapi.bukkit.api.dialog.state.DialogState
import kotlinx.coroutines.CoroutineScope

class DialogScope<S : DialogState>(
private val store: DialogStore<S>,
private val scope: CoroutineScope,
) {
fun state(): S = store.getState()

suspend fun setState(transform: S.() -> S) {
store.update(transform)
}

suspend fun <T> remember(
vararg keys: Any?,
block: suspend CoroutineScope.() -> T
): T {
val key = keys.toList()
val cached = store.recall<T>(key)

if (cached != null) return cached

val value = block(scope)

store.remember<T>(key, value)

return value
Comment thread
ammodev marked this conversation as resolved.
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package dev.slne.surf.surfapi.bukkit.api.dialog.composition

import dev.slne.surf.surfapi.bukkit.api.dialog.state.DialogState
import dev.slne.surf.surfapi.bukkit.api.extensions.server
import io.papermc.paper.dialog.Dialog
import kotlinx.coroutines.CoroutineScope
import java.util.*
import java.util.concurrent.ConcurrentHashMap

class DialogStore<S : DialogState>(
initialState: S,
private val playerUuid: UUID,
private val renderer: suspend DialogScope<S>.() -> Dialog,
private val scope: CoroutineScope,
) {
private var currentState: S = initialState
private var mounted = true

private val memory = ConcurrentHashMap<List<Any?>, Any?>()

suspend fun open() {
rerender()
}

suspend fun update(transform: S.() -> S) {
currentState = currentState.transform()
rerender()
}
Comment thread
ammodev marked this conversation as resolved.

fun getState(): S = currentState

internal suspend fun rerender() {
if (!mounted) return

val dialog = DialogScope(this, scope).renderer()
findPlayer()?.showDialog(dialog)
}
Comment thread
ammodev marked this conversation as resolved.

@Suppress("UNCHECKED_CAST")
fun <T> remember(key: List<Any?>, value: Any?) {
memory[key] = value
}

@Suppress("UNCHECKED_CAST")
fun <T> recall(key: List<Any?>): T? {
return memory[key] as? T
}

private fun findPlayer() = server.getPlayer(playerUuid)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
@file:Suppress("UnstableApiUsage")

package dev.slne.surf.surfapi.bukkit.api.dialog.composition

import dev.slne.surf.surfapi.bukkit.api.dialog.state.DialogState
import io.papermc.paper.dialog.Dialog
import kotlinx.coroutines.CoroutineScope
import org.bukkit.entity.Player

suspend fun <S : DialogState> composableDialog(
player: Player,
initialState: S,
scope: CoroutineScope,
content: suspend DialogScope<S>.() -> Dialog
) {
val store = DialogStore(
initialState = initialState,
playerUuid = player.uniqueId,
renderer = content,
scope = scope
)

store.open()
}
Comment thread
ammodev marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
@file:Suppress("UnstableApiUsage")

package dev.slne.surf.surfapi.bukkit.api.dialog.pagination

import dev.slne.surf.surfapi.bukkit.api.dialog.base
import dev.slne.surf.surfapi.bukkit.api.dialog.builder.actionButton
import dev.slne.surf.surfapi.bukkit.api.dialog.composition.composableDialog
import dev.slne.surf.surfapi.bukkit.api.dialog.dialog
import dev.slne.surf.surfapi.bukkit.api.dialog.query.DialogQuery
import dev.slne.surf.surfapi.bukkit.api.dialog.query.PageResult
import dev.slne.surf.surfapi.bukkit.api.dialog.query.PageState
import dev.slne.surf.surfapi.bukkit.api.dialog.type
import dev.slne.surf.surfapi.core.api.messages.builder.SurfComponentBuilder
import io.papermc.paper.dialog.Dialog
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import net.kyori.adventure.text.Component
import org.bukkit.entity.Player

suspend fun <T> paginatedDialog(
player: Player,
query: DialogQuery<PageState, T>,
titleBuilder: SurfComponentBuilder.(PageState, PageResult<T>) -> Unit = { _, result ->
if (result.totalPages == 0) {
text("Keine Ergebnisse")
} else {
text("Seite ${result.page} von ${result.totalPages}")
}
},
searchable: Boolean = false,
itemBuilder: (T) -> Pair<Component, Dialog>,
scope: CoroutineScope
) = composableDialog(
player = player,
initialState = PageState(),
scope = scope
) {
val state = state()
val page = remember(state.page, state.search) {
query.execute(state)
}

dialog {
base {
title {
titleBuilder(this, state, page)
}

if (searchable) {
input {
text("search") {
label {
text("Suche")
}

initial(state.search ?: "")
}
}
}
}

type {
multiAction {
columns(1)

// Item Buttons
page.items.forEach { item ->
val (dialogTitle, dialog) = itemBuilder(item)

action(actionButton {
label(dialogTitle)

action {
playerCallback {
player.showDialog(dialog)
}
}
})
}

// Pagination Buttons
if (state.page > 1) {
action(actionButton {
label {
text("Zurück")
}

action {
playerCallback {
scope.launch {
setState { copy(page = page.page - 1) }
}
}
}
})
}

if (state.page < page.totalPages) {
action(actionButton {
label {
text("Weiter")
}

action {
playerCallback {
scope.launch {
setState { copy(page = page.page + 1) }
}
}
}
})
}

if (searchable) {
action(actionButton {
label {
text("Suche zurücksetzen")
}

action {
playerCallback {
scope.launch {
setState { copy(search = null, page = 1) }
}
}
}
})

action(actionButton {
label {
text("Suchen")
}

action {
customPlayerClick { response, _ ->
val search = response.getText("search") ?: ""

scope.launch {
setState { copy(search = search, page = 1) }
}
Comment thread
ammodev marked this conversation as resolved.
}
}
})
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package dev.slne.surf.surfapi.bukkit.api.dialog.query

import dev.slne.surf.surfapi.bukkit.api.dialog.state.DialogState

fun interface DialogQuery<S : DialogState, T> {
suspend fun execute(state: S): PageResult<T>
}

fun interface CursorDialogQuery<S : DialogState, T> {
suspend fun execute(state: S): CursorResult<T>
}
Loading