Skip to content
Open
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
86 changes: 54 additions & 32 deletions packages/discord.js/src/managers/GuildChannelManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const { ChannelFlagsBitField } = require('../util/ChannelFlagsBitField.js');
const { transformGuildForumTag, transformGuildDefaultReaction } = require('../util/Channels.js');
const { ThreadChannelTypes } = require('../util/Constants.js');
const { resolveImage } = require('../util/DataResolver.js');
const { toSnakeCase } = require('../util/Transformers.js');
const { setPosition } = require('../util/Util.js');
const { CachedManager } = require('./CachedManager.js');
const { GuildTextThreadManager } = require('./GuildTextThreadManager.js');
Expand Down Expand Up @@ -298,7 +299,7 @@ class GuildChannelManager extends CachedManager {
/**
* Edits the channel.
*
* @param {GuildChannelResolvable} channel The channel to edit
* @param {GuildChannelResolvable} channel The channel to edit. Can be the id of an uncached channel if not editing position or locking permissions
* @param {GuildChannelEditOptions} options Options for editing the channel
* @returns {Promise<GuildChannel>}
* @example
Expand All @@ -308,57 +309,78 @@ class GuildChannelManager extends CachedManager {
* .catch(console.error);
*/
async edit(channel, options) {
const resolvedChannel = this.resolve(channel);
if (!resolvedChannel) throw new DiscordjsTypeError(ErrorCodes.InvalidType, 'channel', 'GuildChannelResolvable');
// Let uncached channels be edited if they don't need to lock to a parent without passing parent_id
const resolvedChannelId = this.resolveId(channel);
if (!resolvedChannelId) throw new DiscordjsTypeError(ErrorCodes.InvalidType, 'channel', 'GuildChannelResolvable');

const parentId = options.parent && this.client.channels.resolveId(options.parent);

if (options.position !== undefined) {
await this.setPosition(resolvedChannel, options.position, { position: options.position, reason: options.reason });
await this.setPosition(resolvedChannelId, options.position, {
position: options.position,
reason: options.reason,
});
}

let permission_overwrites = options.permissionOverwrites?.map(overwrite =>
let permissionOverwrites = options.permissionOverwrites?.map(overwrite =>
PermissionOverwrites.resolve(overwrite, this.guild),
);

if (options.lockPermissions) {
if (parentId) {
const newParent = this.cache.get(parentId);
if (newParent?.type === ChannelType.GuildCategory) {
permission_overwrites = newParent.permissionOverwrites.cache.map(overwrite =>
permissionOverwrites = newParent.permissionOverwrites.cache.map(overwrite =>
PermissionOverwrites.resolve(overwrite, this.guild),
);
}
} else {
const resolvedChannel = this.resolve(channel);
if (!resolvedChannel) throw new DiscordjsTypeError(ErrorCodes.InvalidType, 'channel', 'GuildChannelResolvable');
if (resolvedChannel.parent) {
permissionOverwrites = resolvedChannel.parent.permissionOverwrites.cache.map(overwrite =>
PermissionOverwrites.resolve(overwrite, this.guild),
);
}
} else if (resolvedChannel.parent) {
permission_overwrites = resolvedChannel.parent.permissionOverwrites.cache.map(overwrite =>
PermissionOverwrites.resolve(overwrite, this.guild),
);
}
}

const newData = await this.client.rest.patch(Routes.channel(resolvedChannel.id), {
body: {
name: options.name,
type: options.type,
topic: options.topic,
nsfw: options.nsfw,
bitrate: options.bitrate,
user_limit: options.userLimit,
rtc_region: options.rtcRegion,
video_quality_mode: options.videoQualityMode,
parent_id: parentId,
lock_permissions: options.lockPermissions,
rate_limit_per_user: options.rateLimitPerUser,
default_auto_archive_duration: options.defaultAutoArchiveDuration,
permission_overwrites,
available_tags: options.availableTags?.map(availableTag => transformGuildForumTag(availableTag)),
default_reaction_emoji:
options.defaultReactionEmoji && transformGuildDefaultReaction(options.defaultReactionEmoji),
default_thread_rate_limit_per_user: options.defaultThreadRateLimitPerUser,
flags: 'flags' in options ? ChannelFlagsBitField.resolve(options.flags) : undefined,
default_sort_order: options.defaultSortOrder,
default_forum_layout: options.defaultForumLayout,
const snakeCaseBody = Object.assign(
// Remove properties we don't want to pass to the API in the body
{
set parent(_) {},
set position(_) {},
set reason(_) {},
},
toSnakeCase(options),
);

// This overwrites a passed snake_case version if a camelCase version OR `parent` is passed
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW, seems that in toSnakeCase(), if there are conflicting keys, the last one (by key order) takes precedence. i.e., if both snake case and camel case versions of a key are passed, the later one overwrites the earlier.
This won't be the case for these ones, where camel case will take precedence. Not sure if we should do something about it (maybe tweaking toSnakeCase to prefer the camel case version?..) or just let it be

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I had set a precedent somewhere to prefer camel case when passsed which is why I did it this way.

I'm not exactly sure if I want to change that function in this PR, but I think it would make sense to because it would be more deterministic that way.

if (parentId) {
snakeCaseBody.parent_id = parentId;
}

// This overwrites a passed snake_case version if a camelCase version is passed
if (permissionOverwrites) {
snakeCaseBody.permission_overwrites = permissionOverwrites;
}

if (options.availableTags) {
snakeCaseBody.available_tags = options.availableTags.map(availableTag =>
'emoji' in availableTag ? transformGuildForumTag(availableTag) : availableTag,
);
}

if (options.defaultReactionEmoji?.id || options.DefaultReactionEmoji?.name) {
snakeCaseBody.default_reaction_emoji = transformGuildDefaultReaction(options.defaultReactionEmoji);
}

if (options.flags) {
snakeCaseBody.flags = ChannelFlagsBitField.resolve(options.flags);
}

const newData = await this.client.rest.patch(Routes.channel(resolvedChannelId), {
body: snakeCaseBody,
reason: options.reason,
});

Expand Down