generated from HoloTech/holotech-plugin-template
(discord): Rough reward list and editor UI
This commit is contained in:
@@ -0,0 +1,254 @@
|
||||
"""
|
||||
Defines widgets for displaying a campaign's rewards.
|
||||
"""
|
||||
|
||||
from typing import Optional, TYPE_CHECKING
|
||||
import asyncio
|
||||
import datetime as dt
|
||||
|
||||
import discord
|
||||
from discord.ui import Modal
|
||||
from discord.ui.select import select, Select, SelectOption
|
||||
from discord.ui.button import button, Button, ButtonStyle
|
||||
from discord.ui.text_input import TextInput, TextStyle
|
||||
|
||||
from meta import LionBot
|
||||
from meta.errors import UserInputError
|
||||
from meta.config import conf
|
||||
from utils.lib import tabulate, utc_now, MessageArgs, parse_duration
|
||||
from utils.ui import MessageUI, AButton, AsComponents, ConfigEditor
|
||||
from utils.ui.micros import FastModal
|
||||
from utils.ui.pagers import BasePager, Pager
|
||||
|
||||
from ...campaign import RewardCampaign
|
||||
from ...data import EarnedReward
|
||||
|
||||
from .. import logger
|
||||
|
||||
|
||||
class RewardEditor(FastModal):
|
||||
# Title is the reward we are editing
|
||||
|
||||
# Block of text with dates and user info
|
||||
blurb = discord.ui.TextDisplay(content='placeholder')
|
||||
|
||||
# Fulfilled is a checkbox, and supports a fulfilled note
|
||||
flufbox = discord.ui.Label(
|
||||
text="Fulfilled",
|
||||
description="Whether this reward has been completed",
|
||||
component=discord.ui.Checkbox(
|
||||
default=False
|
||||
)
|
||||
)
|
||||
|
||||
# Reference and further notes need to be editable,
|
||||
# and need to set the default text correctly
|
||||
reference = discord.ui.Label(
|
||||
text="Reference",
|
||||
description="Reference URL or other information",
|
||||
component=discord.ui.TextInput(
|
||||
style=discord.TextStyle.long,
|
||||
),
|
||||
)
|
||||
notes = discord.ui.Label(
|
||||
text="Notes",
|
||||
description="Further notes for this user/reward",
|
||||
component=discord.ui.TextInput(
|
||||
style=discord.TextStyle.long,
|
||||
),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_reward(cls, reward: EarnedReward):
|
||||
title = f"Reward #{reward.earned_id} for {reward.twitch_user_name or reward.twitch_user_id}"
|
||||
self = cls(title=title)
|
||||
|
||||
# blurb
|
||||
table = {
|
||||
'Earned At': discord.utils.format_dt(reward.earned_at, 'F'),
|
||||
'Earned From': reward.earned_from,
|
||||
}
|
||||
prop_table = '\n'.join(tabulate(*table.items()))
|
||||
self.blurb.content = prop_table
|
||||
|
||||
|
||||
# Fulfilled
|
||||
self.flufbox.component.default = (reward.fulfilled_at is not None)
|
||||
|
||||
# Notes
|
||||
self.reference.component.default = reward.reference or ''
|
||||
self.notes.component.default = reward.modnote or ''
|
||||
|
||||
return self
|
||||
|
||||
|
||||
class RewardList(MessageUI):
|
||||
blocklen = 10
|
||||
|
||||
def __init__(self, bot: LionBot, campaign: RewardCampaign, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self.bot = bot
|
||||
self.campaign = campaign
|
||||
|
||||
# UI state
|
||||
self.pagen = 0
|
||||
self.pages = []
|
||||
self.reward_blocks = [[]]
|
||||
|
||||
self._rewards: list[EarnedReward] = []
|
||||
# self._reward_viewer: Optional[RewardViewer] = None
|
||||
|
||||
@property
|
||||
def page(self):
|
||||
self.pagen %= self.page_count
|
||||
return self.reward_blocks[self.pagen]
|
||||
|
||||
@property
|
||||
def page_count(self):
|
||||
return len(self.reward_blocks)
|
||||
|
||||
# ----- UI API -----
|
||||
|
||||
# ----- UI Components -----
|
||||
@select(
|
||||
cls=Select,
|
||||
placeholder="Select reward to edit",
|
||||
min_values=0,
|
||||
max_values=1,
|
||||
)
|
||||
async def reward_menu(self, selection: discord.Interaction, selected):
|
||||
"""
|
||||
Select reward to view and edit.
|
||||
|
||||
Maybe just edit for a start?
|
||||
"""
|
||||
if selected.values:
|
||||
# Hopefully this is a list of reminderids
|
||||
values = selected.values
|
||||
# Create detail widget, add to children
|
||||
# Spawn editor modal for this reward, callback to refresh detail widget
|
||||
# Also want to refresh this widget
|
||||
# Show detail widget as followup
|
||||
# TODO: Dedicated editor maybe, could e.g. embed refs. Maybe.
|
||||
value = int(selected.values[0])
|
||||
reward = next(r for r in self._rewards if r.earned_id == value)
|
||||
modal = RewardEditor.from_reward(reward)
|
||||
await selection.response.send_modal(modal)
|
||||
await self.refresh()
|
||||
else:
|
||||
await selection.response.defer()
|
||||
|
||||
async def reward_menu_refresh(self):
|
||||
menu = self.reward_menu
|
||||
rewards = self.page
|
||||
if rewards:
|
||||
menu.options = [
|
||||
self._format_reward_option(r) for r in rewards
|
||||
]
|
||||
menu.disabled = False
|
||||
else:
|
||||
menu.options = [
|
||||
SelectOption(label='DUMMY')
|
||||
]
|
||||
menu.disabled = True
|
||||
|
||||
# Meta buttons
|
||||
@button(emoji=conf.emojis.getemoji("forward"))
|
||||
async def next_page_button(self, press: discord.Interaction, pressed):
|
||||
await press.response.defer()
|
||||
self.pagen += 1
|
||||
await self.refresh()
|
||||
|
||||
@button(emoji=conf.emojis.getemoji("backward"))
|
||||
async def prev_page_button(self, press: discord.Interaction, pressed):
|
||||
await press.response.defer()
|
||||
self.pagen -= 1
|
||||
await self.refresh()
|
||||
|
||||
@button(emoji=conf.emojis.cancel, style=ButtonStyle.red)
|
||||
async def quit_button(self, press: discord.Interaction, pressed: Button):
|
||||
"""Close the UI and all children."""
|
||||
await press.response.defer(thinking=False)
|
||||
await self.quit()
|
||||
|
||||
@button(emoji=conf.emojis.refresh)
|
||||
async def refresh_button(self, press: discord.Interaction, pressed: Button):
|
||||
await press.response.defer()
|
||||
await self.refresh()
|
||||
|
||||
# ----- UI Flow -----
|
||||
def _format_reward_section(self, reward: EarnedReward) -> tuple[str, str]:
|
||||
"""
|
||||
Format the earned reward as an embed section.
|
||||
"""
|
||||
# Title is Reward #n earned by twitch_username
|
||||
# Reward, Earned at, Earned by, Fulfilled at (not fulfilled/date), Ref, Added Notes
|
||||
name = f"Reward #{reward.earned_id} earned by {reward.twitch_user_name or reward.twitch_user_id}"
|
||||
|
||||
if reward.fulfilled_at is not None:
|
||||
fat = discord.utils.format_dt(reward.fulfilled_at, 'F')
|
||||
if reward.fulfilled_note is not None:
|
||||
fluf = f"{fat} ({reward.fulfilled_note})"
|
||||
else:
|
||||
fluf = fat
|
||||
else:
|
||||
fluf = "*Not Fulfilled*"
|
||||
|
||||
table = {
|
||||
'Reward': "Plus Campaign Sketch",
|
||||
'Earned At': discord.utils.format_dt(reward.earned_at, 'F'),
|
||||
'Earned From': reward.earned_from,
|
||||
'Fulfilled At': fluf,
|
||||
'Reference': "*No reference set*",
|
||||
'Further notes': reward.modnote or "*No notes*",
|
||||
}
|
||||
prop_table = '\n'.join(tabulate(*table.items()))
|
||||
|
||||
return (name, prop_table)
|
||||
|
||||
def _format_reward_option(self, reward: EarnedReward) -> SelectOption:
|
||||
"""
|
||||
Format the earned reward as a selectable option
|
||||
"""
|
||||
# Reward #n earned by twitch_username
|
||||
# Value is the reward id
|
||||
name = f"Reward #{reward.earned_id} earned by {reward.twitch_user_name or reward.twitch_user_id}"
|
||||
value = reward.earned_id
|
||||
|
||||
return SelectOption(
|
||||
label=name, value=value
|
||||
)
|
||||
|
||||
async def refresh_layout(self):
|
||||
to_refresh = (
|
||||
self.reward_menu_refresh(),
|
||||
)
|
||||
await asyncio.gather(*to_refresh)
|
||||
|
||||
if self.page_count > 1:
|
||||
self.prev_page_button.disabled = True
|
||||
self.next_page_button.disabled = True
|
||||
else:
|
||||
self.prev_page_button.disabled = False
|
||||
self.next_page_button.disabled = False
|
||||
|
||||
self.set_layout(
|
||||
(self.prev_page_button, self.refresh_button, self.next_page_button, self.quit_button),
|
||||
(self.reward_menu,),
|
||||
)
|
||||
|
||||
async def make_message(self) -> MessageArgs:
|
||||
embed = discord.Embed(title=f"{self.campaign.row.campaign_name} Reward List")
|
||||
# embed.set_footer with last update
|
||||
|
||||
return MessageArgs(embed=embed)
|
||||
|
||||
async def reload(self):
|
||||
rewards = self._rewards = await self.campaign.get_rewards()
|
||||
|
||||
# TODO: Consider filter by unfulfilled or reward type
|
||||
self.reward_blocks = [
|
||||
rewards[i:i+self.blocklen]
|
||||
for i in range(0, len(rewards), self.blocklen)
|
||||
] or [[]]
|
||||
Reference in New Issue
Block a user