2026-06-19 02:10:25 +02:00
|
|
|
import SwiftUI
|
feat(clients): expose all channel codec params + guest nickname everywhere
Channel create/edit UIs only surfaced a subset of the core's vc_audio_config,
and DRED was exposed nowhere. While adding it, found a latent ABI mismatch:
both Swift AudioConfig and the C# VcAudioConfigNative blittable struct were one
int short of the native vc_audio_config (missing the trailing `dred`), so native
read past the managed struct in vc_create_channel/vc_edit_channel.
- core marshaling: thread `dred` through Swift (Models/Marshaling/toNative) and
C# (Structs/Models/Marshaling/VoiceCatClient) -- fixes the ABI gap + enables it
- windows: add the one missing DRED checkbox to ChannelEditDialog
- macos: ChannelEditSheet now exposes application, sample rate, packet loss,
complexity, and DRED (was stereo/bitrate/frame/FEC/DTX only)
- ios: rebuild ChannelEditView into a full create+edit form (all params); add
SessionState.editChannel + an admin Edit swipe action (iOS had no edit UI)
- guest nickname: add a dedicated `nickname` to SavedServer on macOS+iOS
(backward-compatible Codable), shown in Guest mode, wired into the guest auth
path -- guests could not set a display name on either before (only Windows)
Verified: macOS + iOS (sim, arm64) xcodebuild BUILD SUCCEEDED; core ctest 22/23
(only external_pcm aborts on a pre-existing shutdown mutex race; no C++ changed).
2026-06-21 04:03:50 +02:00
|
|
|
import VoiceCatCore
|
2026-06-19 02:10:25 +02:00
|
|
|
|
|
|
|
|
struct ChannelEditView: View {
|
|
|
|
|
let channelId: UInt32?
|
|
|
|
|
@Bindable var session: SessionState
|
|
|
|
|
@Environment(\.dismiss) private var dismiss
|
|
|
|
|
|
feat(clients): expose all channel codec params + guest nickname everywhere
Channel create/edit UIs only surfaced a subset of the core's vc_audio_config,
and DRED was exposed nowhere. While adding it, found a latent ABI mismatch:
both Swift AudioConfig and the C# VcAudioConfigNative blittable struct were one
int short of the native vc_audio_config (missing the trailing `dred`), so native
read past the managed struct in vc_create_channel/vc_edit_channel.
- core marshaling: thread `dred` through Swift (Models/Marshaling/toNative) and
C# (Structs/Models/Marshaling/VoiceCatClient) -- fixes the ABI gap + enables it
- windows: add the one missing DRED checkbox to ChannelEditDialog
- macos: ChannelEditSheet now exposes application, sample rate, packet loss,
complexity, and DRED (was stereo/bitrate/frame/FEC/DTX only)
- ios: rebuild ChannelEditView into a full create+edit form (all params); add
SessionState.editChannel + an admin Edit swipe action (iOS had no edit UI)
- guest nickname: add a dedicated `nickname` to SavedServer on macOS+iOS
(backward-compatible Codable), shown in Guest mode, wired into the guest auth
path -- guests could not set a display name on either before (only Windows)
Verified: macOS + iOS (sim, arm64) xcodebuild BUILD SUCCEEDED; core ctest 22/23
(only external_pcm aborts on a pre-existing shutdown mutex race; no C++ changed).
2026-06-21 04:03:50 +02:00
|
|
|
// General
|
2026-06-19 02:10:25 +02:00
|
|
|
@State private var name = ""
|
|
|
|
|
@State private var topic = ""
|
feat(clients): expose all channel codec params + guest nickname everywhere
Channel create/edit UIs only surfaced a subset of the core's vc_audio_config,
and DRED was exposed nowhere. While adding it, found a latent ABI mismatch:
both Swift AudioConfig and the C# VcAudioConfigNative blittable struct were one
int short of the native vc_audio_config (missing the trailing `dred`), so native
read past the managed struct in vc_create_channel/vc_edit_channel.
- core marshaling: thread `dred` through Swift (Models/Marshaling/toNative) and
C# (Structs/Models/Marshaling/VoiceCatClient) -- fixes the ABI gap + enables it
- windows: add the one missing DRED checkbox to ChannelEditDialog
- macos: ChannelEditSheet now exposes application, sample rate, packet loss,
complexity, and DRED (was stereo/bitrate/frame/FEC/DTX only)
- ios: rebuild ChannelEditView into a full create+edit form (all params); add
SessionState.editChannel + an admin Edit swipe action (iOS had no edit UI)
- guest nickname: add a dedicated `nickname` to SavedServer on macOS+iOS
(backward-compatible Codable), shown in Guest mode, wired into the guest auth
path -- guests could not set a display name on either before (only Windows)
Verified: macOS + iOS (sim, arm64) xcodebuild BUILD SUCCEEDED; core ctest 22/23
(only external_pcm aborts on a pre-existing shutdown mutex race; no C++ changed).
2026-06-21 04:03:50 +02:00
|
|
|
@State private var parentId: UInt32 = 0
|
|
|
|
|
@State private var passwordProtected = false
|
|
|
|
|
@State private var password = ""
|
|
|
|
|
@State private var maxUsers = "0"
|
|
|
|
|
@State private var sortOrder = "0"
|
|
|
|
|
|
feat: fix voice join/leave, channel edit defaults, channel-update stream restart
Three bugs fixed across the full stack (proto/server/core/ABI/Win/macOS/iOS):
1. Join/Leave Voice now truly subscribes/unsubscribes from the voice plane.
Previously the button only toggled the local mic — receiving was always on
(gated by channel membership alone). Added a protocol-level voice subscription
concept: new SubscribeVoiceRequest/UnsubscribeVoiceRequest/VoiceSubscriptionResult
proto messages, User.voice_subscribed field, vc_join_voice/vc_leave_voice C ABI
functions, VC_EVENT_VOICE_STATE event, server-side voice_subscribed flag checked
by the SFU relay recipient filter, and core-client gating of remote-stream
decoder setup. All three clients rewired to subscribe+mic on Join / unsubscribe
on Leave. Text chat works regardless of voice subscription.
2. Channel edit dialog now shows the channel's actual current settings. The read
struct vc_channel was missing sort_order and audio fields — only the write
struct vc_channel_info had them. Extended vc_channel with both (additive, no
ABI break), updated the session model and list_channels marshaling to populate
them, and updated all three clients' edit callers to use actual channel info
instead of hardcoded defaults.
3. Channel parameter updates now automatically restart everyone's streams.
Previously editing a channel's audio config persisted and broadcast a
ChannelEvent::UPDATED, but no layer restarted streams — encoders/decoders are
frozen at announce time. handle_channel_event now detects audio-config changes
on the user's current channel and stop->starts each active local stream. The
server reads the updated config on re-announce; peers wire up fresh decoders
at the new ssrc.
All 29 CTest tests pass; Windows DLL + C# client build clean. Apple clients not
yet compile-verified (Windows environment).
2026-06-24 14:29:39 +02:00
|
|
|
// Audio (Opus) — populated from the channel's current config when editing.
|
feat(clients): expose all channel codec params + guest nickname everywhere
Channel create/edit UIs only surfaced a subset of the core's vc_audio_config,
and DRED was exposed nowhere. While adding it, found a latent ABI mismatch:
both Swift AudioConfig and the C# VcAudioConfigNative blittable struct were one
int short of the native vc_audio_config (missing the trailing `dred`), so native
read past the managed struct in vc_create_channel/vc_edit_channel.
- core marshaling: thread `dred` through Swift (Models/Marshaling/toNative) and
C# (Structs/Models/Marshaling/VoiceCatClient) -- fixes the ABI gap + enables it
- windows: add the one missing DRED checkbox to ChannelEditDialog
- macos: ChannelEditSheet now exposes application, sample rate, packet loss,
complexity, and DRED (was stereo/bitrate/frame/FEC/DTX only)
- ios: rebuild ChannelEditView into a full create+edit form (all params); add
SessionState.editChannel + an admin Edit swipe action (iOS had no edit UI)
- guest nickname: add a dedicated `nickname` to SavedServer on macOS+iOS
(backward-compatible Codable), shown in Guest mode, wired into the guest auth
path -- guests could not set a display name on either before (only Windows)
Verified: macOS + iOS (sim, arm64) xcodebuild BUILD SUCCEEDED; core ctest 22/23
(only external_pcm aborts on a pre-existing shutdown mutex race; no C++ changed).
2026-06-21 04:03:50 +02:00
|
|
|
@State private var stereo = false
|
|
|
|
|
@State private var bitrate = "64000"
|
|
|
|
|
@State private var sampleRate = "48000"
|
|
|
|
|
@State private var frameMs: UInt32 = 20
|
|
|
|
|
@State private var application: UInt32 = 0
|
|
|
|
|
@State private var packetLoss = "5"
|
|
|
|
|
@State private var complexity = 10
|
|
|
|
|
@State private var fec = true
|
|
|
|
|
@State private var dtx = false
|
|
|
|
|
@State private var dred = false
|
|
|
|
|
|
|
|
|
|
private var isEditing: Bool { channelId != nil }
|
2026-06-19 02:10:25 +02:00
|
|
|
|
|
|
|
|
var body: some View {
|
|
|
|
|
NavigationStack {
|
|
|
|
|
Form {
|
|
|
|
|
Section("Channel Info") {
|
|
|
|
|
TextField("Name", text: $name)
|
|
|
|
|
.autocorrectionDisabled()
|
|
|
|
|
.accessibilityLabel("Channel name")
|
|
|
|
|
TextField("Topic (optional)", text: $topic)
|
|
|
|
|
.accessibilityLabel("Channel topic, optional")
|
feat(clients): expose all channel codec params + guest nickname everywhere
Channel create/edit UIs only surfaced a subset of the core's vc_audio_config,
and DRED was exposed nowhere. While adding it, found a latent ABI mismatch:
both Swift AudioConfig and the C# VcAudioConfigNative blittable struct were one
int short of the native vc_audio_config (missing the trailing `dred`), so native
read past the managed struct in vc_create_channel/vc_edit_channel.
- core marshaling: thread `dred` through Swift (Models/Marshaling/toNative) and
C# (Structs/Models/Marshaling/VoiceCatClient) -- fixes the ABI gap + enables it
- windows: add the one missing DRED checkbox to ChannelEditDialog
- macos: ChannelEditSheet now exposes application, sample rate, packet loss,
complexity, and DRED (was stereo/bitrate/frame/FEC/DTX only)
- ios: rebuild ChannelEditView into a full create+edit form (all params); add
SessionState.editChannel + an admin Edit swipe action (iOS had no edit UI)
- guest nickname: add a dedicated `nickname` to SavedServer on macOS+iOS
(backward-compatible Codable), shown in Guest mode, wired into the guest auth
path -- guests could not set a display name on either before (only Windows)
Verified: macOS + iOS (sim, arm64) xcodebuild BUILD SUCCEEDED; core ctest 22/23
(only external_pcm aborts on a pre-existing shutdown mutex race; no C++ changed).
2026-06-21 04:03:50 +02:00
|
|
|
Picker("Parent", selection: $parentId) {
|
|
|
|
|
Text("(root)").tag(UInt32(0))
|
|
|
|
|
ForEach(parentOptions) { ch in
|
|
|
|
|
Text(ch.name).tag(ch.id)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.accessibilityLabel("Parent channel")
|
|
|
|
|
Toggle("Password protected", isOn: $passwordProtected)
|
|
|
|
|
if passwordProtected {
|
|
|
|
|
SecureField("Password (blank keeps existing)", text: $password)
|
|
|
|
|
.accessibilityLabel("Channel password")
|
|
|
|
|
}
|
|
|
|
|
TextField("Max users (0 = unlimited)", text: $maxUsers)
|
|
|
|
|
.keyboardType(.numberPad)
|
|
|
|
|
.accessibilityLabel("Maximum users, zero means unlimited")
|
|
|
|
|
TextField("Sort order", text: $sortOrder)
|
|
|
|
|
.keyboardType(.numberPad)
|
|
|
|
|
.accessibilityLabel("Sort order")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Section("Audio (Opus)") {
|
|
|
|
|
Toggle("Stereo", isOn: $stereo)
|
|
|
|
|
TextField("Bitrate (bps)", text: $bitrate)
|
|
|
|
|
.keyboardType(.numberPad)
|
|
|
|
|
.accessibilityLabel("Bitrate in bits per second")
|
|
|
|
|
TextField("Sample rate (Hz)", text: $sampleRate)
|
|
|
|
|
.keyboardType(.numberPad)
|
|
|
|
|
.accessibilityLabel("Sample rate in Hz")
|
|
|
|
|
Picker("Frame", selection: $frameMs) {
|
|
|
|
|
ForEach([UInt32(10), 20, 40, 60], id: \.self) { ms in
|
|
|
|
|
Text("\(ms) ms").tag(ms)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.accessibilityLabel("Opus frame duration")
|
|
|
|
|
Picker("Application", selection: $application) {
|
|
|
|
|
Text("VoIP").tag(UInt32(0))
|
|
|
|
|
Text("Audio").tag(UInt32(1))
|
|
|
|
|
Text("Low delay").tag(UInt32(2))
|
|
|
|
|
}
|
|
|
|
|
.accessibilityLabel("Opus application profile")
|
|
|
|
|
TextField("Expected packet loss %", text: $packetLoss)
|
|
|
|
|
.keyboardType(.numberPad)
|
|
|
|
|
.accessibilityLabel("Expected packet loss percent, 0 to 100")
|
|
|
|
|
Stepper("Complexity: \(complexity)", value: $complexity, in: 0...10)
|
|
|
|
|
.accessibilityLabel("Opus complexity, 0 to 10")
|
|
|
|
|
Toggle("FEC (forward error correction)", isOn: $fec)
|
|
|
|
|
Toggle("DTX (discontinuous transmission)", isOn: $dtx)
|
|
|
|
|
Toggle("DRED (deep redundancy)", isOn: $dred)
|
2026-06-19 02:10:25 +02:00
|
|
|
}
|
|
|
|
|
}
|
feat(clients): expose all channel codec params + guest nickname everywhere
Channel create/edit UIs only surfaced a subset of the core's vc_audio_config,
and DRED was exposed nowhere. While adding it, found a latent ABI mismatch:
both Swift AudioConfig and the C# VcAudioConfigNative blittable struct were one
int short of the native vc_audio_config (missing the trailing `dred`), so native
read past the managed struct in vc_create_channel/vc_edit_channel.
- core marshaling: thread `dred` through Swift (Models/Marshaling/toNative) and
C# (Structs/Models/Marshaling/VoiceCatClient) -- fixes the ABI gap + enables it
- windows: add the one missing DRED checkbox to ChannelEditDialog
- macos: ChannelEditSheet now exposes application, sample rate, packet loss,
complexity, and DRED (was stereo/bitrate/frame/FEC/DTX only)
- ios: rebuild ChannelEditView into a full create+edit form (all params); add
SessionState.editChannel + an admin Edit swipe action (iOS had no edit UI)
- guest nickname: add a dedicated `nickname` to SavedServer on macOS+iOS
(backward-compatible Codable), shown in Guest mode, wired into the guest auth
path -- guests could not set a display name on either before (only Windows)
Verified: macOS + iOS (sim, arm64) xcodebuild BUILD SUCCEEDED; core ctest 22/23
(only external_pcm aborts on a pre-existing shutdown mutex race; no C++ changed).
2026-06-21 04:03:50 +02:00
|
|
|
.navigationTitle(isEditing ? "Edit Channel" : "New Channel")
|
2026-06-19 02:10:25 +02:00
|
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
|
|
|
.toolbar {
|
|
|
|
|
ToolbarItem(placement: .cancellationAction) {
|
|
|
|
|
Button("Cancel") { dismiss() }
|
|
|
|
|
}
|
|
|
|
|
ToolbarItem(placement: .confirmationAction) {
|
|
|
|
|
Button("Save") {
|
feat(clients): expose all channel codec params + guest nickname everywhere
Channel create/edit UIs only surfaced a subset of the core's vc_audio_config,
and DRED was exposed nowhere. While adding it, found a latent ABI mismatch:
both Swift AudioConfig and the C# VcAudioConfigNative blittable struct were one
int short of the native vc_audio_config (missing the trailing `dred`), so native
read past the managed struct in vc_create_channel/vc_edit_channel.
- core marshaling: thread `dred` through Swift (Models/Marshaling/toNative) and
C# (Structs/Models/Marshaling/VoiceCatClient) -- fixes the ABI gap + enables it
- windows: add the one missing DRED checkbox to ChannelEditDialog
- macos: ChannelEditSheet now exposes application, sample rate, packet loss,
complexity, and DRED (was stereo/bitrate/frame/FEC/DTX only)
- ios: rebuild ChannelEditView into a full create+edit form (all params); add
SessionState.editChannel + an admin Edit swipe action (iOS had no edit UI)
- guest nickname: add a dedicated `nickname` to SavedServer on macOS+iOS
(backward-compatible Codable), shown in Guest mode, wired into the guest auth
path -- guests could not set a display name on either before (only Windows)
Verified: macOS + iOS (sim, arm64) xcodebuild BUILD SUCCEEDED; core ctest 22/23
(only external_pcm aborts on a pre-existing shutdown mutex race; no C++ changed).
2026-06-21 04:03:50 +02:00
|
|
|
save()
|
2026-06-19 02:10:25 +02:00
|
|
|
dismiss()
|
|
|
|
|
}
|
|
|
|
|
.disabled(name.trimmingCharacters(in: .whitespaces).isEmpty)
|
|
|
|
|
}
|
|
|
|
|
}
|
feat(clients): expose all channel codec params + guest nickname everywhere
Channel create/edit UIs only surfaced a subset of the core's vc_audio_config,
and DRED was exposed nowhere. While adding it, found a latent ABI mismatch:
both Swift AudioConfig and the C# VcAudioConfigNative blittable struct were one
int short of the native vc_audio_config (missing the trailing `dred`), so native
read past the managed struct in vc_create_channel/vc_edit_channel.
- core marshaling: thread `dred` through Swift (Models/Marshaling/toNative) and
C# (Structs/Models/Marshaling/VoiceCatClient) -- fixes the ABI gap + enables it
- windows: add the one missing DRED checkbox to ChannelEditDialog
- macos: ChannelEditSheet now exposes application, sample rate, packet loss,
complexity, and DRED (was stereo/bitrate/frame/FEC/DTX only)
- ios: rebuild ChannelEditView into a full create+edit form (all params); add
SessionState.editChannel + an admin Edit swipe action (iOS had no edit UI)
- guest nickname: add a dedicated `nickname` to SavedServer on macOS+iOS
(backward-compatible Codable), shown in Guest mode, wired into the guest auth
path -- guests could not set a display name on either before (only Windows)
Verified: macOS + iOS (sim, arm64) xcodebuild BUILD SUCCEEDED; core ctest 22/23
(only external_pcm aborts on a pre-existing shutdown mutex race; no C++ changed).
2026-06-21 04:03:50 +02:00
|
|
|
.onAppear(perform: loadIfEditing)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Channels offered as a parent. Excludes the channel being edited so it can't parent itself.
|
|
|
|
|
private var parentOptions: [Channel] {
|
|
|
|
|
session.channels
|
|
|
|
|
.filter { $0.id != channelId }
|
|
|
|
|
.sorted { $0.name < $1.name }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private func loadIfEditing() {
|
|
|
|
|
guard let id = channelId,
|
|
|
|
|
let ch = session.channels.first(where: { $0.id == id }) else { return }
|
|
|
|
|
name = ch.name
|
|
|
|
|
topic = ch.topic
|
|
|
|
|
parentId = ch.parentId
|
|
|
|
|
passwordProtected = ch.passwordProtected
|
|
|
|
|
maxUsers = "\(ch.maxUsers)"
|
feat: fix voice join/leave, channel edit defaults, channel-update stream restart
Three bugs fixed across the full stack (proto/server/core/ABI/Win/macOS/iOS):
1. Join/Leave Voice now truly subscribes/unsubscribes from the voice plane.
Previously the button only toggled the local mic — receiving was always on
(gated by channel membership alone). Added a protocol-level voice subscription
concept: new SubscribeVoiceRequest/UnsubscribeVoiceRequest/VoiceSubscriptionResult
proto messages, User.voice_subscribed field, vc_join_voice/vc_leave_voice C ABI
functions, VC_EVENT_VOICE_STATE event, server-side voice_subscribed flag checked
by the SFU relay recipient filter, and core-client gating of remote-stream
decoder setup. All three clients rewired to subscribe+mic on Join / unsubscribe
on Leave. Text chat works regardless of voice subscription.
2. Channel edit dialog now shows the channel's actual current settings. The read
struct vc_channel was missing sort_order and audio fields — only the write
struct vc_channel_info had them. Extended vc_channel with both (additive, no
ABI break), updated the session model and list_channels marshaling to populate
them, and updated all three clients' edit callers to use actual channel info
instead of hardcoded defaults.
3. Channel parameter updates now automatically restart everyone's streams.
Previously editing a channel's audio config persisted and broadcast a
ChannelEvent::UPDATED, but no layer restarted streams — encoders/decoders are
frozen at announce time. handle_channel_event now detects audio-config changes
on the user's current channel and stop->starts each active local stream. The
server reads the updated config on re-announce; peers wire up fresh decoders
at the new ssrc.
All 29 CTest tests pass; Windows DLL + C# client build clean. Apple clients not
yet compile-verified (Windows environment).
2026-06-24 14:29:39 +02:00
|
|
|
sortOrder = "\(ch.sortOrder)"
|
|
|
|
|
stereo = ch.audio.stereo
|
|
|
|
|
bitrate = "\(ch.audio.bitrateBps)"
|
|
|
|
|
sampleRate = "\(ch.audio.sampleRate)"
|
|
|
|
|
frameMs = ch.audio.frameMs
|
|
|
|
|
application = ch.audio.application
|
|
|
|
|
packetLoss = "\(ch.audio.expectedPacketLoss)"
|
2026-06-24 16:49:37 +02:00
|
|
|
complexity = Int(ch.audio.complexity)
|
feat: fix voice join/leave, channel edit defaults, channel-update stream restart
Three bugs fixed across the full stack (proto/server/core/ABI/Win/macOS/iOS):
1. Join/Leave Voice now truly subscribes/unsubscribes from the voice plane.
Previously the button only toggled the local mic — receiving was always on
(gated by channel membership alone). Added a protocol-level voice subscription
concept: new SubscribeVoiceRequest/UnsubscribeVoiceRequest/VoiceSubscriptionResult
proto messages, User.voice_subscribed field, vc_join_voice/vc_leave_voice C ABI
functions, VC_EVENT_VOICE_STATE event, server-side voice_subscribed flag checked
by the SFU relay recipient filter, and core-client gating of remote-stream
decoder setup. All three clients rewired to subscribe+mic on Join / unsubscribe
on Leave. Text chat works regardless of voice subscription.
2. Channel edit dialog now shows the channel's actual current settings. The read
struct vc_channel was missing sort_order and audio fields — only the write
struct vc_channel_info had them. Extended vc_channel with both (additive, no
ABI break), updated the session model and list_channels marshaling to populate
them, and updated all three clients' edit callers to use actual channel info
instead of hardcoded defaults.
3. Channel parameter updates now automatically restart everyone's streams.
Previously editing a channel's audio config persisted and broadcast a
ChannelEvent::UPDATED, but no layer restarted streams — encoders/decoders are
frozen at announce time. handle_channel_event now detects audio-config changes
on the user's current channel and stop->starts each active local stream. The
server reads the updated config on re-announce; peers wire up fresh decoders
at the new ssrc.
All 29 CTest tests pass; Windows DLL + C# client build clean. Apple clients not
yet compile-verified (Windows environment).
2026-06-24 14:29:39 +02:00
|
|
|
fec = ch.audio.fec
|
|
|
|
|
dtx = ch.audio.dtx
|
|
|
|
|
dred = ch.audio.dred
|
feat(clients): expose all channel codec params + guest nickname everywhere
Channel create/edit UIs only surfaced a subset of the core's vc_audio_config,
and DRED was exposed nowhere. While adding it, found a latent ABI mismatch:
both Swift AudioConfig and the C# VcAudioConfigNative blittable struct were one
int short of the native vc_audio_config (missing the trailing `dred`), so native
read past the managed struct in vc_create_channel/vc_edit_channel.
- core marshaling: thread `dred` through Swift (Models/Marshaling/toNative) and
C# (Structs/Models/Marshaling/VoiceCatClient) -- fixes the ABI gap + enables it
- windows: add the one missing DRED checkbox to ChannelEditDialog
- macos: ChannelEditSheet now exposes application, sample rate, packet loss,
complexity, and DRED (was stereo/bitrate/frame/FEC/DTX only)
- ios: rebuild ChannelEditView into a full create+edit form (all params); add
SessionState.editChannel + an admin Edit swipe action (iOS had no edit UI)
- guest nickname: add a dedicated `nickname` to SavedServer on macOS+iOS
(backward-compatible Codable), shown in Guest mode, wired into the guest auth
path -- guests could not set a display name on either before (only Windows)
Verified: macOS + iOS (sim, arm64) xcodebuild BUILD SUCCEEDED; core ctest 22/23
(only external_pcm aborts on a pre-existing shutdown mutex race; no C++ changed).
2026-06-21 04:03:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private func save() {
|
|
|
|
|
let trimmedName = name.trimmingCharacters(in: .whitespaces)
|
|
|
|
|
guard !trimmedName.isEmpty else { return }
|
|
|
|
|
|
|
|
|
|
let audio = AudioConfig(
|
|
|
|
|
stereo: stereo,
|
|
|
|
|
sampleRate: UInt32(sampleRate) ?? 48000,
|
|
|
|
|
bitrateBps: UInt32(bitrate) ?? 64000,
|
|
|
|
|
frameMs: frameMs,
|
|
|
|
|
application: application,
|
|
|
|
|
fec: fec,
|
|
|
|
|
expectedPacketLoss: min(UInt32(packetLoss) ?? 5, 100),
|
|
|
|
|
dtx: dtx,
|
|
|
|
|
complexity: UInt32(complexity),
|
|
|
|
|
dred: dred
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
let pw: String? = passwordProtected ? (password.isEmpty ? nil : password) : nil
|
|
|
|
|
let info = ChannelEdit(
|
|
|
|
|
id: channelId ?? 0,
|
|
|
|
|
parentId: parentId,
|
|
|
|
|
name: trimmedName,
|
|
|
|
|
topic: topic,
|
|
|
|
|
passwordProtected: passwordProtected,
|
|
|
|
|
password: pw,
|
|
|
|
|
maxUsers: UInt32(maxUsers) ?? 0,
|
|
|
|
|
sortOrder: UInt32(sortOrder) ?? 0,
|
|
|
|
|
audio: audio
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if isEditing {
|
|
|
|
|
session.editChannel(info)
|
|
|
|
|
} else {
|
|
|
|
|
session.createChannel(info)
|
2026-06-19 02:10:25 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|