272 lines
8.6 KiB
Python
272 lines
8.6 KiB
Python
import asyncio
|
|
|
|
import discord
|
|
from discord.ui.select import select, Select
|
|
from discord.components import SelectOption
|
|
from discord.enums import ButtonStyle, TextStyle
|
|
from discord.ui.button import button, Button
|
|
from discord.ui.text_input import TextInput
|
|
|
|
from meta import LionBot, conf
|
|
from meta.errors import SafeCancellation
|
|
|
|
from utils.ui import MessageUI, input
|
|
from utils.lib import MessageArgs
|
|
|
|
from .. import logger
|
|
from ...koans import KoanRegistry
|
|
from ...lib import minify
|
|
|
|
class KoanListUI(MessageUI):
|
|
block_len = 10
|
|
def __init__(self, bot: LionBot, koans: KoanRegistry, communityid: int, profileid: int, callerid: int, **kwargs):
|
|
super().__init__(callerid=callerid, **kwargs)
|
|
|
|
self.bot = bot
|
|
self.koans = koans
|
|
self.communityid = communityid
|
|
self.caller_profileid = profileid
|
|
|
|
# Paging state
|
|
self._pagen = 0
|
|
self.blocks = [[]]
|
|
|
|
@property
|
|
def page_count(self):
|
|
return len(self.blocks)
|
|
|
|
@property
|
|
def pagen(self):
|
|
self._pagen = self._pagen % self.page_count
|
|
return self._pagen
|
|
|
|
@pagen.setter
|
|
def pagen(self, value):
|
|
self._pagen = value % self.page_count
|
|
|
|
@property
|
|
def current_page(self):
|
|
return self.blocks[self.pagen]
|
|
|
|
# ----- API -----
|
|
|
|
# ----- UI Components -----
|
|
@button(
|
|
label="New Koan",
|
|
style=ButtonStyle.green
|
|
)
|
|
async def new_koan_button(self, press: discord.Interaction, pressed: Button):
|
|
# Show the create koan modal
|
|
# Create koan flow.
|
|
try:
|
|
interaction, to_create = await input(
|
|
interaction=press,
|
|
title="Create Koan",
|
|
question="Koan Content",
|
|
timeout=300,
|
|
style=TextStyle.long,
|
|
min_length=1,
|
|
max_length=2000 - 8
|
|
)
|
|
except asyncio.TimeoutError:
|
|
raise SafeCancellation
|
|
await interaction.response.defer(thinking=True, ephemeral=True)
|
|
|
|
koan = await self.koans.create_koan(
|
|
communityid=self.communityid,
|
|
content=to_create,
|
|
created_by=self.caller_profileid
|
|
)
|
|
self.pagen = -1
|
|
await self.refresh(thinking=interaction)
|
|
|
|
# Koan edit and delete selectors
|
|
async def make_page_options(self):
|
|
options = []
|
|
for koan in self.current_page:
|
|
labelstr = f"#{koan.koanlabel}: "
|
|
minified = minify(koan.content, 100 - len(labelstr) - 1, strip=' >')
|
|
option = SelectOption(
|
|
label=f"{labelstr} {minified}",
|
|
value=str(koan.koanid)
|
|
)
|
|
options.append(option)
|
|
return options
|
|
|
|
@select(
|
|
cls=Select,
|
|
placeholder="Select to delete",
|
|
min_values=1, max_values=1
|
|
)
|
|
async def delete_menu(self, selection: discord.Interaction, selected: Select):
|
|
await selection.response.defer(thinking=True, ephemeral=True)
|
|
if selected.values:
|
|
koanid = int(selected.values[0])
|
|
await self.koans.delete_koan(koanid)
|
|
await self.refresh(thinking=selection)
|
|
|
|
async def refresh_delete_menu(self):
|
|
self.delete_menu.options = await self.make_page_options()
|
|
|
|
@select(
|
|
cls=Select,
|
|
placeholder="Select to edit",
|
|
min_values=1, max_values=1
|
|
)
|
|
async def edit_menu(self, selection: discord.Interaction, selected: Select):
|
|
if selected.values:
|
|
koanid = int(selected.values[0])
|
|
koan = await self.koans.get_koan(koanid)
|
|
assert koan is not None
|
|
try:
|
|
interaction, new_content = await input(
|
|
interaction=selection,
|
|
title="Edit Koan",
|
|
question="Koan Content",
|
|
timeout=300,
|
|
style=TextStyle.long,
|
|
default=koan.content,
|
|
min_length=1,
|
|
max_length=2000 - 8
|
|
)
|
|
await interaction.response.defer(thinking=True, ephemeral=True)
|
|
except asyncio.TimeoutError:
|
|
raise SafeCancellation
|
|
|
|
await koan.update(content=new_content)
|
|
await self.refresh(thinking=interaction)
|
|
|
|
async def refresh_edit_menu(self):
|
|
self.edit_menu.options = await self.make_page_options()
|
|
|
|
# Backwards
|
|
@button(emoji=conf.emojis.backward, style=ButtonStyle.grey)
|
|
async def prev_button(self, press: discord.Interaction, pressed: Button):
|
|
await press.response.defer(thinking=True, ephemeral=True)
|
|
self.pagen -= 1
|
|
await self.refresh(thinking=press)
|
|
|
|
# Jump to page
|
|
@button(label="JUMP_PLACEHOLDER", style=ButtonStyle.blurple)
|
|
async def jump_button(self, press: discord.Interaction, pressed: Button):
|
|
"""
|
|
Jump-to-page button.
|
|
Loads a page-switch dialogue.
|
|
"""
|
|
t = self.bot.translator.t
|
|
try:
|
|
interaction, value = await input(
|
|
press,
|
|
title="Jump to page",
|
|
question="Page number to jump to"
|
|
)
|
|
value = value.strip()
|
|
except asyncio.TimeoutError:
|
|
return
|
|
|
|
if not value.lstrip('- ').isdigit():
|
|
error_embed = discord.Embed(
|
|
title="Invalid page number, please try again!",
|
|
colour=discord.Colour.brand_red()
|
|
)
|
|
await interaction.response.send_message(embed=error_embed, ephemeral=True)
|
|
else:
|
|
await interaction.response.defer(thinking=True)
|
|
pagen = int(value.lstrip('- '))
|
|
if value.startswith('-'):
|
|
pagen = -1 * pagen
|
|
elif pagen > 0:
|
|
pagen = pagen - 1
|
|
self.pagen = pagen
|
|
await self.refresh(thinking=interaction)
|
|
|
|
async def jump_button_refresh(self):
|
|
component = self.jump_button
|
|
component.label = f"{self.pagen + 1}/{self.page_count}"
|
|
component.disabled = (self.page_count <= 1)
|
|
|
|
# Forward
|
|
@button(emoji=conf.emojis.forward, style=ButtonStyle.grey)
|
|
async def next_button(self, press: discord.Interaction, pressed: Button):
|
|
await press.response.defer(thinking=True)
|
|
self.pagen += 1
|
|
await self.refresh(thinking=press)
|
|
|
|
# Quit
|
|
@button(emoji=conf.emojis.cancel, style=ButtonStyle.red)
|
|
async def quit_button(self, press: discord.Interaction, pressed: Button):
|
|
"""
|
|
Quit the UI.
|
|
"""
|
|
await press.response.defer()
|
|
await self.quit()
|
|
|
|
# ----- UI Flow -----
|
|
async def make_message(self) -> MessageArgs:
|
|
if self.current_page:
|
|
lines = []
|
|
for koan in self.current_page:
|
|
labelstr = f"**#{koan.koanlabel}:** "
|
|
minified = minify(koan.content, 100 - len(labelstr) - 1, strip=' >')
|
|
lines.append(f"{labelstr} {minified}")
|
|
embed = discord.Embed(
|
|
title="Community Koans",
|
|
description='\n'.join(lines)
|
|
)
|
|
else:
|
|
embed = discord.Embed(
|
|
title="Community Koans",
|
|
description="No koans have been created! Use `/koans add` or the 'New Koan' button below to make a koan!"
|
|
)
|
|
return MessageArgs(embed=embed)
|
|
|
|
async def refresh_layout(self):
|
|
# Refresh menus
|
|
to_refresh = (
|
|
self.refresh_edit_menu(),
|
|
self.refresh_delete_menu(),
|
|
self.jump_button_refresh(),
|
|
)
|
|
await asyncio.gather(*to_refresh)
|
|
|
|
if self.page_count > 1:
|
|
# Multiple page case
|
|
# < Add Jump Close >
|
|
# edit
|
|
# delete
|
|
self.set_layout(
|
|
(self.prev_button,
|
|
self.new_koan_button,
|
|
self.jump_button,
|
|
self.quit_button,
|
|
self.next_button),
|
|
(self.edit_menu,),
|
|
(self.delete_menu,),
|
|
)
|
|
elif self.current_page:
|
|
# Single page case
|
|
# Add Close
|
|
# Edit
|
|
# Delete
|
|
self.set_layout(
|
|
(self.new_koan_button,
|
|
self.quit_button),
|
|
(self.edit_menu,),
|
|
(self.delete_menu,),
|
|
)
|
|
else:
|
|
# Add Close
|
|
self.set_layout(
|
|
(self.new_koan_button,
|
|
self.quit_button),
|
|
)
|
|
|
|
async def reload(self):
|
|
koans = await self.koans.get_community_koans(self.communityid)
|
|
blocks = [
|
|
koans[i:i+self.block_len]
|
|
for i in range(0, len(koans), self.block_len)
|
|
]
|
|
self.blocks = blocks or [[]]
|
|
|