Files
mumh5/src/core/channel-order.ts
T
kibiandClaude Opus 5.5 30a75a0629 Text channels between mumh5 users; drag channels in front of or behind another
Text channels are real Mumble channels with a marker in the description.
Their messages travel as plugin data to everyone on the server, are kept
by the clients and handed to people who connect later.

Channels can be dropped in front of or behind another channel, with a
line where they land. A description preview no longer sticks after a drop.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 20:16:03 +02:00

26 lines
1.4 KiB
TypeScript

// Where a channel sits among its siblings. Mumble sorts by position, then by name, so a
// channel dropped between two others needs a position that puts it exactly there.
export interface Ordered { id: number; position: number; name: string }
export const byOrder = (a: Ordered, b: Ordered) => a.position - b.position || a.name.localeCompare(b.name);
// Position changes that put `moved` at `index` among `siblings` (which need not contain it).
// One change when there is room between the neighbours; otherwise the row is numbered again.
export function planMove(siblings: Ordered[], moved: Ordered, index: number): { id: number; position: number }[] {
const rest = siblings.filter(c => c.id !== moved.id).sort(byOrder);
const at = Math.max(0, Math.min(index, rest.length));
const prev = rest[at - 1];
const next = rest[at];
const fits = (position: number) => {
const m = { ...moved, position };
return (!prev || byOrder(prev, m) < 0) && (!next || byOrder(m, next) < 0);
};
const tries = [moved.position, prev?.position, next?.position, prev && prev.position + 1, next && next.position - 1];
for (const p of tries) {
if (p == null || !fits(p)) continue;
return p === moved.position ? [] : [{ id: moved.id, position: p }];
}
const order = [...rest.slice(0, at), moved, ...rest.slice(at)];
return order.map((c, i) => ({ id: c.id, position: i })).filter((u, i) => order[i].position !== u.position);
}