rewrite: New Pomodoro Timer system.
This commit is contained in:
315
src/modules/pomodoro/ui/config.py
Normal file
315
src/modules/pomodoro/ui/config.py
Normal file
@@ -0,0 +1,315 @@
|
||||
from typing import Optional, TYPE_CHECKING
|
||||
import asyncio
|
||||
|
||||
import discord
|
||||
from discord.ui.button import button, Button, ButtonStyle
|
||||
from discord.ui.select import select, Select, RoleSelect, ChannelSelect
|
||||
|
||||
from meta import LionBot, conf
|
||||
from utils.lib import utc_now, MessageArgs, error_embed
|
||||
from utils.ui import MessageUI
|
||||
from babel.translator import ctx_locale
|
||||
|
||||
from .. import babel
|
||||
from ..lib import TimerRole
|
||||
from ..options import TimerOptions
|
||||
|
||||
from .edit import TimerEditor
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..timer import Timer
|
||||
|
||||
|
||||
_p = babel._p
|
||||
|
||||
|
||||
class TimerOptionsUI(MessageUI):
|
||||
"""
|
||||
View options for and reconfigure a single timer.
|
||||
"""
|
||||
|
||||
def __init__(self, bot: LionBot, timer: 'Timer', role: TimerRole, **kwargs):
|
||||
self.locale = timer.locale.value
|
||||
ctx_locale.set(self.locale)
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self.bot = bot
|
||||
self.timer = timer
|
||||
self.role = role
|
||||
|
||||
@button(label="EDIT_PLACEHOLDER", style=ButtonStyle.blurple)
|
||||
async def edit_button(self, press: discord.Interaction, pressed: Button):
|
||||
"""
|
||||
Open the edit modal to modify focus/break/threshold/name/format_string
|
||||
"""
|
||||
modal = await TimerEditor.open_editor(self.bot, press, self.timer, press.user, callback=self.refresh)
|
||||
await modal.wait()
|
||||
|
||||
async def refresh_edit_button(self):
|
||||
self.edit_button.label = self.bot.translator.t(_p(
|
||||
'ui:timer_options|button:edit|label',
|
||||
"Edit"
|
||||
))
|
||||
|
||||
@button(label="VOICE_ALERT_PLACEHOLDER", style=ButtonStyle.green)
|
||||
async def voice_button(self, press: discord.Interaction, pressed: Button):
|
||||
await press.response.defer(thinking=True, ephemeral=True)
|
||||
value = not self.timer.voice_alerts
|
||||
setting = self.timer.config.get('voice_alerts')
|
||||
setting.value = value
|
||||
await setting.write()
|
||||
await self.refresh(thinking=press)
|
||||
|
||||
async def refresh_voice_button(self):
|
||||
button = self.voice_button
|
||||
button.label = self.bot.translator.t(_p(
|
||||
'ui:timer_options|button:voice_alerts|label',
|
||||
"Voice Alerts"
|
||||
))
|
||||
if self.timer.voice_alerts:
|
||||
button.style = ButtonStyle.green
|
||||
else:
|
||||
button.style = ButtonStyle.grey
|
||||
|
||||
@button(label="DELETE_PLACEHOLDER", style=ButtonStyle.red)
|
||||
async def delete_button(self, press: discord.Interaction, pressed: Button):
|
||||
await press.response.defer(thinking=True)
|
||||
channelid = self.timer.data.channelid
|
||||
# Destroy through cog to maintain cache
|
||||
cog = self.bot.get_cog('TimerCog')
|
||||
await cog.destroy_timer(self.timer, reason="Manually destroyed through OptionUI")
|
||||
await self.quit()
|
||||
|
||||
t = self.bot.translator.t
|
||||
embed = discord.Embed(
|
||||
colour=discord.Colour.brand_red(),
|
||||
title=t(_p(
|
||||
'ui:timer_options|button:delete|success|title',
|
||||
"Timer Deleted"
|
||||
)),
|
||||
description=t(_p(
|
||||
'ui:timer_options|button:delete|success|description',
|
||||
"The timer in {channel} has been removed."
|
||||
)).format(channel=f"<#{channelid}>")
|
||||
)
|
||||
await press.edit_original_response(embed=embed)
|
||||
|
||||
async def refresh_delete_button(self):
|
||||
self.delete_button.label = self.bot.translator.t(_p(
|
||||
'ui:timer_options|button:delete|label',
|
||||
"Delete"
|
||||
))
|
||||
|
||||
@button(emoji=conf.emojis.cancel, style=ButtonStyle.red)
|
||||
async def close_button(self, press: discord.Interaction, pressed: Button):
|
||||
print("HERE")
|
||||
await press.response.defer()
|
||||
await self.quit()
|
||||
|
||||
async def refresh_close_button(self):
|
||||
pass
|
||||
|
||||
@select(cls=ChannelSelect, placeholder="VOICE_CHANNEL_PLACEHOLDER", channel_types=[discord.ChannelType.voice])
|
||||
async def voice_menu(self, selection: discord.Interaction, selected):
|
||||
...
|
||||
|
||||
async def refresh_voice_menu(self):
|
||||
self.voice_menu.placeholder = self.bot.translator.t(_p(
|
||||
'ui:timer_options|menu:voice_channel|placeholder',
|
||||
"Set Voice Channel"
|
||||
))
|
||||
|
||||
@select(cls=ChannelSelect,
|
||||
placeholder="NOTIFICATION_PLACEHOLDER",
|
||||
channel_types=[discord.ChannelType.text, discord.ChannelType.voice],
|
||||
min_values=0, max_values=1)
|
||||
async def notification_menu(self, selection: discord.Interaction, selected):
|
||||
await selection.response.defer(thinking=True, ephemeral=True)
|
||||
value = selected.values[0] if selected.values else None
|
||||
setting = self.timer.config.get('notification_channel')
|
||||
|
||||
if issue := await setting._check_value(self.timer.data.channelid, value):
|
||||
await selection.edit_original_response(embed=error_embed(issue))
|
||||
else:
|
||||
setting.value = value
|
||||
await setting.write()
|
||||
await self.timer.send_status()
|
||||
await self.refresh(thinking=selection)
|
||||
|
||||
async def refresh_notification_menu(self):
|
||||
self.notification_menu.placeholder = self.bot.translator.t(_p(
|
||||
'ui:timer_options|menu:notification_channel|placeholder',
|
||||
"Set Notification Channel"
|
||||
))
|
||||
|
||||
@select(cls=RoleSelect, placeholder="ROLE_PLACEHOLDER", min_values=0, max_values=1)
|
||||
async def manage_role_menu(self, selection: discord.Interaction, selected):
|
||||
await selection.response.defer(thinking=True, ephemeral=True)
|
||||
value = selected.values[0] if selected.values else None
|
||||
setting = self.timer.config.get('manager_role')
|
||||
setting.value = value
|
||||
await setting.write()
|
||||
await self.refresh(thinking=selection)
|
||||
|
||||
async def refresh_manage_role_menu(self):
|
||||
self.manage_role_menu.placeholder = self.bot.translator.t(_p(
|
||||
'ui:timer_options|menu:manager_role|placeholder',
|
||||
"Set Manager Role"
|
||||
))
|
||||
|
||||
# ----- UI FLow -----
|
||||
async def make_message(self) -> MessageArgs:
|
||||
t = self.bot.translator.t
|
||||
|
||||
title = t(_p(
|
||||
'ui:timer_options|embed|title',
|
||||
"Timer Control Panel for {channel}"
|
||||
)).format(channel=f"<#{self.timer.data.channelid}>")
|
||||
|
||||
table = await TimerOptions().make_setting_table(self.timer.data.channelid, timer=self.timer)
|
||||
|
||||
footer = t(_p(
|
||||
'ui:timer_options|embed|footer',
|
||||
"Hover over the option names to view descriptions."
|
||||
))
|
||||
|
||||
embed = discord.Embed(
|
||||
colour=discord.Colour.orange(),
|
||||
title=title,
|
||||
description=table
|
||||
)
|
||||
embed.set_footer(text=footer)
|
||||
|
||||
# Add pattern field
|
||||
embed.add_field(
|
||||
name=t(_p('ui:timer_options|embed|field:pattern|name', "Pattern")),
|
||||
value=t(_p(
|
||||
'ui:timer_options|embed|field:pattern|value',
|
||||
"**`{focus_len} minutes`** focus\n**`{break_len} minutes`** break"
|
||||
)).format(
|
||||
focus_len=self.timer.data.focus_length // 60,
|
||||
break_len=self.timer.data.break_length // 60
|
||||
)
|
||||
)
|
||||
|
||||
# Add channel name field
|
||||
embed.add_field(
|
||||
name=t(_p(
|
||||
'ui:timer_options|embed|field:channel_name|name',
|
||||
"Channel Name Preview"
|
||||
)),
|
||||
value=t(_p(
|
||||
'ui:timer_options|embed|field:channel_name|value',
|
||||
"**`{name}`**\n(The actual channel name may not match due to ratelimits.)"
|
||||
)).format(name=self.timer.channel_name)
|
||||
)
|
||||
|
||||
# Add issue field (if there are any permission issues).
|
||||
issues = await self._get_issues()
|
||||
if issues:
|
||||
embed.add_field(
|
||||
name=t(_p(
|
||||
'ui:timer_options|embed|field:issues|name',
|
||||
"Issues"
|
||||
)),
|
||||
value='\n'.join(f"{conf.emojis.warning} {issue}" for issue in issues),
|
||||
inline=False
|
||||
)
|
||||
return MessageArgs(embed=embed)
|
||||
|
||||
async def _get_issues(self):
|
||||
"""
|
||||
Report any issues with the timer setup, particularly with permissions.
|
||||
"""
|
||||
t = self.bot.translator.t
|
||||
|
||||
issues = []
|
||||
if self.timer.channel is None:
|
||||
issues.append(
|
||||
t(_p(
|
||||
'ui:timer_options|issue:no_voice_channel',
|
||||
"The configured voice channel does not exist! Please update it below."
|
||||
))
|
||||
)
|
||||
else:
|
||||
channel = self.timer.channel
|
||||
# Check join and speak permissions
|
||||
my_voice_permissions = channel.permissions_for(channel.guild.me)
|
||||
if self.timer.voice_alerts and not (my_voice_permissions.connect and my_voice_permissions.speak):
|
||||
issues.append(
|
||||
t(_p(
|
||||
'ui:timer_options|issue:cannot_speak',
|
||||
"Voice alerts are on, but I don't have speaking permissions in {channel}"
|
||||
)).format(channel=channel.mention)
|
||||
)
|
||||
if not my_voice_permissions.manage_channels:
|
||||
issues.append(
|
||||
t(_p(
|
||||
'ui:timer_options|issue:cannot_change_name',
|
||||
"I cannot update the name of {channel}! (Needs `MANAGE_CHANNELS` permission)"
|
||||
)).format(channel=channel.mention)
|
||||
)
|
||||
|
||||
notif_channelid = self.timer.data.notification_channelid
|
||||
if notif_channelid:
|
||||
channel = self.bot.get_channel(notif_channelid)
|
||||
if channel is None:
|
||||
issues.append(
|
||||
t(_p(
|
||||
'ui:timer_options|issue:notif_channel_dne',
|
||||
"Configured notification channel does not exist!"
|
||||
))
|
||||
)
|
||||
else:
|
||||
perms = channel.permissions_for(channel.guild.me)
|
||||
if not (perms.embed_links and perms.attach_files):
|
||||
issues.append(
|
||||
t(_p(
|
||||
'ui:timer_options|issue:notif_channel_write',
|
||||
"I cannot attach files (`ATTACH_FILES`) or send embeds (`EMBED_LINKS`) in {channel}"
|
||||
)).format(channel=channel.mention)
|
||||
)
|
||||
|
||||
if not (perms.manage_webhooks):
|
||||
issues.append(
|
||||
t(_p(
|
||||
'ui:timer_options|issues:cannot_make_webhooks',
|
||||
"I cannot create the notification webhook (`MANAGE_WEBHOOKS`) in {channel}"
|
||||
)).format(channel=channel.mention)
|
||||
)
|
||||
return issues
|
||||
|
||||
async def refresh_layout(self):
|
||||
if self.timer.owned:
|
||||
# Owned timers cannot change their voice channel, text channel, or manager role.
|
||||
await asyncio.gather(
|
||||
self.refresh_edit_button(),
|
||||
self.refresh_close_button(),
|
||||
self.refresh_voice_button(),
|
||||
self.refresh_delete_button(),
|
||||
)
|
||||
self.set_layout(
|
||||
(self.edit_button, self.voice_button, self.close_button)
|
||||
)
|
||||
else:
|
||||
await asyncio.gather(
|
||||
self.refresh_edit_button(),
|
||||
self.refresh_close_button(),
|
||||
self.refresh_voice_button(),
|
||||
self.refresh_delete_button(),
|
||||
# self.refresh_voice_menu(),
|
||||
self.refresh_manage_role_menu(),
|
||||
self.refresh_notification_menu()
|
||||
)
|
||||
self.set_layout(
|
||||
# (self.voice_menu,),
|
||||
(self.notification_menu,),
|
||||
(self.manage_role_menu,),
|
||||
(self.edit_button, self.voice_button, self.delete_button, self.close_button)
|
||||
)
|
||||
|
||||
async def reload(self):
|
||||
"""
|
||||
Nothing to reload.
|
||||
"""
|
||||
pass
|
||||
Reference in New Issue
Block a user