Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/features/showcase/create-showcase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
} from '@/common/interactions/modal-interaction.js';
import { logToChannel } from '@/util/channel-logging.js';
import { customId } from '@/util/custom-id.js';
import { deleteShowcase } from './delete-showcase.js';
import { deleteShowcase, deleteShowcaseModal } from './delete-showcase.js';
import { editShowcaseInteraction } from './edit-showcase.js';
import { buildShowcaseModal, createShowcaseMessageContent } from './util.js';
import { SERVER_CHANNELS } from '@/constants/channels.js';
Expand Down Expand Up @@ -157,5 +157,6 @@ const modalHandler: ModalSubmitInteraction = {
};

registerModalSubmitInteraction(modalHandler);
registerModalSubmitInteraction(deleteShowcaseModal);
registerButtonSubmitInteraction(deleteShowcase);
registerButtonSubmitInteraction(editShowcaseInteraction);
150 changes: 129 additions & 21 deletions src/features/showcase/delete-showcase.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,55 @@
import { ChannelType, Colors, EmbedBuilder, MessageFlags } from 'discord.js';
import {
ChannelType,
Colors,
EmbedBuilder,
MessageFlags,
User,
} from 'discord.js';
import type { ButtonSubmitInteraction } from '@/common/interactions/button-interaction.js';
import type { ModalSubmitInteraction } from '@/common/interactions/modal-interaction.js';
import { logToChannel } from '@/util/channel-logging.js';
import { parseCustomId } from '@/util/custom-id.js';
import { customId, parseCustomId } from '@/util/custom-id.js';
import { isUserInServer, isUserModerator } from '@/util/member.js';
import { SERVER_CHANNELS } from '@/constants/channels.js';
import { buildDeleteShowcaseModal, parseShowcaseMessage } from './util.js';

const logShowcaseDeletion = async ({
projectName,
authorId,
interactionUser,
reason,
}: {
projectName: string;
authorId: string;
interactionUser: User;
reason?: string;
}): Promise<void> => {
const embed = new EmbedBuilder()
.setTitle('Showcase Deleted')
.setDescription(
[
`**Project Name:** ${projectName}`,
`**Author:** <@${authorId}>`,
`**Deleted By:** <@${interactionUser.id}>`,
reason ? `**Reason:** ${reason}` : undefined,
]
.filter((line) => line !== undefined)
.join('\n')
)
.setColor(Colors.Red)
.setAuthor({
name: interactionUser.tag,
iconURL: interactionUser.displayAvatarURL(),
});

await logToChannel({
channel: SERVER_CHANNELS.showcaseLogs,
content: {
type: 'embed',
embed,
},
});
};

export const deleteShowcase: ButtonSubmitInteraction = {
commandName: 'delete_showcase',
Expand Down Expand Up @@ -42,7 +88,75 @@ export const deleteShowcase: ButtonSubmitInteraction = {
});
return;
}
if (forumPost === null) {

const message = await forumPost.fetchStarterMessage();
if (!message) {
await interaction.reply({
content: '❌ Could not find the showcase message to delete.',
flags: MessageFlags.Ephemeral,
});
return;
}

if (
interactionUser.id !== ownerId &&
isUserModerator(interaction.member, interaction)
) {
const modal = buildDeleteShowcaseModal({
id: customId('delete_showcase_modal', forumPost.id),
});
await interaction.showModal(modal);
return;
}

const projectName = forumPost.name;
const { authorId } = parseShowcaseMessage(message.content);
await interaction.channel?.delete();
await logShowcaseDeletion({ projectName, authorId, interactionUser });
await interaction.reply({
content: '✅ Showcase post has been deleted.',
flags: MessageFlags.Ephemeral,
});
} catch (error) {
console.error('Error deleting showcase:', error);
await interaction.reply({
content: '❌ An error occurred while trying to delete the showcase.',
flags: MessageFlags.Ephemeral,
});
}
},
};

export const deleteShowcaseModal: ModalSubmitInteraction = {
commandName: 'delete_showcase_modal',
handler: async (interaction) => {
const interactionUser = interaction.user;
const [, forumPostId] = parseCustomId(interaction.customId);
const deleteReason =
interaction.fields.getTextInputValue('deleteReason') || undefined;

if (!interaction.member || !isUserInServer(interaction.member)) {
await interaction.reply({
content: '❌ This command can only be used by server members.',
flags: MessageFlags.Ephemeral,
});
return;
}

if (!isUserModerator(interaction.member, interaction)) {
await interaction.reply({
content: '❌ You do not have permission to delete this showcase.',
flags: MessageFlags.Ephemeral,
});
return;
}

try {
const forumPost =
interaction.channel?.type === ChannelType.PublicThread
? interaction.channel
: null;
if (forumPost === null || forumPost.id !== forumPostId) {
await interaction.reply({
content: '❌ This command can only be used in a forum post.',
flags: MessageFlags.Ephemeral,
Expand All @@ -60,31 +174,25 @@ export const deleteShowcase: ButtonSubmitInteraction = {
}

const projectName = forumPost.name;
const { authorId } = parseShowcaseMessage(message.content);
await interaction.reply({
content: '✅ Showcase deleted successfully.',
flags: MessageFlags.Ephemeral,
});
await interaction.channel?.delete();
await logToChannel({
channel: SERVER_CHANNELS.showcaseLogs,
content: {
type: 'embed',
embed: new EmbedBuilder()
.setTitle('Showcase Deleted')
.setDescription(
`**Project Name:** ${projectName}\n**Deleted By:** <@${interactionUser.id}>`
)
.setColor(Colors.Red)
.setAuthor({
name: interactionUser.tag,
iconURL: interactionUser.displayAvatarURL(),
}),
},

void logShowcaseDeletion({
projectName,
authorId,
interactionUser,
reason: deleteReason,
});
} catch (error) {
console.error('Error deleting showcase:', error);
console.error('Error deleting showcase via modal:', error);
await interaction.reply({
content: '❌ An error occurred while trying to delete the showcase.',
flags: MessageFlags.Ephemeral,
});
}
},
};

// Registration is in create-showcase.ts
28 changes: 28 additions & 0 deletions src/features/showcase/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
StringSelectMenuOptionBuilder,
TextInputBuilder,
TextInputStyle,
ActionRowBuilder,

Check warning on line 11 in src/features/showcase/util.ts

View workflow job for this annotation

GitHub Actions / build-and-test

eslint(no-unused-vars)

Identifier 'ActionRowBuilder' is imported but never used.
type ModalActionRowComponentBuilder,

Check warning on line 12 in src/features/showcase/util.ts

View workflow job for this annotation

GitHub Actions / build-and-test

eslint(no-unused-vars)

Type 'ModalActionRowComponentBuilder' is imported but never used.
} from 'discord.js';

export type ShowcaseMessageData = {
Expand Down Expand Up @@ -162,3 +164,29 @@
(id) => availableTags.find((tag) => tag.id === id)?.name ?? id
);
};

export type BuildDeleteShowcaseModalOptions = {
id: string;
};

export const buildDeleteShowcaseModal = ({
id,
}: BuildDeleteShowcaseModalOptions): ModalBuilder => {
return new ModalBuilder()
.setCustomId(id)
.setTitle('Delete Showcase')
.addLabelComponents((label) =>
label
.setLabel('Reason for deletion (optional)')
.setDescription(
'Provide a reason for deleting this showcase (optional)'
)
.setTextInputComponent((textInput) =>
textInput
.setCustomId('deleteReason')
.setStyle(TextInputStyle.Paragraph)
.setMaxLength(1000)
.setRequired(false)
)
);
};
Loading