Channel display fix

This commit is contained in:
Garth Vander Houwen 2026-04-18 16:10:48 -07:00
parent ed9605a4c1
commit a6e7a2a803
3 changed files with 64 additions and 9 deletions

View file

@ -218,7 +218,18 @@ extension AccessoryManager {
do {
let fetchedMyInfo = try context.fetch(fetchMyInfoRequest)
if fetchedMyInfo.count == 1 {
let channelsToDelete = fetchedMyInfo[0].channels
for channel in channelsToDelete {
context.delete(channel)
}
fetchedMyInfo[0].channels.removeAll()
// Clean orphaned channels from older app versions where channels were
// detached but not deleted, which can create duplicate rows in queries.
let allChannels = try context.fetch(FetchDescriptor<ChannelEntity>())
for channel in allChannels where channel.myInfoChannel == nil {
context.delete(channel)
}
do {
try context.save()
} catch {

View file

@ -22,8 +22,19 @@ struct ChannelList: View {
var restrictedChannels = ["gpio", "mqtt", "serial", "admin"]
@Query(sort: \ChannelEntity.index)
private var channels: [ChannelEntity]
private var visibleChannels: [ChannelEntity] {
guard let myInfo = node?.myInfo else { return [] }
var channelsByIndex: [Int32: ChannelEntity] = [:]
for channel in myInfo.channels {
channelsByIndex[channel.index] = channel
}
return channelsByIndex
.values
.filter { !restrictedChannels.contains($0.name?.lowercased() ?? "") }
.sorted { $0.index < $1.index }
}
@ViewBuilder
private func makeChannelRow(
@ -170,10 +181,8 @@ struct ChannelList: View {
// Display Contacts for the rest of the non admin channels
if let node, let myInfo = node.myInfo {
List(selection: $channelSelection) {
ForEach(channels) { (channel: ChannelEntity) in
if !restrictedChannels.contains(channel.name?.lowercased() ?? "") {
makeChannelListItem(node: node, myInfo: myInfo, channel: channel)
}
ForEach(visibleChannels) { (channel: ChannelEntity) in
makeChannelListItem(node: node, myInfo: myInfo, channel: channel)
}
}
.olderThanOS26 { $0.padding([.top, .bottom]) }

View file

@ -53,6 +53,33 @@ struct Channels: View {
@Query(sort: \NodeInfoEntity.lastHeard, order: .reverse)
var nodes: [NodeInfoEntity]
private var displayChannels: [ChannelEntity] {
guard let channels = node.myInfo?.channels else { return [] }
var byIndex: [Int32: ChannelEntity] = [:]
for channel in channels {
byIndex[channel.index] = channel
}
return byIndex.values.sorted { $0.index < $1.index }
}
private func normalizeDuplicateChannelsIfNeeded() {
guard let channels = node.myInfo?.channels else { return }
var uniqueChannels: [Int32: ChannelEntity] = [:]
for channel in channels {
uniqueChannels[channel.index] = channel
}
let deduped = uniqueChannels.values.sorted { $0.index < $1.index }
guard deduped.count != channels.count else { return }
node.myInfo?.channels = deduped
do {
try context.save()
Logger.data.info("💾 Normalized duplicate channels for node \(self.node.num, privacy: .public)")
} catch {
context.rollback()
Logger.data.error("Failed normalizing duplicate channels: \(error.localizedDescription, privacy: .public)")
}
}
var body: some View {
VStack {
@ -61,7 +88,7 @@ struct Channels: View {
.tipBackground(colorScheme == .dark ? Color(.systemBackground) : Color(.secondarySystemBackground))
.listRowSeparator(.hidden)
if node.myInfo != nil {
ForEach(node.myInfo?.channels ?? [], id: \.self) { (channel: ChannelEntity) in
ForEach(displayChannels, id: \.self) { (channel: ChannelEntity) in
Button(action: {
channelIndex = channel.index
channelRole = Int(channel.role)
@ -175,12 +202,17 @@ struct Channels: View {
guard var channels = node.myInfo?.channels else {
return
}
if let idx = channels.firstIndex(where: { $0.psk == selectedChannel?.psk && $0.name == selectedChannel?.name }) {
if let idx = channels.firstIndex(where: { $0.index == selectedChannel?.index }) {
channels[idx] = selectedChannel!
} else {
channels.append(selectedChannel!)
}
node.myInfo?.channels = channels
var uniqueChannels: [Int32: ChannelEntity] = [:]
for channel in channels {
uniqueChannels[channel.index] = channel
}
node.myInfo?.channels = uniqueChannels.values.sorted { $0.index < $1.index }
if channel.role != Channel.Role.disabled {
do {
try context.save()
@ -340,6 +372,9 @@ struct Channels: View {
}
.padding(.bottom, 5)
.navigationTitle("Channels")
.onAppear {
normalizeDuplicateChannelsIfNeeded()
}
.navigationBarItems(trailing:
ZStack {
ConnectedDevice(deviceConnected: accessoryManager.isConnected, name: accessoryManager.activeConnection?.device.shortName ?? "?")