diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index fc33a6dc..fe9506d9 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -79,7 +79,6 @@ DD86D40C287F401000BAEB7A /* SaveChannelQRCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD86D40B287F401000BAEB7A /* SaveChannelQRCode.swift */; }; DD86D40F2881BE4C00BAEB7A /* CsvDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD86D40E2881BE4C00BAEB7A /* CsvDocument.swift */; }; DD86D4112881D16900BAEB7A /* WriteCsvFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD86D4102881D16900BAEB7A /* WriteCsvFile.swift */; }; - DD882F5D2772E4640005BF05 /* Contacts.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD882F5C2772E4640005BF05 /* Contacts.swift */; }; DD8EBF43285058FA00426DCA /* DisplayConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8EBF42285058FA00426DCA /* DisplayConfig.swift */; }; DD8ED9C52898D51F00B3B0AB /* NetworkConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8ED9C42898D51F00B3B0AB /* NetworkConfig.swift */; }; DD8ED9C8289CE4B900B3B0AB /* RoutingError.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8ED9C7289CE4B900B3B0AB /* RoutingError.swift */; }; @@ -281,7 +280,6 @@ DD86D40B287F401000BAEB7A /* SaveChannelQRCode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SaveChannelQRCode.swift; sourceTree = ""; }; DD86D40E2881BE4C00BAEB7A /* CsvDocument.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CsvDocument.swift; sourceTree = ""; }; DD86D4102881D16900BAEB7A /* WriteCsvFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WriteCsvFile.swift; sourceTree = ""; }; - DD882F5C2772E4640005BF05 /* Contacts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Contacts.swift; sourceTree = ""; }; DD8EBF42285058FA00426DCA /* DisplayConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayConfig.swift; sourceTree = ""; }; DD8ED9C42898D51F00B3B0AB /* NetworkConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkConfig.swift; sourceTree = ""; }; DD8ED9C7289CE4B900B3B0AB /* RoutingError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoutingError.swift; sourceTree = ""; }; @@ -714,7 +712,6 @@ children = ( DDB8F4132A9EE5F000230ECE /* ChannelList.swift */, DD798B062915928D005217CD /* ChannelMessageList.swift */, - DD882F5C2772E4640005BF05 /* Contacts.swift */, DDB8F40F2A9EE5B400230ECE /* Messages.swift */, DDB8F4112A9EE5DD00230ECE /* UserList.swift */, DD1BF2F82776FE2E008C8D2F /* UserMessageList.swift */, @@ -1092,7 +1089,6 @@ DD23A50F26FD1B4400D9B90C /* PeripheralModel.swift in Sources */, DDB6ABDB28B0AC6000384BA1 /* DistanceText.swift in Sources */, DD5E520D298EE33B00D21B61 /* storeforward.pb.swift in Sources */, - DD882F5D2772E4640005BF05 /* Contacts.swift in Sources */, DD964FC2297272AE007C176F /* WaypointEntityExtension.swift in Sources */, 6DA39D8E2A92DC52007E311C /* MeshtasticAppDelegate.swift in Sources */, DD47E3CE26F103C600029299 /* NodeList.swift in Sources */, diff --git a/Meshtastic/Views/Messages/ChannelList.swift b/Meshtastic/Views/Messages/ChannelList.swift index dfbf5e9f..05fae6b7 100644 --- a/Meshtastic/Views/Messages/ChannelList.swift +++ b/Meshtastic/Views/Messages/ChannelList.swift @@ -32,7 +32,7 @@ struct ChannelList: View { ForEach(node!.myInfo!.channels!.array as! [ChannelEntity], id: \.self) { (channel: ChannelEntity) in if channel.name?.lowercased() ?? "" != "admin" && channel.name?.lowercased() ?? "" != "gpio" && channel.name?.lowercased() ?? "" != "serial" { - NavigationLink(destination: ChannelMessageList(channel: channel)) { + NavigationLink(destination: ChannelMessageList(myInfo: node!.myInfo!, channel: channel)) { let mostRecent = channel.allPrivateMessages.last(where: { $0.channel == channel.index }) let lastMessageTime = Date(timeIntervalSince1970: TimeInterval(Int64((mostRecent?.messageTimestamp ?? 0 )))) diff --git a/Meshtastic/Views/Messages/ChannelMessageList.swift b/Meshtastic/Views/Messages/ChannelMessageList.swift index 15336573..5a1924c4 100644 --- a/Meshtastic/Views/Messages/ChannelMessageList.swift +++ b/Meshtastic/Views/Messages/ChannelMessageList.swift @@ -10,6 +10,7 @@ import CoreData struct ChannelMessageList: View { + @StateObject var appState = AppState.shared @Environment(\.managedObjectContext) var context @EnvironmentObject var bleManager: BLEManager @@ -23,7 +24,8 @@ struct ChannelMessageList: View { var maxbytes = 228 @FocusState var focusedField: Field? - @ObservedObject var channel: ChannelEntity + @StateObject var myInfo: MyInfoEntity + @StateObject var channel: ChannelEntity @State var showDeleteMessageAlert = false @State private var deleteMessageId: Int64 = 0 @State private var replyMessageId: Int64 = 0 @@ -232,6 +234,7 @@ struct ChannelMessageList: View { do { try context.save() print("Read message \(message.messageId) ") + appState.unreadChannelMessages = myInfo.unreadMessages } catch { print("Failed to read message \(message.messageId)") } diff --git a/Meshtastic/Views/Messages/Contacts.swift b/Meshtastic/Views/Messages/Contacts.swift deleted file mode 100644 index e3b3de07..00000000 --- a/Meshtastic/Views/Messages/Contacts.swift +++ /dev/null @@ -1,301 +0,0 @@ -// -// Contacts.swift -// MeshtasticApple -// -// Created by Garth Vander Houwen on 12/21/21. -// -// -//import SwiftUI -//import CoreData -// -//struct Contacts: View { -// -// @Environment(\.managedObjectContext) var context -// @EnvironmentObject var bleManager: BLEManager -// -// @FetchRequest( -// sortDescriptors: [NSSortDescriptor(key: "lastMessage", ascending: false), NSSortDescriptor(key: "longName", ascending: true)], -// animation: .default) -// -// private var users: FetchedResults -// @State var node: NodeInfoEntity? -// @State private var userSelection: UserEntity? // Nothing selected by default. -// @State private var channelSelection: ChannelEntity? // Nothing selected by default. -// @State private var isPresentingDeleteChannelMessagesConfirm: Bool = false -// @State private var isPresentingDeleteUserMessagesConfirm: Bool = false -// @State private var isPresentingTraceRouteSentAlert = false -// -// var body: some View { -// -// NavigationSplitView { -// let localeDateFormat = DateFormatter.dateFormat(fromTemplate: "yyMMdd", options: 0, locale: Locale.current) -// let dateFormatString = (localeDateFormat ?? "MM/dd/YY") -// List { -// Section(header: Text("channels")) { -// // Display Contacts for the rest of the non admin channels -// if node != nil && node!.myInfo != nil && node!.myInfo!.channels != nil { -// ForEach(node!.myInfo!.channels!.array as! [ChannelEntity], id: \.self) { (channel: ChannelEntity) in -// if channel.name?.lowercased() ?? "" != "admin" && channel.name?.lowercased() ?? "" != "gpio" && channel.name?.lowercased() ?? "" != "serial" { -// -// NavigationLink(destination: ChannelMessageList(channel: channel)) { -// -// let mostRecent = channel.allPrivateMessages.last(where: { $0.channel == channel.index }) -// let lastMessageTime = Date(timeIntervalSince1970: TimeInterval(Int64((mostRecent?.messageTimestamp ?? 0 )))) -// let lastMessageDay = Calendar.current.dateComponents([.day], from: lastMessageTime).day ?? 0 -// let currentDay = Calendar.current.dateComponents([.day], from: Date()).day ?? 0 -// -// -// ZStack { -// Image(systemName: "circle.fill") -// .opacity(channel.unreadMessages > 0 ? 1 : 0) -// .font(.system(size: 10)) -// .foregroundColor(.accentColor) -// .brightness(0.2) -// } -// CircleText(text: String(channel.index), color: .accentColor, circleSize: 45, fontSize: 40) -// .brightness(0.2) -// -// VStack(alignment: .leading){ -// HStack{ -// if channel.name?.isEmpty ?? false { -// if channel.role == 1 { -// Text(String("PrimaryChannel").camelCaseToWords()) -// } else { -// Text(String("Channel \(channel.index)").camelCaseToWords()) -// } -// } else { -// Text(String(channel.name ?? "Channel \(channel.index)").camelCaseToWords()) -// } -// -// Spacer() -// -// if channel.allPrivateMessages.count > 0 { -// -// if lastMessageDay == currentDay { -// Text(lastMessageTime, style: .time ) -// .font(.system(size: 16)) -// .foregroundColor(.secondary) -// } else if lastMessageDay == (currentDay - 1) { -// Text("Yesterday") -// .font(.system(size: 16)) -// .foregroundColor(.secondary) -// } else if lastMessageDay < (currentDay - 1) && lastMessageDay > (currentDay - 5) { -// Text(lastMessageTime.formattedDate(format: dateFormatString)) -// .font(.system(size: 16)) -// .foregroundColor(.secondary) -// } else if lastMessageDay < (currentDay - 1800) { -// Text(lastMessageTime.formattedDate(format: dateFormatString)) -// .font(.system(size: 16)) -// .foregroundColor(.secondary) -// } -// } -// -//// Image(systemName: "chevron.forward") -//// .font(.caption) -//// .foregroundColor(.secondary) -// } -// -// if channel.allPrivateMessages.count > 0 { -// HStack(alignment: .top) { -// Text("\(mostRecent != nil ? mostRecent!.messagePayload! : " ")") -// .font(.system(size: 16)) -// .foregroundColor(.secondary) -// } -// } -// } -// } -// .frame(height: 62) -// .contextMenu { -// Button { -// channel.mute = !channel.mute -// -// do { -// try context.save() -// // Would rather not do this but the merge changes on -// // A single object is only working on mac GVH -// context.refreshAllObjects() -// // context.refresh(channel, mergeChanges: true) -// } catch { -// context.rollback() -// print("💥 Save Channel Mute Error") -// } -// } label: { -// Label(channel.mute ? "Show Alerts" : "Hide Alerts", systemImage: channel.mute ? "bell" : "bell.slash") -// } -// -// if channel.allPrivateMessages.count > 0 { -// Button(role: .destructive) { -// isPresentingDeleteChannelMessagesConfirm = true -// channelSelection = channel -// } label: { -// Label("Delete Messages", systemImage: "trash") -// } -// } -// } -// .confirmationDialog( -// "This conversation will be deleted.", -// isPresented: $isPresentingDeleteChannelMessagesConfirm, -// titleVisibility: .visible -// ) { -// Button(role: .destructive) { -// deleteChannelMessages(channel: channelSelection!, context: context) -// context.refresh(node!.myInfo!, mergeChanges: true) -// channelSelection = nil -// } label: { -// Text("delete") -// } -// } -// } -// } -// .padding([.top, .bottom]) -// } -// } -// Section(header: Text("direct.messages")) { -// -// ForEach(users) { (user: UserEntity) in -// -// let mostRecent = user.messageList.last -// let lastMessageTime = Date(timeIntervalSince1970: TimeInterval(Int64((mostRecent?.messageTimestamp ?? 0 )))) -// let lastMessageDay = Calendar.current.dateComponents([.day], from: lastMessageTime).day ?? 0 -// let currentDay = Calendar.current.dateComponents([.day], from: Date()).day ?? 0 -// if user.num != bleManager.connectedPeripheral?.num ?? 0 { -// -// NavigationLink(destination: UserMessageList(user: user)) { -// ZStack { -// Image(systemName: "circle.fill") -// .opacity(user.unreadMessages > 0 ? 1 : 0) -// .font(.system(size: 10)) -// .foregroundColor(.accentColor) -// .brightness(0.2) -// } -// -// CircleText(text: user.shortName ?? "?", color: Color(UIColor(hex: UInt32(user.num))), circleSize: 45, fontSize: (user.shortName ?? "?").isEmoji() ? 32 : (user.shortName?.count ?? 0 == 4 ? 14 : (user.shortName?.count ?? 0 == 3 ? 18 : 22)), brightness: 0.0, textColor: UIColor(hex: UInt32(user.num)).isLight() ? .black : .white) -// -// VStack(alignment: .leading){ -// HStack{ -// Text(user.longName ?? "unknown".localized) -// -// Spacer() -// -// if user.messageList.count > 0 { -// if lastMessageDay == currentDay { -// Text(lastMessageTime, style: .time ) -// .font(.system(size: 16)) -// .foregroundColor(.secondary) -// } else if lastMessageDay == (currentDay - 1) { -// Text("Yesterday") -// .font(.system(size: 16)) -// .foregroundColor(.secondary) -// } else if lastMessageDay < (currentDay - 1) && lastMessageDay > (currentDay - 5) { -// Text(lastMessageTime.formattedDate(format: dateFormatString)) -// .font(.system(size: 16)) -// .foregroundColor(.secondary) -// } else if lastMessageDay < (currentDay - 1800) { -// Text(lastMessageTime.formattedDate(format: dateFormatString)) -// .font(.system(size: 16)) -// .foregroundColor(.secondary) -// } -// } -// -//// Image(systemName: "chevron.forward") -//// .font(.caption) -//// .foregroundColor(.secondary) -// } -// -// if user.messageList.count > 0 { -// HStack(alignment: .top) { -// Text("\(mostRecent != nil ? mostRecent!.messagePayload! : " ")") -// .font(.system(size: 16)) -// .foregroundColor(.secondary) -// } -// } -// } -// } -// .frame(height: 62) -// .contextMenu { -// Button { -// user.mute = !user.mute -// do { -// try context.save() -// } catch { -// context.rollback() -// print("💥 Save User Mute Error") -// } -// } label: { -// Label(user.mute ? "Show Alerts" : "Hide Alerts", systemImage: user.mute ? "bell" : "bell.slash") -// } -// Button { -// let success = bleManager.sendTraceRouteRequest(destNum: user.num, wantResponse: true) -// if success { -// isPresentingTraceRouteSentAlert = true -// } -// } label: { -// Label("Trace Route", systemImage: "signpost.right.and.left") -// } -// if user.messageList.count > 0 { -// Button(role: .destructive) { -// isPresentingDeleteUserMessagesConfirm = true -// userSelection = user -// } label: { -// Label("Delete Messages", systemImage: "trash") -// } -// } -// } -// .alert( -// "Trace Route Sent", -// isPresented: $isPresentingTraceRouteSentAlert -// ) { -// Button("OK", role: .cancel) { } -// } -// message: { -// Text("This could take a while, response will appear in the mesh log.") -// } -// .confirmationDialog( -// "This conversation will be deleted.", -// isPresented: $isPresentingDeleteUserMessagesConfirm, -// titleVisibility: .visible -// ) { -// Button(role: .destructive) { -// deleteUserMessages(user: userSelection!, context: context) -// context.refresh(node!.user!, mergeChanges: true) -// } label: { -// Text("delete") -// } -// } -// } -// } -// } -// } -// .listStyle(.grouped) -// .navigationTitle("contacts") -// .navigationBarItems(leading: -// MeshtasticLogo() -// ) -// .onAppear { -// self.bleManager.context = context -// if UserDefaults.preferredPeripheralId.count > 0 { -// let fetchNodeInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") -// fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(bleManager.connectedPeripheral?.num ?? -1)) -// do { -// guard let fetchedNode = try context.fetch(fetchNodeInfoRequest) as? [NodeInfoEntity] else { -// return -// } -// // Found a node, check it for a region -// if !fetchedNode.isEmpty { -// node = fetchedNode[0] -// } -// } catch { -// -// } -// } -// } -// } detail: { -// if let user = userSelection { -// UserMessageList(user: user) -// -// } else { -// Text("select.contact") -// } -// } -// } -//} diff --git a/Meshtastic/Views/Messages/UserMessageList.swift b/Meshtastic/Views/Messages/UserMessageList.swift index e4829d0e..40320633 100644 --- a/Meshtastic/Views/Messages/UserMessageList.swift +++ b/Meshtastic/Views/Messages/UserMessageList.swift @@ -23,7 +23,7 @@ struct UserMessageList: View { var maxbytes = 228 @FocusState var focusedField: Field? // View State Items - @ObservedObject var user: UserEntity + @StateObject var user: UserEntity @State var showDeleteMessageAlert = false @State private var deleteMessageId: Int64 = 0 @State private var replyMessageId: Int64 = 0 diff --git a/Meshtastic/Views/Nodes/NodeDetail.swift b/Meshtastic/Views/Nodes/NodeDetail.swift index fdd32a56..768f172f 100644 --- a/Meshtastic/Views/Nodes/NodeDetail.swift +++ b/Meshtastic/Views/Nodes/NodeDetail.swift @@ -33,7 +33,7 @@ struct NodeDetail: View { canReplaceMapContent: true ) - var node: NodeInfoEntity + @ObservedObject var node: NodeInfoEntity @FetchRequest(sortDescriptors: [NSSortDescriptor(key: "name", ascending: false)], predicate: NSPredicate(