Skip to content

Commit 63ebc1f

Browse files
sebkuiplorenzo132
authored andcommitted
Added submenu functionality to threadmenu. Fixes modmail-dev#3403 (modmail-dev#3404)
* Threadmenu now supports submenus * Fix a small issue with path not resetting after main menu. * Fix copilot suggestions * Black formatting * Fix undeclared vars * threadmenu: submenu navigation fixes Signed-off-by: lorenzo132 <50767078+lorenzo132@users.noreply.github.com> * threadmenu: submenu navigation fixes Refactor thread creation menu handling to improve path management and submenu navigation. Signed-off-by: lorenzo132 <50767078+lorenzo132@users.noreply.github.com> * Fix formatting according to black --------- Signed-off-by: lorenzo132 <50767078+lorenzo132@users.noreply.github.com> Co-authored-by: lorenzo132 <50767078+lorenzo132@users.noreply.github.com>
1 parent 59a2df0 commit 63ebc1f

2 files changed

Lines changed: 94 additions & 17 deletions

File tree

cogs/threadmenu.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,9 @@ def typecheck(m):
178178
if label.lower() == "cancel":
179179
return await ctx.send("Cancelled.")
180180

181+
if label.lower() == "main menu":
182+
return await ctx.send("You cannot use that label.")
183+
181184
if sanitized_label in conf["options"]:
182185
await ctx.send("That option already exists. Use `threadmenu edit` to edit it.")
183186
return

core/thread.py

Lines changed: 91 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -865,11 +865,9 @@ async def send_genesis_message():
865865
if getattr(self, "_selected_thread_creation_menu_option", None) and self.bot.config.get(
866866
"thread_creation_menu_selection_log"
867867
):
868-
opt = self._selected_thread_creation_menu_option
868+
path = self._selected_thread_creation_menu_option
869869
try:
870-
log_txt = f"Selected menu option: {opt.get('label')} ({opt.get('type')})"
871-
if opt.get("type") == "command":
872-
log_txt += f" -> {opt.get('callback')}"
870+
log_txt = f"Selected menu path: {' -> '.join(path)}"
873871
await channel.send(embed=discord.Embed(description=log_txt, color=self.bot.mod_color))
874872
except Exception:
875873
logger.warning(
@@ -2668,29 +2666,44 @@ async def create(
26682666
placeholder = "Select an option to contact the staff team."
26692667
timeout = 20
26702668

2671-
options = self.bot.config.get("thread_creation_menu_options") or {}
2672-
submenus = self.bot.config.get("thread_creation_menu_submenus") or {}
2673-
26742669
# Minimal inline view implementation (avoid importing plugin code)
26752670

26762671
thread.ready = False # not ready yet
26772672

26782673
class _ThreadCreationMenuSelect(discord.ui.Select):
2679-
def __init__(self, outer_thread: Thread):
2674+
def __init__(
2675+
self,
2676+
bot,
2677+
outer_thread: Thread,
2678+
option_data: dict,
2679+
menu_msg: discord.Message,
2680+
path: list,
2681+
is_home: bool = True,
2682+
):
2683+
self.bot = bot
26802684
self.outer_thread = outer_thread
2681-
opts = [
2685+
self.option_data = option_data
2686+
self.menu_msg = menu_msg
2687+
self.path = path
2688+
options = [
26822689
discord.SelectOption(
26832690
label=o["label"],
26842691
description=o["description"],
26852692
emoji=o["emoji"],
26862693
)
2687-
for o in options.values()
2694+
for o in option_data.values()
26882695
]
2696+
if not is_home:
2697+
options.append(
2698+
discord.SelectOption(
2699+
label="main menu", description="Return to the main menu", emoji="🏠"
2700+
)
2701+
)
26892702
super().__init__(
26902703
placeholder=placeholder,
26912704
min_values=1,
26922705
max_values=1,
2693-
options=opts,
2706+
options=options,
26942707
)
26952708

26962709
async def callback(self, interaction: discord.Interaction):
@@ -2705,8 +2718,45 @@ async def callback(self, interaction: discord.Interaction):
27052718
chosen_label = self.values[0]
27062719
# Resolve option key
27072720
key = chosen_label.lower().replace(" ", "_")
2708-
selected = options.get(key)
2709-
self.outer_thread._selected_thread_creation_menu_option = selected
2721+
if key == "main_menu":
2722+
option_data = self.bot.config.get("thread_creation_menu_options") or {}
2723+
new_view = _ThreadCreationMenuView(
2724+
self.bot,
2725+
self.outer_thread,
2726+
option_data,
2727+
self.menu_msg,
2728+
path=[],
2729+
is_home=True,
2730+
)
2731+
return await self.menu_msg.edit(view=new_view)
2732+
selected: dict = self.option_data.get(key, {})
2733+
next_path = [*self.path, chosen_label]
2734+
if selected.get("type", "command") == "submenu":
2735+
submenu_data = self.bot.config.get("thread_creation_menu_submenus") or {}
2736+
submenu_key = selected.get("callback", key)
2737+
option_data = submenu_data.get(submenu_key, {})
2738+
if not option_data:
2739+
home_options = self.bot.config.get("thread_creation_menu_options") or {}
2740+
new_view = _ThreadCreationMenuView(
2741+
self.bot,
2742+
self.outer_thread,
2743+
home_options,
2744+
self.menu_msg,
2745+
path=[],
2746+
is_home=True,
2747+
)
2748+
return await self.menu_msg.edit(view=new_view)
2749+
new_view = _ThreadCreationMenuView(
2750+
self.bot,
2751+
self.outer_thread,
2752+
option_data,
2753+
self.menu_msg,
2754+
path=next_path,
2755+
is_home=False,
2756+
)
2757+
return await self.menu_msg.edit(view=new_view)
2758+
2759+
self.outer_thread._selected_thread_creation_menu_option = next_path
27102760
# Reflect the selection in the original DM by editing the embed/body
27112761
try:
27122762
msg = getattr(interaction, "message", None)
@@ -2945,10 +2995,30 @@ async def callback(self, interaction: discord.Interaction):
29452995
ctx_.command.checks = old_checks
29462996

29472997
class _ThreadCreationMenuView(discord.ui.View):
2948-
def __init__(self, outer_thread: Thread):
2998+
def __init__(
2999+
self,
3000+
bot,
3001+
outer_thread: Thread,
3002+
option_data: dict,
3003+
menu_msg: discord.Message,
3004+
path: list,
3005+
is_home: bool = True,
3006+
):
29493007
super().__init__(timeout=timeout)
29503008
self.outer_thread = outer_thread
2951-
self.add_item(_ThreadCreationMenuSelect(outer_thread))
3009+
self.path = path
3010+
self.menu_msg = menu_msg
3011+
self.option_data = option_data
3012+
self.add_item(
3013+
_ThreadCreationMenuSelect(
3014+
bot,
3015+
outer_thread,
3016+
option_data=option_data,
3017+
menu_msg=menu_msg,
3018+
path=self.path,
3019+
is_home=is_home,
3020+
)
3021+
)
29523022

29533023
async def on_timeout(self):
29543024
# Timeout -> abort thread creation
@@ -3070,8 +3140,12 @@ async def on_timeout(self):
30703140
embed.set_thumbnail(url=embed_thumb)
30713141
except Exception as e:
30723142
logger.debug("Thumbnail set failed (ignored): %s", e)
3073-
menu_view = _ThreadCreationMenuView(thread)
3074-
menu_msg = await recipient.send(embed=embed, view=menu_view)
3143+
menu_msg = await recipient.send(embed=embed)
3144+
option_data = self.bot.config.get("thread_creation_menu_options") or {}
3145+
menu_view = _ThreadCreationMenuView(
3146+
self.bot, thread, option_data, menu_msg, path=[], is_home=True
3147+
)
3148+
menu_msg = await menu_msg.edit(view=menu_view)
30753149
# mark thread as pending menu selection
30763150
thread._pending_menu = True
30773151
# Explicitly attach the message to the view for safety in callbacks

0 commit comments

Comments
 (0)