Skip to content
Merged
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
49 changes: 21 additions & 28 deletions src/utils/event-channel.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,50 +257,43 @@ export namespace EventChannelUtils {
const category = await store.jsChannel(event.roomCategoryId);
if (!category || category.type !== ChannelType.GuildCategory) return;

const rowByChannelId = new Map<Snowflake, DbEventRoomChannel>();
for (const row of rows) rowByChannelId.set(row.channelId, row);

// Partition category children into non-tracked (kept at the top of the category
// in their existing relative order) and tracked (sorted into desired order and
// placed below). Children are split by Discord channel type so text and voice
// channels stay within their own type-local position sequence.
const children = [...(category.children?.cache.values() ?? [])]
.sort((a, b) => (a as any).position - (b as any).position);

const byType = new Map<ChannelType, { untracked: Snowflake[], tracked: DbEventRoomChannel[] }>();
for (const ch of children) {
const bucket = byType.get(ch.type) ?? { untracked: [], tracked: [] };
const row = rowByChannelId.get(ch.id);
if (row) bucket.tracked.push(row);
else bucket.untracked.push(ch.id);
byType.set(ch.type, bucket);
}

const desiredOrder = (a: DbEventRoomChannel, b: DbEventRoomChannel) => {
const indexDiff = Number(a.roomIndex) - Number(b.roomIndex);
if (indexDiff !== 0) return indexDiff;
// null suffix (main channel) sorts LAST within each room
// null suffix (main channel) sorts LAST within each room — keeps `room-code-N` above `room-N`
if (a.suffix === null && b.suffix === null) return 0;
if (a.suffix === null) return 1;
if (b.suffix === null) return -1;
return a.suffix.localeCompare(b.suffix);
};

// Tracked sequence is driven from the DB, not the cache: newly created or
// not-yet-cached channels would otherwise be silently dropped, leaving them
// clumped at their creation-order positions.
const sortedTracked = [...rows].sort(desiredOrder);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Filter tracked rows to the event category before positioning

Building sortedTracked from all DB rows causes reorderRoomChannels to include channels that are tracked but no longer children of event.roomCategoryId (for example, if a room channel was manually moved to a different category). Those off-category channels are then passed into guild.channels.setPositions, which can reorder unrelated channels or fail with missing-permission errors in that other category; with this commit’s new throw, that failure aborts the rest of reconciliation (including reconcileRoleAssignments). The previous logic only reordered channels found under category.children, so this is a functional regression.

Useful? React with 👍 / 👎.


// Untracked channels are anything in the category not in the tracked set.
// Preserve their current relative Discord-position order so user-owned channels
// are not shuffled.
const trackedIds = new Set<Snowflake>(rows.map(r => r.channelId));
const untrackedIds = [...(category.children?.cache.values() ?? [])]
.filter(ch => !trackedIds.has(ch.id))
.sort((a, b) => (a as any).position - (b as any).position)
.map(ch => ch.id);

const payload: { channel: Snowflake, position: number }[] = [];
for (const { untracked, tracked } of byType.values()) {
if (tracked.length === 0) continue;
const sortedTracked = [...tracked].sort(desiredOrder);
let position = 0;
for (const id of untracked) payload.push({ channel: id, position: position++ });
for (const row of sortedTracked) payload.push({ channel: row.channelId, position: position++ });
}
if (payload.length === 0) return;
let position = 0;
for (const id of untrackedIds) payload.push({ channel: id, position: position++ });
for (const row of sortedTracked) payload.push({ channel: row.channelId, position: position++ });

try {
await store.guild.channels.setPositions(payload);
}
catch (e) {
console.error(`EventChannelUtils.reorderRoomChannels: failed to set positions for event ${event.id}:`, e);
throw new CustomError({
message: "Failed to reorder room channels — check that the bot has the Manage Channels permission in the room category.",
});
}
}

Expand Down