Skip to content

Commit

Permalink
optimized pagination with realtime database-searches
Browse files Browse the repository at this point in the history
  • Loading branch information
peterwilli committed Sep 15, 2022
1 parent 214367d commit 0a9db16
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 149 deletions.
37 changes: 19 additions & 18 deletions src/main/kotlin/commands/chapters/ListChaptersCommand.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package commands.chapters

import commands.make.DiffusionParameters
import database.chapterDao
import database.models.UserChapter
import database.userDao
import dev.minn.jda.ktx.events.onCommand
import dev.minn.jda.ktx.interactions.components.button
Expand All @@ -13,6 +14,7 @@ import net.dv8tion.jda.api.JDA
import net.dv8tion.jda.api.interactions.components.buttons.ButtonStyle
import net.dv8tion.jda.api.utils.messages.MessageEditData
import replyPaginator
import ui.GetImageCallback
import ui.ImageSliderEntry
import ui.sendImageSlider
import java.net.URL
Expand All @@ -27,36 +29,37 @@ fun listChaptersCommand(jda: JDA) {
.setEphemeral(true).queue()
return@onCommand
}
event.deferReply(true).queue()
val possibleChapters =
chapterDao.queryBuilder().orderBy("creationTimestamp", false).selectColumns().where()
.eq("userID", user.id).query()
if (possibleChapters.isEmpty()) {
val chaptersCount =
chapterDao.queryBuilder().selectColumns("id").where()
.eq("userID", user.id).countOf()
if (chaptersCount == 0L) {
event.reply_("Sorry, we couldn't find any chapters! $miniManual")
.setEphemeral(true).queue()
return@onCommand
}
val chapterEntries = possibleChapters.map {
val latestEntry = it.getLatestEntry()
var lastSelectedChapter: UserChapter? = null
val onImage: GetImageCallback = { index ->
lastSelectedChapter = chapterDao.queryBuilder().selectColumns().limit(1).offset(index).orderBy("creationTimestamp", false).where()
.eq("userID", user.id).queryForFirst()
val latestEntry = lastSelectedChapter!!.getLatestEntry()
val parameters = gson.fromJson(latestEntry.parameters, Array<DiffusionParameters>::class.java)
ImageSliderEntry(
description = parameters.first().getPrompt() ?: "No prompt",
image = URL(latestEntry.imageURL)
)
}
val slider = sendImageSlider("My Chapters", chapterEntries)
val slider = sendImageSlider("My Chapters", chaptersCount, onImage)
slider.customActionComponents = listOf(jda.button(
label = "Select",
style = ButtonStyle.PRIMARY,
user = event.user
) {
val chapter = possibleChapters[slider.getIndex()]
val parameters =
gson.fromJson(chapter.getLatestEntry().parameters, Array<DiffusionParameters>::class.java)
gson.fromJson(lastSelectedChapter!!.getLatestEntry().parameters, Array<DiffusionParameters>::class.java)

val updateBuilder = userDao.updateBuilder()
updateBuilder.where().eq("id", chapter.userID)
updateBuilder.updateColumnValue("currentChapterId", chapter.id)
updateBuilder.where().eq("id", lastSelectedChapter!!.userID)
updateBuilder.updateColumnValue("currentChapterId", lastSelectedChapter!!.id)
updateBuilder.update()

it.reply_(
Expand All @@ -69,9 +72,8 @@ fun listChaptersCommand(jda: JDA) {
style = ButtonStyle.DANGER,
user = event.user
) { deleteEvent ->
val chapter = possibleChapters[slider.getIndex()]
val parameters =
gson.fromJson(chapter.getLatestEntry().parameters, Array<DiffusionParameters>::class.java)
gson.fromJson(lastSelectedChapter!!.getLatestEntry().parameters, Array<DiffusionParameters>::class.java)

deleteEvent.reply_("**Are you sure to delete this chapter?** *${parameters.first().getPrompt()}*")
.setEphemeral(true).addActionRow(listOf(
Expand All @@ -80,8 +82,8 @@ fun listChaptersCommand(jda: JDA) {
style = ButtonStyle.DANGER,
user = event.user
) {
chapter.delete()
deleteEvent.hook.editMessage(content = "*Delete!*").setComponents().queue()
lastSelectedChapter!!.delete()
deleteEvent.hook.editMessage(content = "*Deleted!*").setComponents().queue()
},
jda.button(
label = "Keep!",
Expand All @@ -92,8 +94,7 @@ fun listChaptersCommand(jda: JDA) {
}
)).queue()
})
event.hook.editOriginal(MessageEditData.fromCreateData(slider.also { jda.addEventListener(it) }.pages[0]))
.setComponents(slider.getControls()).queue()
event.replyPaginator(slider).queue()
} catch (e: Exception) {
e.printStackTrace()
event.reply_("**Error!** $e").setEphemeral(true).queue()
Expand Down
16 changes: 10 additions & 6 deletions src/main/kotlin/commands/chapters/RollbackCommand.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package commands.chapters
import commands.make.DiffusionParameters
import database.chapterDao
import database.chapterEntryDao
import database.models.ChapterEntry
import database.userDao
import dev.minn.jda.ktx.events.onCommand
import dev.minn.jda.ktx.interactions.components.button
Expand All @@ -12,6 +13,7 @@ import miniManual
import net.dv8tion.jda.api.JDA
import net.dv8tion.jda.api.interactions.components.buttons.ButtonStyle
import replyPaginator
import ui.GetImageCallback
import ui.ImageSliderEntry
import ui.sendImageSlider
import java.net.URL
Expand All @@ -38,21 +40,23 @@ fun rollbackChapterCommand(jda: JDA) {
return@onCommand
}

val chapterEntries = usingChapter.getEntries()
val slides = chapterEntries.map { entry ->
val parameters = gson.fromJson(entry.parameters, Array<DiffusionParameters>::class.java)
val entryCount = usingChapter.getEntryCount()
var lastEntry: ChapterEntry? = null
val onImage: GetImageCallback = { index ->
lastEntry = usingChapter.getEntryAtIndex(index)
val parameters = gson.fromJson(lastEntry!!.parameters, Array<DiffusionParameters>::class.java)
ImageSliderEntry(
description = parameters.first().getPrompt() ?: "No prompt",
image = URL(entry.imageURL)
image = URL(lastEntry!!.imageURL)
)
}
val slider = sendImageSlider("Rollback to", slides)
val slider = sendImageSlider("Rollback to", entryCount, onImage)
slider.customActionComponents = listOf(jda.button(
label = "Rollback",
style = ButtonStyle.PRIMARY,
user = event.user
) {
val entryToRollBackTo = chapterEntries[slider.getIndex()]
val entryToRollBackTo = lastEntry!!
val db = chapterEntryDao.deleteBuilder()
db.where().eq("chapterID", entryToRollBackTo.chapterID).and().gt("creationTimestamp", entryToRollBackTo.creationTimestamp)
chapterEntryDao.delete(db.prepare())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ package commands.social

import commands.make.DiffusionParameters
import database.chapterDao
import database.models.SharedArtCacheEntry
import database.sharedArtCacheEntryDao
import database.userDao
import dev.minn.jda.ktx.events.onCommand
import dev.minn.jda.ktx.interactions.components.button
Expand All @@ -13,7 +11,6 @@ import gson
import miniManual
import net.dv8tion.jda.api.JDA
import net.dv8tion.jda.api.entities.Message
import net.dv8tion.jda.api.entities.User
import net.dv8tion.jda.api.entities.emoji.Emoji
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent
import net.dv8tion.jda.api.events.interaction.component.SelectMenuInteractionEvent
Expand All @@ -24,39 +21,14 @@ import net.dv8tion.jda.api.interactions.components.selections.SelectMenu
import net.dv8tion.jda.api.interactions.components.selections.SelectOption
import net.dv8tion.jda.api.requests.restaction.WebhookMessageEditAction
import net.dv8tion.jda.api.utils.FileUpload
import org.atteo.evo.inflector.English
import ui.makeSelectImageFromQuilt
import utils.*
import java.awt.Color
import java.awt.Font
import java.awt.Graphics
import java.awt.image.BufferedImage
import java.net.URL
import javax.imageio.ImageIO

object DropdownBot : ListenerAdapter() {
override fun onSlashCommandInteraction(event: SlashCommandInteractionEvent) {
if (event.name == "food") {
val selectMenu = SelectMenu.create("choose-food")
.addOption("Pizza", "pizza", "Classic") // SelectOption with only the label, value, and description
.addOptions(
SelectOption.of("Hamburger", "hamburger") // another way to create a SelectOption
.withDescription("Tasty") // this time with a description
.withEmoji(Emoji.fromUnicode("\uD83C\uDF54")) // and an emoji
.withDefault(true)) // while also being the default option
.build()
}
}

override fun onSelectMenuInteraction(event: SelectMenuInteractionEvent) {
if (event.componentId == "choose-food") {
event.reply("You chose " + event.values[0]).queue()
}
}
}

fun setBackgroundCommand(jda: JDA) {
jda.onCommand("set_background") { event ->
jda.onCommand("edit_profile") { event ->
try {
val user = userDao.queryBuilder().selectColumns("id", "currentChapterId").where()
.eq("discordUserID", event.user.id).queryForFirst()
Expand Down
1 change: 1 addition & 0 deletions src/main/kotlin/commands/variate/VariateCommand.kt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ fun variateCommand(jda: JDA) {
val usingChapter =
chapterDao.queryBuilder().selectColumns().where().eq("id", user.currentChapterId).and()
.eq("userID", user.id).queryForFirst()

if (usingChapter == null) {
event.reply_("Sorry, we couldn't find any chapters! $miniManual")
.setEphemeral(true).queue()
Expand Down
11 changes: 8 additions & 3 deletions src/main/kotlin/database/models/UserChapter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,14 @@ class UserChapter {
this.userID = userID
}

fun getEntries(): Array<ChapterEntry> {
return chapterEntryDao.queryBuilder().orderBy("creationTimestamp", false).selectColumns().where()
.eq("chapterID", this.id).query().toTypedArray()
fun getEntryAtIndex(index: Long): ChapterEntry {
return chapterEntryDao.queryBuilder().offset(index).limit(1).orderBy("creationTimestamp", false).selectColumns().where()
.eq("chapterID", this.id).queryForFirst()
}

fun getEntryCount(): Long {
return chapterEntryDao.queryBuilder().selectColumns().where()
.eq("chapterID", this.id).countOf()
}

fun getLatestEntry(): ChapterEntry {
Expand Down
50 changes: 18 additions & 32 deletions src/main/kotlin/ui/PaginationUI.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package ui

import GetPageCallback
import Paginator
import dev.minn.jda.ktx.interactions.components.button
import dev.minn.jda.ktx.messages.MessageCreate
import getAverageColor
import net.dv8tion.jda.api.EmbedBuilder
import net.dv8tion.jda.api.entities.User
Expand All @@ -23,62 +25,46 @@ data class ImageSliderEntry(
val image: URL
)

fun sendImageSlider(title: String, items: List<ImageSliderEntry>): Paginator {
val pagesAmount = items.size
val messages = items.mapIndexed { index, entry ->
typealias GetImageCallback = ((index: Long) -> ImageSliderEntry)

fun sendImageSlider(title: String, amountOfImages: Long, onImage: GetImageCallback): Paginator {
val getPageCallback: GetPageCallback = { index ->
val page = EmbedBuilder()
page.setTitle(title)
page.setFooter("Page ${index + 1} / $pagesAmount")
page.setFooter("Page ${index + 1} / $amountOfImages")
val entry = onImage(index)
page.setImage(entry.image.toString())
page.setDescription(entry.description)
page.setColor(0x33cc33)
page.build()
}.toTypedArray()
return paginator(*messages, expireAfter = 10.minutes)
MessageCreate(embeds = listOf(page.build()))
}
return paginator(amountOfImages, getPage = getPageCallback, expireAfter = 10.minutes)
}

fun makeSelectImageFromQuilt(event: GenericCommandInteractionEvent, user: User, title: String, quilt: BufferedImage, totalImages: Int, callback: (btnEvent: ButtonInteractionEvent, index: Int) -> Unit): ReplyCallbackAction {
val messages = (0 until totalImages).map { index ->
val getPageCallback: GetPageCallback = { index ->
val page = EmbedBuilder()
page.setTitle(title)
page.setFooter("Image ${index + 1} / $totalImages")
page.setImage("attachment://${index + 1}.jpg")
val imageSlice = takeSlice(quilt, totalImages, index)
val imageSlice = takeSlice(quilt, totalImages, index.toInt())
val avgColor = getAverageColor(imageSlice, 0, 0, imageSlice.width, imageSlice.height)
page.setColor(avgColor)
page.build()
}.toTypedArray()
val imageSelector = paginator(*messages, expireAfter = 10.minutes)
MessageCreate(embeds = listOf(page.build()))
}
val imageSelector = paginator(amountOfPages = totalImages.toLong(), getPage = getPageCallback, expireAfter = 10.minutes)
imageSelector.injectMessageCallback = { idx, msgEdit ->
val imageSlice = takeSlice(quilt, totalImages, idx)
val imageSlice = takeSlice(quilt, totalImages, idx.toInt())
msgEdit.setFiles(FileUpload.fromData(bufferedImageToByteArray(imageSlice, "jpg"), "${idx + 1}.jpg"))
}
imageSelector.customActionComponents = listOf(user.jda.button(
label = "Select",
style = ButtonStyle.PRIMARY,
user = user
) { btnEvent ->
callback(btnEvent, imageSelector.getIndex())
callback(btnEvent, imageSelector.getIndex().toInt())
})
val firstSlice = takeSlice(quilt, totalImages, 0)
return event.replyPaginator(imageSelector).setFiles(FileUpload.fromData(bufferedImageToByteArray(firstSlice, "jpg"), "1.jpg"))
}

fun sendPagination(
event: GenericCommandInteractionEvent,
title: String,
items: List<String>,
itemsPerPage: Int
): Paginator {
val chunks = items.chunked(itemsPerPage)
val pagesAmount = chunks.size
val messages = (0 until pagesAmount).map { pageNumber ->
val page = EmbedBuilder()
page.setTitle(title)
page.setFooter("Page ${pageNumber + 1} / $pagesAmount")
page.setDescription(chunks[pageNumber].joinToString("\n"))
page.setColor(0x33cc33)
page.build()
}.toTypedArray()
return paginator(*messages, expireAfter = 10.minutes)
}
Loading

0 comments on commit 0a9db16

Please sign in to comment.