Fix Windows channel tree ordering
Build and test / test (macos-latest) (push) Canceled after 0s
Build and test / test (ubuntu-24.04) (push) Canceled after 0s
Build and test / test (windows-latest) (push) Canceled after 0s
Build and test / apple-client (push) Canceled after 0s

This commit is contained in:
2026-09-21 19:47:14 +02:00
parent 4aa0aa4fbd
commit 8471eac363
7 changed files with 66 additions and 16 deletions
@@ -24,7 +24,7 @@ public sealed partial class VoiceCatClient
}
private static Voicecat.V1.Channel Channel(ChannelEditInfo info) => new()
{
Id = info.Id, ParentId = info.ParentId, Name = info.Name, Topic = info.Topic, MaxUsers = info.MaxUsers, Order = unchecked((int)info.SortOrder),
Id = info.Id, ParentId = info.ParentId, Name = info.Name, Topic = info.Topic, MaxUsers = info.MaxUsers, Order = info.SortOrder,
Audio = new() { Codec = info.Audio.Codec, Mode = info.Audio.Stereo ? ChannelMode.ModeStereo : ChannelMode.ModeMono, SampleRate = info.Audio.SampleRate,
BitrateBps = info.Audio.BitrateBps, FrameMs = info.Audio.FrameMs, Application = (OpusApplication)info.Audio.Application, Complexity = info.Audio.Complexity,
Fec = info.Audio.Fec, ExpectedPacketLoss = info.Audio.ExpectedPacketLoss, Dtx = info.Audio.Dtx, Dred = info.Audio.Dred }
@@ -0,0 +1,32 @@
namespace VoiceCat.Windows;
/// <summary>A channel and its ordered children as displayed by the Windows client.</summary>
public sealed record ChannelHierarchyItem(
ChannelInfo Channel,
IReadOnlyList<ChannelHierarchyItem> Children);
public static class ChannelHierarchy
{
/// <summary>
/// Builds the server-defined channel hierarchy. SortOrder orders siblings; LINQ's stable
/// ordering preserves the server's sequence when siblings have the same value.
/// </summary>
public static IReadOnlyList<ChannelHierarchyItem> Build(IReadOnlyList<ChannelInfo> channels)
{
var byParent = channels
.GroupBy(channel => channel.ParentId)
.ToDictionary(group => group.Key, group => group.OrderBy(channel => channel.SortOrder).ToArray());
IReadOnlyList<ChannelHierarchyItem> AddChildren(uint parentId)
{
if (!byParent.TryGetValue(parentId, out ChannelInfo[]? children))
return [];
return children
.Select(channel => new ChannelHierarchyItem(channel, AddChildren(channel.Id)))
.ToArray();
}
return AddChildren(0);
}
}
+2 -2
View File
@@ -8,7 +8,7 @@ public sealed record ChannelInfo(
string Topic,
bool PasswordProtected,
uint MaxUsers,
uint SortOrder,
int SortOrder,
AudioConfigInfo Audio);
public sealed record ChannelEditInfo(
@@ -19,7 +19,7 @@ public sealed record ChannelEditInfo(
bool PasswordProtected,
string? Password,
uint MaxUsers,
uint SortOrder,
int SortOrder,
AudioConfigInfo Audio);
public sealed record UserInfo(
@@ -172,7 +172,7 @@ public sealed partial class VoiceCatClient : IDisposable
}
public VcResult LeaveVoice() { StopDevices(); local.Clear(); return Request(new() { UnsubscribeVoice = new() }); }
public List<ChannelInfo> ListChannels() => core.Channels.Select(c => new ChannelInfo(c.Id, c.ParentId, c.Name, c.Topic, c.PasswordProtected, c.MaxUsers, unchecked((uint)c.Order), Audio(c.Audio))).ToList();
public List<ChannelInfo> ListChannels() => core.Channels.Select(c => new ChannelInfo(c.Id, c.ParentId, c.Name, c.Topic, c.PasswordProtected, c.MaxUsers, c.Order, Audio(c.Audio))).ToList();
public List<UserInfo> ListUsers() => core.Users.Select(u => new UserInfo(u.Id, u.Nickname, u.IsGuest, u.ChannelId, u.SelfMicMuted, u.SelfDeafened, u.ServerMuted, u.ServerDeafened, u.VoiceSubscribed)).ToList();
public List<StreamSummary> ListUserStreams(uint id) => id == core.Authentication?.Self.Id
? local.Values.Select(s => new StreamSummary(s.Alias, (VcStreamKind)s.Info.Kind, s.Info.Label)).ToList()