From f42295345749d9868c2a676f034c2d70ecbed2b7 Mon Sep 17 00:00:00 2001 From: coinmoles Date: Thu, 21 Dec 2023 21:26:14 +0900 Subject: [PATCH] Changed patch mission to modal --- .../missionGive/patchMission/builders.ts | 18 +-- .../missionGive/patchMission/index.ts | 5 +- .../missionGive/patchMission/operations.ts | 142 +++++++++--------- .../missionGive/patchMission/reply.ts | 13 +- .../missionGive/postMission/operations.ts | 13 +- 5 files changed, 85 insertions(+), 106 deletions(-) diff --git a/src/commands/missionGive/patchMission/builders.ts b/src/commands/missionGive/patchMission/builders.ts index bf52c3e..be02e38 100644 --- a/src/commands/missionGive/patchMission/builders.ts +++ b/src/commands/missionGive/patchMission/builders.ts @@ -1,6 +1,8 @@ -import { ActionRowBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder, MessageActionRowComponentBuilder } from "discord.js" +import { EmbedBuilder } from "discord.js" import { cancelColor, errorColor, normalColor, successColor } from "../../utils/colors.js" +const commandId = "patch-mission" + const missionNotFoundEmbed = new EmbedBuilder().setTitle("승격조건을 찾을 수 없습니다.").setColor(errorColor) const noChangeEmbed = new EmbedBuilder().setTitle("수정 사항이 없습니다").setColor(errorColor) @@ -8,21 +10,11 @@ const replyEmbedPrototype = new EmbedBuilder().setTitle("승격조건을 수정 const successEmbedPrototype = new EmbedBuilder().setTitle("승격조건을 수정했습니다").setColor(successColor) const cancelEmbedPrototype = new EmbedBuilder().setTitle("승격조건 수정을 취소했습니다").setColor(cancelColor) -const confirmButtonId = "patch-mission-confirm" -const confirmButton = new ButtonBuilder().setCustomId(confirmButtonId).setLabel("확인").setStyle(ButtonStyle.Success) -const cancelButtonId = "patch-mission-cancel" -const cancelButton = new ButtonBuilder().setCustomId(cancelButtonId).setLabel("취소").setStyle(ButtonStyle.Danger) -const actionRow = new ActionRowBuilder().addComponents(confirmButton, cancelButton) - export default { + commandId, missionNotFoundEmbed, noChangeEmbed, replyEmbedPrototype, successEmbedPrototype, - cancelEmbedPrototype, - confirmButtonId, - confirmButton, - cancelButtonId, - cancelButton, - actionRow + cancelEmbedPrototype } \ No newline at end of file diff --git a/src/commands/missionGive/patchMission/index.ts b/src/commands/missionGive/patchMission/index.ts index 26feb78..7efea74 100644 --- a/src/commands/missionGive/patchMission/index.ts +++ b/src/commands/missionGive/patchMission/index.ts @@ -11,10 +11,7 @@ const patchMission: SlashCommandSubcommandContainer = { .setDescription(description) .addUserOption(option => option.setName("대상").setDescription("수정할 승격조건 대상, 공통 조건을 수정하는 경우 자기 자신을 입력").setRequired(true)) .addStringOption(option => option.setName("카테고리").setDescription("수정할 승격조건 카테고리, 카테고리가 없는 경우 \"공통 조건\" 또는 \"개인 조건\" 입력").setRequired(true)) - .addNumberOption(option => option.setName("번호").setDescription("수정할 승격조건 번호 (1-base)").setRequired(true)) - .addStringOption(option => option.setName("내용").setDescription("수정 후 승격조건 내용")) - .addStringOption(option => option.setName("비고").setDescription("수정 후 승격조건 비고")) - .addNumberOption(option => option.setName("점수").setDescription("수정 후 승격조건 점수")), + .addNumberOption(option => option.setName("번호").setDescription("수정할 승격조건 번호 (1-base)").setRequired(true)), callback: reply } diff --git a/src/commands/missionGive/patchMission/operations.ts b/src/commands/missionGive/patchMission/operations.ts index 5bc7c4a..2767d54 100644 --- a/src/commands/missionGive/patchMission/operations.ts +++ b/src/commands/missionGive/patchMission/operations.ts @@ -1,4 +1,4 @@ -import { ButtonInteraction, ChatInputCommandInteraction, ComponentType, EmbedBuilder, InteractionResponse, Message, User } from "discord.js" +import { ButtonInteraction, ChatInputCommandInteraction, ComponentType, EmbedBuilder, InteractionResponse, Message, ModalSubmitInteraction, User } from "discord.js" import { getMission, patchMission } from "../../../db/actions/missionActions.js" import { Mission, MissionUpdateData } from "../../../interfaces/models/Mission.js" import { checkAssociate } from "../../utils/checks/checkAssociate.js" @@ -6,115 +6,111 @@ import { createMissionEditPreviewString, createMissionPreviewTitle } from "../.. import { errorEmbed } from "../../utils/errorEmbeds.js" import { getQuarterDataFooter } from "../../utils/quarterData/getQuarterData.js" import builders from "./builders.js" +import { createMissionModal, createMissionModalId, getMissionModalMission } from "../../utils/components/missionModal.js" +import { addConfirmCollector, createConfirmActionRow } from "../../utils/components/confirmActionRow.js" const { + commandId, missionNotFoundEmbed, noChangeEmbed, replyEmbedPrototype, successEmbedPrototype, - cancelEmbedPrototype, - confirmButtonId, - cancelButtonId, - actionRow + cancelEmbedPrototype } = builders -const readOptions = (interaction: ChatInputCommandInteraction) => ({ +export const readOptions = (interaction: ChatInputCommandInteraction) => ({ target: interaction.options.getUser("대상", true), category: interaction.options.getString("카테고리", true), index: interaction.options.getNumber("번호", true) - 1, - content: interaction.options.getString("내용"), - note: interaction.options.getString("비고"), - score: interaction.options.getNumber("점수") }) -const doConfirm = async ( - interaction: ChatInputCommandInteraction, buttonInteraction: ButtonInteraction, - target: User, category: string, index: number, missionUpdateData: MissionUpdateData, +const onConfirm = ( + interaction: ModalSubmitInteraction, + target: User, category: string, index: number, missionNew: Mission, missionLast: Mission -) => { - await buttonInteraction.deferReply({ephemeral: true}) +) => + async (buttonInteraction: ButtonInteraction) => { + await buttonInteraction.deferReply({ ephemeral: true }) - const success = await patchMission(interaction.user.id, target.id, category, index, missionUpdateData) + const success = await patchMission(interaction.user.id, target.id, category, index, missionNew) - if (success) { - const successEmbed = new EmbedBuilder(successEmbedPrototype.toJSON()) - .addFields({ name: createMissionPreviewTitle(missionNew, target), value: createMissionEditPreviewString(missionNew, missionLast, target) }) + if (success) { + const successEmbed = new EmbedBuilder(successEmbedPrototype.toJSON()) + .addFields({ name: createMissionPreviewTitle(missionNew, target), value: createMissionEditPreviewString(missionNew, missionLast, target) }) - await buttonInteraction.deleteReply() - await interaction.editReply({ embeds: [successEmbed.setFooter(await getQuarterDataFooter())], components: [] }) + await buttonInteraction.deleteReply() + await interaction.editReply({ embeds: [successEmbed.setFooter(await getQuarterDataFooter())], components: [] }) + } + else + await buttonInteraction.editReply({ embeds: [errorEmbed] }) } - else - await buttonInteraction.editReply({ embeds: [errorEmbed] }) -} -const doCancel = async ( - interaction: ChatInputCommandInteraction, buttonInteraction: ButtonInteraction, - target: User, category: string, index: number, missionUpdateData: MissionUpdateData, +const onCancel = ( + interaction: ModalSubmitInteraction, + target: User, category: string, index: number, missionNew: Mission, missionLast: Mission -) => { - await buttonInteraction.deferUpdate() - - const cancelEmbed = new EmbedBuilder(cancelEmbedPrototype.toJSON()) - .addFields({ name: createMissionPreviewTitle(missionNew, target), value: createMissionEditPreviewString(missionNew, missionLast, target) }) +) => + async (buttonInteraction: ButtonInteraction) => { + await buttonInteraction.deferUpdate() - await interaction.editReply({ embeds: [cancelEmbed.setFooter(await getQuarterDataFooter())], components: [] }) -} + const cancelEmbed = new EmbedBuilder(cancelEmbedPrototype.toJSON()) + .addFields({ name: createMissionPreviewTitle(missionNew, target), value: createMissionEditPreviewString(missionNew, missionLast, target) }) -const addCollector = ( - interaction: ChatInputCommandInteraction, reply: Message | InteractionResponse, - target: User, category: string, index: number, missionUpdateData: MissionUpdateData, - missionNew: Mission, missionLast: Mission -) => { - const collector = reply.createMessageComponentCollector({ - filter: i => i.user === interaction.user, - componentType: ComponentType.Button - }) - - collector.on("collect", async buttonInteraction => { - if (buttonInteraction.customId === confirmButtonId) - doConfirm(interaction, buttonInteraction, target, category, index, missionUpdateData, missionNew, missionLast) - else if (buttonInteraction.customId === cancelButtonId) - doCancel(interaction, buttonInteraction, target, category, index, missionUpdateData, missionNew, missionLast) - }) -} - -const doReply = async (interaction: ChatInputCommandInteraction, target: User, category: string, index: number, missionUpdateData: MissionUpdateData, isEditing: boolean = false) => { - if (!isEditing) - await interaction.deferReply({ephemeral: true}) + await interaction.editReply({ embeds: [cancelEmbed.setFooter(await getQuarterDataFooter())], components: [] }) + } +export const doReply = async (interaction: ChatInputCommandInteraction, target: User, category: string, index: number, isEditing: boolean = false) => { if (!await checkAssociate(interaction, target.id, true)) return - if (missionUpdateData.content === undefined && missionUpdateData.note === undefined && missionUpdateData.score === undefined) { - await interaction.editReply({ embeds: [noChangeEmbed] }) - return - } - const missionLast = await getMission(interaction.user.id, target.id, category, index) if (missionLast === null) { - await interaction.editReply({ embeds: [missionNotFoundEmbed] }) + await interaction.reply({ embeds: [missionNotFoundEmbed] }) return } if (missionLast === undefined) { - await interaction.editReply({ embeds: [errorEmbed] }) + await interaction.reply({ embeds: [errorEmbed] }) return } - const missionNew: Mission = { - ...missionLast, - ...missionUpdateData + await interaction.showModal(createMissionModal( + commandId, interaction.user, target, + missionLast.category, missionLast.content, missionLast.score, missionLast.note + )) + + const modalInteraction = await interaction.awaitModalSubmit({ + filter: mI => mI.customId === createMissionModalId(commandId, interaction.user), + time: 60_000 + }).catch(_ => null) + if (modalInteraction === null) + return + + const missionNew = getMissionModalMission(modalInteraction, target, missionLast.completed) + if (missionNew === undefined) { + await modalInteraction.reply("점수가 숫자가 아닙니다!") + return + } + else if (missionNew.content === missionLast.content && missionNew.note === missionLast.note && missionNew.score === missionLast.score) { + await modalInteraction.reply({ embeds: [noChangeEmbed] }) + return } - const replyEmbed = new EmbedBuilder(replyEmbedPrototype.toJSON()) - .addFields({ name: createMissionPreviewTitle(missionNew, target), value: createMissionEditPreviewString(missionNew, missionLast, target) }) - const reply = await interaction.editReply({ embeds: [replyEmbed.setFooter(await getQuarterDataFooter())], components: [actionRow] }); + const reply = await modalInteraction.reply({ + embeds: [ + EmbedBuilder.from(replyEmbedPrototype) + .addFields({ name: createMissionPreviewTitle(missionNew, target), value: createMissionEditPreviewString(missionNew, missionLast, target) }) + .setFooter(await getQuarterDataFooter()) + ], + components: [createConfirmActionRow(commandId, modalInteraction.user)], + ephemeral: true, + fetchReply: true + }); if (!isEditing) - addCollector(interaction, reply, target, category, index, missionUpdateData, missionNew, missionLast) -} - -export default { - readOptions, - doReply + await addConfirmCollector( + commandId, modalInteraction, reply, + onConfirm(modalInteraction, target, category, index, missionNew, missionLast), + onCancel(modalInteraction, target, category, index, missionNew, missionLast) + ) } \ No newline at end of file diff --git a/src/commands/missionGive/patchMission/reply.ts b/src/commands/missionGive/patchMission/reply.ts index f2b11d4..dc6d918 100644 --- a/src/commands/missionGive/patchMission/reply.ts +++ b/src/commands/missionGive/patchMission/reply.ts @@ -1,21 +1,14 @@ import assert from "assert" import { InteractionOperation } from "../../../interfaces/commands/InteractionOperation.js" import { MissionUpdateData } from "../../../interfaces/models/Mission.js" -import operations from "./operations.js" - -const { readOptions, doReply } = operations +import { doReply, readOptions } from "./operations.js" const reply: InteractionOperation = async interaction => { assert(interaction.isChatInputCommand()) - const { target, index, category, content, note, score } = readOptions(interaction) - - const missionUpdateData: MissionUpdateData = {} - if (content !== null) missionUpdateData.content = content - if (note !== null) missionUpdateData.note = note - if (score !== null) missionUpdateData.score = score + const { target, index, category } = readOptions(interaction) - await doReply(interaction, target, category, index, missionUpdateData) + await doReply(interaction, target, category, index) } export default reply \ No newline at end of file diff --git a/src/commands/missionGive/postMission/operations.ts b/src/commands/missionGive/postMission/operations.ts index 537d38f..e5107d5 100644 --- a/src/commands/missionGive/postMission/operations.ts +++ b/src/commands/missionGive/postMission/operations.ts @@ -65,7 +65,7 @@ export const doReply = async (interaction: ChatInputCommandInteraction, target: await modalInteraction.reply("점수가 숫자가 아닙니다!") return } - + const reply = await modalInteraction.reply({ embeds: [ EmbedBuilder.from(replyEmbedPrototype) @@ -77,9 +77,10 @@ export const doReply = async (interaction: ChatInputCommandInteraction, target: fetchReply: true }); - await addConfirmCollector( - commandId, modalInteraction, reply, - onConfirm(modalInteraction, target, mission), - onCancel(modalInteraction, target, mission) - ) + if (!isEditing) + await addConfirmCollector( + commandId, modalInteraction, reply, + onConfirm(modalInteraction, target, mission), + onCancel(modalInteraction, target, mission) + ) } \ No newline at end of file