From 7416676c697a8a425b4932f3f5bb235a9849c29b Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sat, 1 Jan 2022 08:03:46 -0800 Subject: [PATCH] Turn new messaging views back on --- .../CoreDataSample.xcdatamodel/contents | 5 +- .../Persistence/Persistence.swift | 4 +- MeshtasticClient/Views/ContentView.swift | 42 +- .../Views/Messages/Channels.swift | 88 +-- .../Views/Messages/Contacts.swift | 277 ++++----- .../Views/Messages/Messages.swift | 408 ++++++------ .../Views/Messages/UserMessageList.swift | 404 ++++++------ MeshtasticClient/Views/Nodes/NodeDetail.swift | 579 +++++++++--------- MeshtasticClient/Views/Nodes/NodeList.swift | 252 ++++---- 9 files changed, 1041 insertions(+), 1018 deletions(-) diff --git a/MeshtasticClient/Meshtastic.xcdatamodeld/CoreDataSample.xcdatamodel/contents b/MeshtasticClient/Meshtastic.xcdatamodeld/CoreDataSample.xcdatamodel/contents index 86be9a38..6860e1c6 100644 --- a/MeshtasticClient/Meshtastic.xcdatamodeld/CoreDataSample.xcdatamodel/contents +++ b/MeshtasticClient/Meshtastic.xcdatamodeld/CoreDataSample.xcdatamodel/contents @@ -65,12 +65,15 @@ + + + - + \ No newline at end of file diff --git a/MeshtasticClient/Persistence/Persistence.swift b/MeshtasticClient/Persistence/Persistence.swift index 43b77da3..d8b6a973 100644 --- a/MeshtasticClient/Persistence/Persistence.swift +++ b/MeshtasticClient/Persistence/Persistence.swift @@ -34,14 +34,16 @@ class PersistenceController { init(inMemory: Bool = false) { container = NSPersistentContainer(name: "Meshtastic") + if inMemory { container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null") } + container.loadPersistentStores(completionHandler: { (_, error) in // Merge policy that favors in memory data over data in the db self.container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy - //self.container.viewContext.automaticallyMergesChangesFromParent = true + self.container.viewContext.automaticallyMergesChangesFromParent = true if let error = error as NSError? { diff --git a/MeshtasticClient/Views/ContentView.swift b/MeshtasticClient/Views/ContentView.swift index d0ba3a04..327c55ef 100644 --- a/MeshtasticClient/Views/ContentView.swift +++ b/MeshtasticClient/Views/ContentView.swift @@ -19,21 +19,21 @@ struct ContentView: View { var body: some View { TabView(selection: $selection) { -// Contacts() -// .tabItem { -// Label("Messages", systemImage: "text.bubble") -// .symbolRenderingMode(.hierarchical) -// .symbolVariant(.none) -// -// } -// .tag(Tab.contacts) - Channels() - .tabItem { - Label("Messages", systemImage: "text.bubble") + Contacts() + .tabItem { + Label("Messages", systemImage: "text.bubble") .symbolRenderingMode(.hierarchical) - .symbolVariant(.none) - } - .tag(Tab.messages) + .symbolVariant(.none) + + } + .tag(Tab.contacts) +// Channels() +// .tabItem { +// Label("Messages", systemImage: "text.bubble") +// .symbolRenderingMode(.hierarchical) +// .symbolVariant(.none) +// } +// .tag(Tab.messages) Connect() .tabItem { Label("Bluetooth", systemImage: "dot.radiowaves.left.and.right") @@ -41,13 +41,13 @@ struct ContentView: View { .symbolVariant(.none) } .tag(Tab.ble) - NodeList() - .tabItem { - Label("Nodes", systemImage: "flipphone") - .symbolRenderingMode(.hierarchical) - .symbolVariant(.none) - } - .tag(Tab.nodes) +// NodeList() +// .tabItem { +// Label("Nodes", systemImage: "flipphone") +// .symbolRenderingMode(.hierarchical) +// .symbolVariant(.none) +// } +// .tag(Tab.nodes) NodeMap() .tabItem { Label("Mesh Map", systemImage: "map") diff --git a/MeshtasticClient/Views/Messages/Channels.swift b/MeshtasticClient/Views/Messages/Channels.swift index 5055f10a..3068fa13 100644 --- a/MeshtasticClient/Views/Messages/Channels.swift +++ b/MeshtasticClient/Views/Messages/Channels.swift @@ -1,44 +1,44 @@ -import Foundation -import SwiftUI -import CoreBluetooth - -struct Channels: View { - - @State private var isShowingDetailView = true - - var body: some View { - - NavigationView { - - NavigationLink(destination: Messages(), isActive: $isShowingDetailView) { - - List { - - HStack { - - Image(systemName: "megaphone.fill") - .font(.largeTitle) - .symbolRenderingMode(.hierarchical) - .padding(.trailing) - .foregroundColor(.accentColor) - - Text("All - Broadcast") - .font(.largeTitle) - - }.padding() - } - } - .navigationTitle("Contacts") - } - .navigationViewStyle(DoubleColumnNavigationViewStyle()) - } -} - -struct MessageList_Previews: PreviewProvider { - - static var previews: some View { - Group { - Channels() - } - } -} +//import Foundation +//import SwiftUI +//import CoreBluetooth +// +//struct Channels: View { +// +// @State private var isShowingDetailView = true +// +// var body: some View { +// +// NavigationView { +// +// NavigationLink(destination: Messages(), isActive: $isShowingDetailView) { +// +// List { +// +// HStack { +// +// Image(systemName: "megaphone.fill") +// .font(.largeTitle) +// .symbolRenderingMode(.hierarchical) +// .padding(.trailing) +// .foregroundColor(.accentColor) +// +// Text("All - Broadcast") +// .font(.largeTitle) +// +// }.padding() +// } +// } +// .navigationTitle("Contacts") +// } +// .navigationViewStyle(DoubleColumnNavigationViewStyle()) +// } +//} +// +//struct MessageList_Previews: PreviewProvider { +// +// static var previews: some View { +// Group { +// Channels() +// } +// } +//} diff --git a/MeshtasticClient/Views/Messages/Contacts.swift b/MeshtasticClient/Views/Messages/Contacts.swift index 734fec19..84e627a7 100644 --- a/MeshtasticClient/Views/Messages/Contacts.swift +++ b/MeshtasticClient/Views/Messages/Contacts.swift @@ -1,141 +1,142 @@ -//// -//// Contacts.swift -//// MeshtasticClient -//// -//// Created by Garth Vander Houwen on 12/21/21. -//// // -//import SwiftUI +// Contacts.swift +// MeshtasticClient // -//struct Contacts: View { +// Created by Garth Vander Houwen on 12/21/21. // -// @Environment(\.managedObjectContext) var context -// @EnvironmentObject var bleManager: BLEManager -// -// @FetchRequest( -// sortDescriptors: [NSSortDescriptor(key: "longName", ascending: true)], -// animation: .default) -// -// private var users: FetchedResults -// -// var body: some View { -// -// NavigationView { -// -// List(users) { (user: UserEntity) in -// -// if user.receivedMessages?.count ?? 0 > 0 { -// -// let currentUserNum = self.bleManager.connectedPeripheral != nil ? self.bleManager.connectedPeripheral.num : 0 -// -// let mostRecentBC = user.receivedMessages?.array.last as! MessageEntity -// -// let mostRecentDM = user.receivedMessages?.array.last(where: {($0 as! MessageEntity).toUser!.num == currentUserNum }) as? MessageEntity -// -// let lastMessageTime = Date(timeIntervalSince1970: TimeInterval(Int64(mostRecentDM?.messageTimestamp ?? mostRecentBC.messageTimestamp))) -// let lastMessageDay = Calendar.current.dateComponents([.day], from: lastMessageTime).day ?? 0 -// let currentDay = Calendar.current.dateComponents([.day], from: Date()).day ?? 0 -// -// if user.num == bleManager.broadcastNodeNum {//user.num != currentUserNum && (user.num == bleManager.broadcastNodeNum || mostRecentDM != nil) { -// -// NavigationLink(destination: UserMessageList(user: user)) { -// -// HStack { -// -// VStack { -// -// CircleText(text: user.shortName ?? "???", color: Color.blue) -// } -// .padding([.leading, .trailing]) -// -// VStack { -// -// HStack { -// -// VStack { -// -// Text(user.longName ?? "Unknown").font(.headline).fixedSize() -// } -// -// VStack { -// -// if lastMessageDay == currentDay { -// -// Text(lastMessageTime, style: .time ) -// .font(.caption) -// .foregroundColor(.gray) -// -// } else if lastMessageDay == (currentDay - 1) { -// -// Text("Yesterday") -// .font(.callout) -// .foregroundColor(.gray) -// -// } else if lastMessageDay < (currentDay - 1) && lastMessageDay > (currentDay - 5) { -// -// Text(lastMessageTime, style: .date) -// -// } else { -// -// Text(lastMessageTime, style: .date) -// } -// }.frame(maxWidth: .infinity, alignment: .trailing) -// } -// .listRowSeparator(.hidden).frame(height: 5) -// -// HStack(alignment: .top) { -// Text(mostRecentDM != nil ? mostRecentDM?.messagePayload as! String : (mostRecentBC.messagePayload ?? "Unknown" )) -// .frame(height: 60) -// .truncationMode(.tail) -// .foregroundColor(Color.gray) -// .frame(maxWidth: .infinity, alignment: .leading) -// } -// }.padding(.top, 15) -// } -// } -// } -// -// } else if false {// self.bleManager.connectedPeripheral == nil || ((self.bleManager.connectedPeripheral != nil ? self.bleManager.connectedPeripheral.num : 0) != user.num) { -// -// NavigationLink(destination: UserMessageList(user: user)) { -// -// HStack { -// -// VStack { -// -// CircleText(text: user.shortName ?? "???", color: Color.blue) -// } -// .padding(.trailing) -// -// VStack { -// -// HStack { -// -// VStack { -// -// Text(user.longName ?? "Unknown").font(.headline).fixedSize() -// } -// -// VStack { -// Text(" ") -// } -// .frame(maxWidth: .infinity, alignment: .trailing) -// } -// .listRowSeparator(.hidden).frame(height: 5) -// } -// }.padding() -// } -// } -// } -// .navigationTitle("Contacts") -// .navigationBarTitleDisplayMode(.inline) -// } -// .listStyle(PlainListStyle()) -// } -//} -// -//struct Contacts_Previews: PreviewProvider { -// static var previews: some View { -// Contacts() -// } -//} + +import SwiftUI + +struct Contacts: View { + + @Environment(\.managedObjectContext) var context + @EnvironmentObject var bleManager: BLEManager + + @FetchRequest( + sortDescriptors: [NSSortDescriptor(key: "longName", ascending: true)], + animation: .default) + + private var users: FetchedResults + + var body: some View { + + NavigationView { + + List(users) { (user: UserEntity) in + + if user.receivedMessages?.count ?? 0 > 0 { + + let currentUserNum = self.bleManager.connectedPeripheral != nil ? self.bleManager.connectedPeripheral.num : 0 + + let mostRecentBC = user.receivedMessages?.array.last as! MessageEntity + + let mostRecentDM = user.receivedMessages?.array.last(where: {($0 as! MessageEntity).toUser!.num == currentUserNum }) as? MessageEntity + + let lastMessageTime = Date(timeIntervalSince1970: TimeInterval(Int64(mostRecentDM?.messageTimestamp ?? mostRecentBC.messageTimestamp))) + let lastMessageDay = Calendar.current.dateComponents([.day], from: lastMessageTime).day ?? 0 + let currentDay = Calendar.current.dateComponents([.day], from: Date()).day ?? 0 + + if user.num == bleManager.broadcastNodeNum {//user.num != currentUserNum && (user.num == bleManager.broadcastNodeNum || mostRecentDM != nil) { + + NavigationLink(destination: UserMessageList(user: user) + .environment(\.managedObjectContext, self.context)) { + + HStack { + + VStack { + + CircleText(text: user.shortName ?? "???", color: Color.blue) + } + .padding([.leading, .trailing]) + + VStack { + + HStack { + + VStack { + + Text(user.longName ?? "Unknown").font(.headline).fixedSize() + } + + VStack { + + if lastMessageDay == currentDay { + + Text(lastMessageTime, style: .time ) + .font(.caption) + .foregroundColor(.gray) + + } else if lastMessageDay == (currentDay - 1) { + + Text("Yesterday") + .font(.callout) + .foregroundColor(.gray) + + } else if lastMessageDay < (currentDay - 1) && lastMessageDay > (currentDay - 5) { + + Text(lastMessageTime, style: .date) + + } else { + + Text(lastMessageTime, style: .date) + } + }.frame(maxWidth: .infinity, alignment: .trailing) + } + .listRowSeparator(.hidden).frame(height: 5) + + HStack(alignment: .top) { + Text(mostRecentDM != nil ? mostRecentDM?.messagePayload as! String : (mostRecentBC.messagePayload ?? "Unknown" )) + .frame(height: 60) + .truncationMode(.tail) + .foregroundColor(Color.gray) + .frame(maxWidth: .infinity, alignment: .leading) + } + }.padding(.top, 15) + } + } + } + + } else if false {// self.bleManager.connectedPeripheral == nil || ((self.bleManager.connectedPeripheral != nil ? self.bleManager.connectedPeripheral.num : 0) != user.num) { + + NavigationLink(destination: UserMessageList(user: user)) { + + HStack { + + VStack { + + CircleText(text: user.shortName ?? "???", color: Color.blue) + } + .padding(.trailing) + + VStack { + + HStack { + + VStack { + + Text(user.longName ?? "Unknown").font(.headline).fixedSize() + } + + VStack { + Text(" ") + } + .frame(maxWidth: .infinity, alignment: .trailing) + } + .listRowSeparator(.hidden).frame(height: 5) + } + }.padding() + } + } + } + .navigationTitle("Contacts") + .navigationBarTitleDisplayMode(.inline) + } + .listStyle(PlainListStyle()) + } +} + +struct Contacts_Previews: PreviewProvider { + static var previews: some View { + Contacts() + } +} diff --git a/MeshtasticClient/Views/Messages/Messages.swift b/MeshtasticClient/Views/Messages/Messages.swift index 68cdfb72..6ffe5aae 100644 --- a/MeshtasticClient/Views/Messages/Messages.swift +++ b/MeshtasticClient/Views/Messages/Messages.swift @@ -1,204 +1,204 @@ -import SwiftUI -import MapKit -import Foundation -import CoreLocation - -struct Messages: View { - - enum Field: Hashable { - case messageText - } - - // CoreData - @Environment(\.managedObjectContext) var context - @EnvironmentObject var bleManager: BLEManager - - @FetchRequest( - sortDescriptors: [NSSortDescriptor(keyPath: \MessageEntity.messageTimestamp, ascending: true)], - animation: .default) - var messages: FetchedResults - - // Keyboard State - @State var typingMessage: String = "" - @State private var totalBytes = 0 - private var maxbytes = 228 - @State private var lastTypingMessage = "" - @FocusState private var focusedField: Field? - - @State var showDeleteMessageAlert = false - @State private var deleteMessageId: Int64 = 0 - - public var broadcastNodeId: UInt32 = 4294967295 - - var body: some View { - - Text("\(messages.count) Messages").font(.caption) - GeometryReader { bounds in - - VStack { - - ScrollViewReader { scrollView in - - if self.messages.count > 0 { - - ScrollView { - - ForEach(messages) { message in - - HStack(alignment: .top) { - let currentUser: Bool = (bleManager.connectedPeripheral == nil) ? false : ((bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.num == message.fromUser?.num) ? true : false ) - - CircleText(text: (message.fromUser?.shortName ?? "???"), color: currentUser ? .accentColor : Color(.darkGray)).padding(.all, 5) - .gesture(LongPressGesture(minimumDuration: 2) - .onEnded {_ in - print(messages) - print("I want to delete message: \(message.messageId)") - self.showDeleteMessageAlert = true - self.deleteMessageId = message.messageId - - print(deleteMessageId) - }) - - VStack(alignment: .leading) { - Text(message.messagePayload ?? "EMPTY MESSAGE") - .textSelection(.enabled) - .padding(10) - .foregroundColor(.white) - .background(currentUser ? Color.blue : Color(.darkGray)) - .cornerRadius(10) - HStack(spacing: 4) { - - let time = Int32(message.messageTimestamp) - let messageDate = Date(timeIntervalSince1970: TimeInterval(time)) - - if time != 0 { - Text(messageDate, style: .date).font(.caption2).foregroundColor(.gray) - Text(messageDate, style: .time).font(.caption2).foregroundColor(.gray) - } else { - Text("Unknown").font(.caption2).foregroundColor(.gray) - } - } - .padding(.bottom, 10) - } - Spacer() - } - .alert(isPresented: $showDeleteMessageAlert) { - Alert(title: Text("Are you sure you want to delete this message?"), message: Text("This action is permanent."), - primaryButton: .destructive(Text("Delete")) { - print("OK button tapped") - if deleteMessageId > 0 { - - let message = messages.first(where: { $0.messageId == deleteMessageId }) - - context.delete(message!) - do { - try context.save() - - deleteMessageId = 0 - - } catch { - print("Failed to delete message \(deleteMessageId)") - } - - } - }, - secondaryButton: .cancel() - ) - } - } - .onChange(of: messages.count, perform: { newValue in - - if messages.count > 0 { - - scrollView.scrollTo(messages[messages.count-1].id, anchor: .bottom) - } - } - ) - .onAppear(perform: { - - self.bleManager.context = context - if messages.count > 0 { - - scrollView.scrollTo(messages[messages.count-1].id, anchor: .bottom) - } - }) - } - .padding(.horizontal) - } - } - - HStack(alignment: .top) { - - ZStack { - - let kbType = UIKeyboardType(rawValue: UserDefaults.standard.object(forKey: "keyboardType") as? Int ?? 0) - TextEditor(text: $typingMessage) - .onChange(of: typingMessage, perform: { value in - - let size = value.utf8.count - totalBytes = size - if totalBytes <= maxbytes { - // Allow the user to type - lastTypingMessage = typingMessage - } else { - // Set the message back and remove the bytes over the count - self.typingMessage = lastTypingMessage - } - }) - .keyboardType(kbType!) - .toolbar { - ToolbarItemGroup(placement: .keyboard) { - - Button("Dismiss Keyboard") { - focusedField = nil - } - .font(.subheadline) - - Spacer() - - ProgressView("Bytes: \(totalBytes) / \(maxbytes)", value: Double(totalBytes), total: Double(maxbytes)) - .frame(width: 130) - .padding(5) - .font(.subheadline) - .accentColor(.accentColor) - } - } - .padding(.horizontal, 8) - .focused($focusedField, equals: .messageText) - .multilineTextAlignment(.leading) - .frame(minHeight: bounds.size.height / 4, maxHeight: bounds.size.height / 4) - - Text(typingMessage).opacity(0).padding(.all, 0) - - } - .overlay(RoundedRectangle(cornerRadius: 20).stroke(.tertiary, lineWidth: 1)) - .padding(.bottom, 15) - - Button(action: { - if self.bleManager.sendMessage(message: typingMessage, toUserNum: Int64(self.bleManager.broadcastNodeNum)) { - typingMessage = "" - focusedField = nil - } - - }) { - Image(systemName: "arrow.up.circle.fill").font(.largeTitle).foregroundColor(.blue) - } - - } - .padding(.all, 15) - } - } - .navigationTitle("All - Broadcast") - .navigationBarTitleDisplayMode(.inline) - .navigationBarItems(trailing: - - ZStack { - - ConnectedDevice( - bluetoothOn: self.bleManager.isSwitchedOn, - deviceConnected: self.bleManager.connectedPeripheral != nil, - name: (self.bleManager.connectedPeripheral != nil) ? self.bleManager.connectedPeripheral.shortName : "???") - } - ) - } -} +//import SwiftUI +//import MapKit +//import Foundation +//import CoreLocation +// +//struct Messages: View { +// +// enum Field: Hashable { +// case messageText +// } +// +// // CoreData +// @Environment(\.managedObjectContext) var context +// @EnvironmentObject var bleManager: BLEManager +// +// @FetchRequest( +// sortDescriptors: [NSSortDescriptor(keyPath: \MessageEntity.messageTimestamp, ascending: true)], +// animation: .default) +// var messages: FetchedResults +// +// // Keyboard State +// @State var typingMessage: String = "" +// @State private var totalBytes = 0 +// private var maxbytes = 228 +// @State private var lastTypingMessage = "" +// @FocusState private var focusedField: Field? +// +// @State var showDeleteMessageAlert = false +// @State private var deleteMessageId: Int64 = 0 +// +// public var broadcastNodeId: UInt32 = 4294967295 +// +// var body: some View { +// +// Text("\(messages.count) Messages").font(.caption) +// GeometryReader { bounds in +// +// VStack { +// +// ScrollViewReader { scrollView in +// +// if self.messages.count > 0 { +// +// ScrollView { +// +// ForEach(messages) { message in +// +// HStack(alignment: .top) { +// let currentUser: Bool = (bleManager.connectedPeripheral == nil) ? false : ((bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.num == message.fromUser?.num) ? true : false ) +// +// CircleText(text: (message.fromUser?.shortName ?? "???"), color: currentUser ? .accentColor : Color(.darkGray)).padding(.all, 5) +// .gesture(LongPressGesture(minimumDuration: 2) +// .onEnded {_ in +// print(messages) +// print("I want to delete message: \(message.messageId)") +// self.showDeleteMessageAlert = true +// self.deleteMessageId = message.messageId +// +// print(deleteMessageId) +// }) +// +// VStack(alignment: .leading) { +// Text(message.messagePayload ?? "EMPTY MESSAGE") +// .textSelection(.enabled) +// .padding(10) +// .foregroundColor(.white) +// .background(currentUser ? Color.blue : Color(.darkGray)) +// .cornerRadius(10) +// HStack(spacing: 4) { +// +// let time = Int32(message.messageTimestamp) +// let messageDate = Date(timeIntervalSince1970: TimeInterval(time)) +// +// if time != 0 { +// Text(messageDate, style: .date).font(.caption2).foregroundColor(.gray) +// Text(messageDate, style: .time).font(.caption2).foregroundColor(.gray) +// } else { +// Text("Unknown").font(.caption2).foregroundColor(.gray) +// } +// } +// .padding(.bottom, 10) +// } +// Spacer() +// } +// .alert(isPresented: $showDeleteMessageAlert) { +// Alert(title: Text("Are you sure you want to delete this message?"), message: Text("This action is permanent."), +// primaryButton: .destructive(Text("Delete")) { +// print("OK button tapped") +// if deleteMessageId > 0 { +// +// let message = messages.first(where: { $0.messageId == deleteMessageId }) +// +// context.delete(message!) +// do { +// try context.save() +// +// deleteMessageId = 0 +// +// } catch { +// print("Failed to delete message \(deleteMessageId)") +// } +// +// } +// }, +// secondaryButton: .cancel() +// ) +// } +// } +// .onChange(of: messages.count, perform: { newValue in +// +// if messages.count > 0 { +// +// scrollView.scrollTo(messages[messages.count-1].id, anchor: .bottom) +// } +// } +// ) +// .onAppear(perform: { +// +// self.bleManager.context = context +// if messages.count > 0 { +// +// scrollView.scrollTo(messages[messages.count-1].id, anchor: .bottom) +// } +// }) +// } +// .padding(.horizontal) +// } +// } +// +// HStack(alignment: .top) { +// +// ZStack { +// +// let kbType = UIKeyboardType(rawValue: UserDefaults.standard.object(forKey: "keyboardType") as? Int ?? 0) +// TextEditor(text: $typingMessage) +// .onChange(of: typingMessage, perform: { value in +// +// let size = value.utf8.count +// totalBytes = size +// if totalBytes <= maxbytes { +// // Allow the user to type +// lastTypingMessage = typingMessage +// } else { +// // Set the message back and remove the bytes over the count +// self.typingMessage = lastTypingMessage +// } +// }) +// .keyboardType(kbType!) +// .toolbar { +// ToolbarItemGroup(placement: .keyboard) { +// +// Button("Dismiss Keyboard") { +// focusedField = nil +// } +// .font(.subheadline) +// +// Spacer() +// +// ProgressView("Bytes: \(totalBytes) / \(maxbytes)", value: Double(totalBytes), total: Double(maxbytes)) +// .frame(width: 130) +// .padding(5) +// .font(.subheadline) +// .accentColor(.accentColor) +// } +// } +// .padding(.horizontal, 8) +// .focused($focusedField, equals: .messageText) +// .multilineTextAlignment(.leading) +// .frame(minHeight: bounds.size.height / 4, maxHeight: bounds.size.height / 4) +// +// Text(typingMessage).opacity(0).padding(.all, 0) +// +// } +// .overlay(RoundedRectangle(cornerRadius: 20).stroke(.tertiary, lineWidth: 1)) +// .padding(.bottom, 15) +// +// Button(action: { +// if self.bleManager.sendMessage(message: typingMessage, toUserNum: Int64(self.bleManager.broadcastNodeNum)) { +// typingMessage = "" +// focusedField = nil +// } +// +// }) { +// Image(systemName: "arrow.up.circle.fill").font(.largeTitle).foregroundColor(.blue) +// } +// +// } +// .padding(.all, 15) +// } +// } +// .navigationTitle("All - Broadcast") +// .navigationBarTitleDisplayMode(.inline) +// .navigationBarItems(trailing: +// +// ZStack { +// +// ConnectedDevice( +// bluetoothOn: self.bleManager.isSwitchedOn, +// deviceConnected: self.bleManager.connectedPeripheral != nil, +// name: (self.bleManager.connectedPeripheral != nil) ? self.bleManager.connectedPeripheral.shortName : "???") +// } +// ) +// } +//} diff --git a/MeshtasticClient/Views/Messages/UserMessageList.swift b/MeshtasticClient/Views/Messages/UserMessageList.swift index 3162845a..b8393215 100644 --- a/MeshtasticClient/Views/Messages/UserMessageList.swift +++ b/MeshtasticClient/Views/Messages/UserMessageList.swift @@ -1,206 +1,206 @@ -//// -//// UserMessageList.swift -//// MeshtasticClient -//// -//// Created by Garth Vander Houwen on 12/24/21. -//// // -//import SwiftUI -//import CoreData +// UserMessageList.swift +// MeshtasticClient // -//struct UserMessageList: View { +// Created by Garth Vander Houwen on 12/24/21. // -// @Environment(\.managedObjectContext) var context -// @EnvironmentObject var bleManager: BLEManager -// -// enum Field: Hashable { -// case messageText -// } -// // Keyboard State -// @State var typingMessage: String = "" -// @State private var totalBytes = 0 -// var maxbytes = 228 -// @State var lastTypingMessage = "" -// @FocusState var focusedField: Field? -// -// var user: UserEntity -// -// @State var showDeleteMessageAlert = false -// @State private var deleteMessageId: Int64 = 0 -// -// var body: some View { -// -//// HStack { -// -// VStack { -// -// // List { -// -// ScrollViewReader { _ in -// -// ScrollView { -// -// if user.receivedMessages?.count ?? 0 > 0 { -// -// ForEach( user.receivedMessages?.array as! [MessageEntity], id: \.self) { (message: MessageEntity) in -// -// // HStack { -// let currentUser: Bool = (bleManager.connectedPeripheral == nil) ? false : ((bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.num == message.fromUser?.num) ? true : false ) -// -// -// if message.toUser!.num == Int64(bleManager.broadcastNodeNum) || ((bleManager.connectedPeripheral) != nil && bleManager.connectedPeripheral.num == message.fromUser?.num) ? true : true { -// -// -// HStack (alignment: .top) { -// -// if currentUser { Spacer(minLength:50) } -// -// if !currentUser { -// -// CircleText(text: (message.fromUser?.shortName ?? "???"), color: currentUser ? .accentColor : Color(.darkGray)).padding(.all, 5) -// .gesture(LongPressGesture(minimumDuration: 2).onEnded {_ in -// -// print("I want to delete message: \(message.messageId)") -// self.showDeleteMessageAlert = true -// self.deleteMessageId = message.messageId -// print(deleteMessageId) -// }) -// } -// -// VStack(alignment: currentUser ? .trailing : .leading) { -// -// Text(message.messagePayload ?? "EMPTY MESSAGE") -// .textSelection(.enabled) -// .padding(10) -// .foregroundColor(.white) -// .background(currentUser ? Color.blue : Color(.darkGray)) -// .cornerRadius(15) -// -// HStack(spacing: 4) { -// -// let time = Int32(message.messageTimestamp) -// let messageDate = Date(timeIntervalSince1970: TimeInterval(time)) -// -// if time != 0 { -// Text(messageDate, style: .date).font(.caption2).foregroundColor(.gray) -// Text(messageDate, style: .time).font(.caption2).foregroundColor(.gray) -// } else { -// Text("Unknown").font(.caption2).foregroundColor(.gray) -// } -// } -// .padding(.bottom, 10) -// } -// if !currentUser { -// Spacer(minLength:50) -// } -// } -// .padding(.trailing) -// .frame(maxWidth: .infinity) -// } -// // } -// } -// .listRowSeparator(.hidden) -// } -// } -// } -// // } -// // .padding(.top) -// -// HStack(alignment: .top) { -// -// ZStack { -// -// let kbType = UIKeyboardType(rawValue: UserDefaults.standard.object(forKey: "keyboardType") as? Int ?? 0) -// TextEditor(text: $typingMessage) -// .onChange(of: typingMessage, perform: { value in -// -// let size = value.utf8.count -// totalBytes = size -// if totalBytes <= maxbytes { -// // Allow the user to type -// lastTypingMessage = typingMessage -// } else { -// // Set the message back and remove the bytes over the count -// self.typingMessage = lastTypingMessage -// } -// }) -// .keyboardType(kbType!) -// .toolbar { -// ToolbarItemGroup(placement: .keyboard) { -// -// Button("Dismiss Keyboard") { -// focusedField = nil -// } -// .font(.subheadline) -// -// Spacer() -// -// ProgressView("Bytes: \(totalBytes) / \(maxbytes)", value: Double(totalBytes), total: Double(maxbytes)) -// .frame(width: 130) -// .padding(5) -// .font(.subheadline) -// .accentColor(.accentColor) -// } -// } -// .padding(.horizontal, 8) -// .focused($focusedField, equals: .messageText) -// .multilineTextAlignment(.leading) -// .frame(minHeight: 100, maxHeight: 160) -// -// Text(typingMessage).opacity(0).padding(.all, 0) -// -// } -// .overlay(RoundedRectangle(cornerRadius: 20).stroke(.tertiary, lineWidth: 1)) -// .padding(.bottom, 15) -// -// Button(action: { -// if bleManager.sendMessage(message: typingMessage, toUserNum: user.num) { -// typingMessage = "" -// focusedField = nil -// } else { -// -// _ = Timer.scheduledTimer(withTimeInterval: 3.0, repeats: false) { (_) in -// -// if bleManager.sendMessage(message: typingMessage, toUserNum: user.num) { -// typingMessage = "" -// } -// } -// } -// -// }) { -// Image(systemName: "arrow.up.circle.fill").font(.largeTitle).foregroundColor(.blue) -// } -// -// } -// .padding(.all, 15) -// } -//// } -// .navigationViewStyle(.stack) -// .navigationBarTitleDisplayMode(.inline) -// .toolbar { -// ToolbarItem(placement: .principal) { -// -// HStack { -// -// CircleText(text: user.shortName ?? "???", color: .blue).fixedSize() -// Text(user.longName ?? "Unknown").foregroundColor(.gray).font(.caption2).fixedSize() -// } -// } -// ToolbarItem(placement: .navigationBarTrailing) { -// ZStack { -// -// ConnectedDevice( -// bluetoothOn: bleManager.isSwitchedOn, -// deviceConnected: bleManager.connectedPeripheral != nil, -// name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "???") -// } -// } + +import SwiftUI +import CoreData + +struct UserMessageList: View { + + @Environment(\.managedObjectContext) var context + @EnvironmentObject var bleManager: BLEManager + + enum Field: Hashable { + case messageText + } + // Keyboard State + @State var typingMessage: String = "" + @State private var totalBytes = 0 + var maxbytes = 228 + @State var lastTypingMessage = "" + @FocusState var focusedField: Field? + + var user: UserEntity + + @State var showDeleteMessageAlert = false + @State private var deleteMessageId: Int64 = 0 + + var body: some View { + +// HStack { + + VStack { + + // List { + + ScrollViewReader { _ in + + ScrollView { + + if user.receivedMessages?.count ?? 0 > 0 { + + ForEach( user.receivedMessages?.array as! [MessageEntity], id: \.self) { (message: MessageEntity) in + + // HStack { + let currentUser: Bool = (bleManager.connectedPeripheral == nil) ? false : ((bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.num == message.fromUser?.num) ? true : false ) + + + if message.toUser!.num == Int64(bleManager.broadcastNodeNum) || ((bleManager.connectedPeripheral) != nil && bleManager.connectedPeripheral.num == message.fromUser?.num) ? true : true { + + + HStack (alignment: .top) { + + if currentUser { Spacer(minLength:50) } + + if !currentUser { + + CircleText(text: (message.fromUser?.shortName ?? "???"), color: currentUser ? .accentColor : Color(.darkGray)).padding(.all, 5) + .gesture(LongPressGesture(minimumDuration: 2).onEnded {_ in + + print("I want to delete message: \(message.messageId)") + self.showDeleteMessageAlert = true + self.deleteMessageId = message.messageId + print(deleteMessageId) + }) + } + + VStack(alignment: currentUser ? .trailing : .leading) { + + Text(message.messagePayload ?? "EMPTY MESSAGE") + .textSelection(.enabled) + .padding(10) + .foregroundColor(.white) + .background(currentUser ? Color.blue : Color(.darkGray)) + .cornerRadius(15) + + HStack(spacing: 4) { + + let time = Int32(message.messageTimestamp) + let messageDate = Date(timeIntervalSince1970: TimeInterval(time)) + + if time != 0 { + Text(messageDate, style: .date).font(.caption2).foregroundColor(.gray) + Text(messageDate, style: .time).font(.caption2).foregroundColor(.gray) + } else { + Text("Unknown").font(.caption2).foregroundColor(.gray) + } + } + .padding(.bottom, 10) + } + if !currentUser { + Spacer(minLength:50) + } + } + .padding(.trailing) + .frame(maxWidth: .infinity) + } + // } + } + .listRowSeparator(.hidden) + } + } + } + // } + // .padding(.top) + + HStack(alignment: .top) { + + ZStack { + + let kbType = UIKeyboardType(rawValue: UserDefaults.standard.object(forKey: "keyboardType") as? Int ?? 0) + TextEditor(text: $typingMessage) + .onChange(of: typingMessage, perform: { value in + + let size = value.utf8.count + totalBytes = size + if totalBytes <= maxbytes { + // Allow the user to type + lastTypingMessage = typingMessage + } else { + // Set the message back and remove the bytes over the count + self.typingMessage = lastTypingMessage + } + }) + .keyboardType(kbType!) + .toolbar { + ToolbarItemGroup(placement: .keyboard) { + + Button("Dismiss Keyboard") { + focusedField = nil + } + .font(.subheadline) + + Spacer() + + ProgressView("Bytes: \(totalBytes) / \(maxbytes)", value: Double(totalBytes), total: Double(maxbytes)) + .frame(width: 130) + .padding(5) + .font(.subheadline) + .accentColor(.accentColor) + } + } + .padding(.horizontal, 8) + .focused($focusedField, equals: .messageText) + .multilineTextAlignment(.leading) + .frame(minHeight: 100, maxHeight: 160) + + Text(typingMessage).opacity(0).padding(.all, 0) + + } + .overlay(RoundedRectangle(cornerRadius: 20).stroke(.tertiary, lineWidth: 1)) + .padding(.bottom, 15) + + Button(action: { + if bleManager.sendMessage(message: typingMessage, toUserNum: user.num) { + typingMessage = "" + focusedField = nil + } else { + + _ = Timer.scheduledTimer(withTimeInterval: 3.0, repeats: false) { (_) in + + if bleManager.sendMessage(message: typingMessage, toUserNum: user.num) { + typingMessage = "" + } + } + } + + }) { + Image(systemName: "arrow.up.circle.fill").font(.largeTitle).foregroundColor(.blue) + } + + } + .padding(.all, 15) + } // } -// .onAppear(perform: { -// -// self.bleManager.context = context -// -// }) -// } -// -//} + .navigationViewStyle(.stack) + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .principal) { + + HStack { + + CircleText(text: user.shortName ?? "???", color: .blue).fixedSize() + Text(user.longName ?? "Unknown").foregroundColor(.gray).font(.caption2).fixedSize() + } + } + ToolbarItem(placement: .navigationBarTrailing) { + ZStack { + + ConnectedDevice( + bluetoothOn: bleManager.isSwitchedOn, + deviceConnected: bleManager.connectedPeripheral != nil, + name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "???") + } + } + } + .onAppear(perform: { + + self.bleManager.context = context + + }) + } + +} diff --git a/MeshtasticClient/Views/Nodes/NodeDetail.swift b/MeshtasticClient/Views/Nodes/NodeDetail.swift index 90374520..e5bb21db 100644 --- a/MeshtasticClient/Views/Nodes/NodeDetail.swift +++ b/MeshtasticClient/Views/Nodes/NodeDetail.swift @@ -1,281 +1,298 @@ -/* -Abstract: -A view showing the details for a node. -*/ - -import SwiftUI -import MapKit -import CoreLocation - -struct NodeDetail: View { - - @Environment(\.managedObjectContext) var context - @EnvironmentObject var bleManager: BLEManager - - var node: NodeInfoEntity - - var body: some View { - - HStack { - - GeometryReader { bounds in - - VStack { - - if node.positions?.count ?? 0 > 0 { - - let mostRecent = node.positions?.lastObject as! PositionEntity - - if mostRecent.coordinate != nil { - - let nodeCoordinatePosition = CLLocationCoordinate2D(latitude: mostRecent.latitude!, longitude: mostRecent.longitude!) - - let regionBinding = Binding( - get: { - MKCoordinateRegion(center: nodeCoordinatePosition, span: MKCoordinateSpan(latitudeDelta: 0.005, longitudeDelta: 0.005)) - }, - set: { _ in } - ) - let annotations = [MapLocation(name: node.user!.shortName ?? "???", coordinate: mostRecent.coordinate!)] - - Map(coordinateRegion: regionBinding, showsUserLocation: true, userTrackingMode: .none, annotationItems: annotations) { location in - MapAnnotation( - coordinate: location.coordinate, - content: { - CircleText(text: node.user!.shortName ?? "???", color: .accentColor) - } - ) - } - .frame(idealWidth: bounds.size.width, maxHeight: bounds.size.height / 3) - } else { - - Image(node.user?.hwModel ?? "UNSET") - .resizable() - .aspectRatio(contentMode: .fit) - .frame(width: bounds.size.width, height: bounds.size.height / 2) - } - } else { - - Image(node.user?.hwModel ?? "UNSET") - .resizable() - .aspectRatio(contentMode: .fit) - .frame(width: bounds.size.width, height: bounds.size.height / 2) - } - - ScrollView { - - if node.lastHeard != nil { - - HStack { - - Image(systemName: "clock.badge.checkmark.fill") - .font(.title) - .foregroundColor(.accentColor) - .symbolRenderingMode(.hierarchical) - Text("Last Heard: \(node.lastHeard!, style: .relative) ago").font(.title3) - } - .padding() - Divider() - } - - HStack { - - VStack(alignment: .center) { - Text("AKA").font(.title2).fixedSize() - CircleText(text: node.user?.shortName ?? "???", color: .accentColor) - .offset(y: 10) - } - .padding(5) - - Divider() - - VStack { - - Image(node.user!.hwModel ?? "UNSET") - .resizable() - .frame(width: 50, height: 50) - .cornerRadius(5) - - Text(String(node.user!.hwModel ?? "UNSET")) - .font(.callout).fixedSize() - } - .padding(5) - - if node.snr > 0 { - Divider() - VStack(alignment: .center) { - - Image(systemName: "waveform.path") - .font(.title) - .foregroundColor(.accentColor) - .symbolRenderingMode(.hierarchical) - Text("SNR").font(.title2).fixedSize() - Text(String(node.snr)) - .font(.title2) - .foregroundColor(.gray) - .fixedSize() - } - .padding(5) - } - - if node.positions!.count > 0 { - - let mostRecent = node.positions?.lastObject as! PositionEntity - - Divider() - - VStack(alignment: .center) { - - BatteryIcon(batteryLevel: mostRecent.batteryLevel, font: .title, color: .accentColor) - .padding(.bottom) - if mostRecent.batteryLevel > 0 { - - Text("Battery") - .font(.title2) - .fixedSize() - .textCase(.uppercase) - Text(String(mostRecent.batteryLevel) + "%") - .font(.title2) - .foregroundColor(.gray) - .symbolRenderingMode(.hierarchical) - } else { - - Text("Powered") - .font(.callout) - .fixedSize() - .textCase(.uppercase) - } - } - .padding(5) - } - } - .padding(4) - - Divider() - - HStack(alignment: .center) { - VStack { - HStack { - Image(systemName: "person") - .font(.title2) - .foregroundColor(.accentColor) - .symbolRenderingMode(.hierarchical) - Text("Unique Id:").font(.title2) - } - Text(node.user?.userId ?? "??????").font(.title3).foregroundColor(.gray) - } - Divider() - VStack { - HStack { - Image(systemName: "number") - .font(.title2) - .foregroundColor(.accentColor) - .symbolRenderingMode(.hierarchical) - Text("Node Number:").font(.title2) - } - Text(String(node.num)).font(.title3).foregroundColor(.gray) - } - }.padding(5) - HStack { - Text("MAC Address: ") - Text(String(node.user?.macaddr?.macAddressString ?? "not a valid mac address")).foregroundColor(.gray) - } - - if node.positions?.count ?? 0 > 1 { - - Divider() - - HStack { - - Image(systemName: "map.circle.fill") - .font(.title) - .foregroundColor(.accentColor) - .symbolRenderingMode(.hierarchical) - Text("Location History").font(.title2) - } - .padding() - - Divider() - - ForEach(node.positions!.array as! [PositionEntity], id: \.self) { (mappin: PositionEntity) in - - if mappin.coordinate != nil { - - VStack { - - HStack { - - Image(systemName: "mappin.and.ellipse").foregroundColor(.accentColor) // .font(.subheadline) - Text("Lat/Long:").font(.caption) - Text("\(String(mappin.latitude ?? 0)) \(String(mappin.longitude ?? 0))") - .foregroundColor(.gray) - .font(.caption) - - Text("Altitude:") - .font(.caption) - - Text("\(String(mappin.altitude))m") - .foregroundColor(.gray) - .font(.caption) - } - HStack { - - Image(systemName: "clock.badge.checkmark.fill") - .font(.subheadline) - .foregroundColor(.accentColor) - .symbolRenderingMode(.hierarchical) - Text("Time:") - .font(.caption) - Text("\(mappin.time!, style: .date) \(mappin.time!, style: .time)") - .foregroundColor(.gray) - .font(.caption) - Divider() - - Text("Battery").font(.caption).fixedSize() - Text(String(mappin.batteryLevel) + "%") - .font(.caption) - .foregroundColor(.gray) - .symbolRenderingMode(.hierarchical) - } - } - .padding(1) - Divider() - } - } - .padding(.bottom, 5) // Without some padding here there is a transparent contentview bug - } - } - }.ignoresSafeArea(.all, edges: [.leading, .trailing]) - } - } - .navigationTitle(node.user!.longName ?? "Unknown") - .navigationBarTitleDisplayMode(.inline) - .navigationBarItems(trailing: - - ZStack { - - ConnectedDevice( - bluetoothOn: bleManager.isSwitchedOn, - deviceConnected: bleManager.connectedPeripheral != nil, - name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "???") - } - ) - .onAppear(perform: { - - self.bleManager.context = context - - }) - } -} - -struct NodeInfoEntityDetail_Previews: PreviewProvider { - - static let bleManager = BLEManager() - - static var previews: some View { - Group { - - // NodeDetail(node: node) - } - } -} +///* +//Abstract: +//A view showing the details for a node. +//*/ +// +//import SwiftUI +//import MapKit +//import CoreLocation +// +//struct NodeDetail: View { +// +// @Environment(\.managedObjectContext) var context +// @EnvironmentObject var bleManager: BLEManager +// +// var node: NodeInfoEntity +// +// var body: some View { +// +// HStack { +// +// GeometryReader { bounds in +// +// VStack { +// +// if node.positions?.count ?? 0 > 0 { +// +// let mostRecent = node.positions?.lastObject as! PositionEntity +// +// if mostRecent.coordinate != nil { +// +// let nodeCoordinatePosition = CLLocationCoordinate2D(latitude: mostRecent.latitude!, longitude: mostRecent.longitude!) +// +// let regionBinding = Binding( +// get: { +// MKCoordinateRegion(center: nodeCoordinatePosition, span: MKCoordinateSpan(latitudeDelta: 0.005, longitudeDelta: 0.005)) +// }, +// set: { _ in } +// ) +// let annotations = [MapLocation(name: node.user!.shortName ?? "???", coordinate: mostRecent.coordinate!)] +// +// Map(coordinateRegion: regionBinding, showsUserLocation: true, userTrackingMode: .none, annotationItems: annotations) { location in +// MapAnnotation( +// coordinate: location.coordinate, +// content: { +// CircleText(text: node.user!.shortName ?? "???", color: .accentColor) +// } +// ) +// } +// .frame(idealWidth: bounds.size.width, maxHeight: bounds.size.height / 3) +// } else { +// +// Image(node.user?.hwModel ?? "UNSET") +// .resizable() +// .aspectRatio(contentMode: .fit) +// .frame(width: bounds.size.width, height: bounds.size.height / 2) +// } +// } else { +// +// Image(node.user?.hwModel ?? "UNSET") +// .resizable() +// .aspectRatio(contentMode: .fit) +// .frame(width: bounds.size.width, height: bounds.size.height / 2) +// } +// +// ScrollView { +// +// if node.lastHeard != nil { +// +// HStack { +// +// Image(systemName: "clock.badge.checkmark.fill") +// .font(.title) +// .foregroundColor(.accentColor) +// .symbolRenderingMode(.hierarchical) +// Text("Last Heard: \(node.lastHeard!, style: .relative) ago").font(.title3) +// } +// .padding() +// Divider() +// } +// +// HStack { +// +// VStack(alignment: .center) { +// Text("AKA").font(.title2).fixedSize() +// CircleText(text: node.user?.shortName ?? "???", color: .accentColor) +// .offset(y: 10) +// } +// .padding(5) +// +// Divider() +// +// VStack { +// +// Image(node.user!.hwModel ?? "UNSET") +// .resizable() +// .frame(width: 50, height: 50) +// .cornerRadius(5) +// +// Text(String(node.user!.hwModel ?? "UNSET")) +// .font(.callout).fixedSize() +// } +// .padding(5) +// +// if node.snr > 0 { +// Divider() +// VStack(alignment: .center) { +// +// Image(systemName: "waveform.path") +// .font(.title) +// .foregroundColor(.accentColor) +// .symbolRenderingMode(.hierarchical) +// Text("SNR").font(.title2).fixedSize() +// Text(String(node.snr)) +// .font(.title2) +// .foregroundColor(.gray) +// .fixedSize() +// } +// .padding(5) +// } +// +// if node.positions!.count > 0 { +// +// let mostRecent = node.positions?.lastObject as! PositionEntity +// +// Divider() +// +// VStack(alignment: .center) { +// +// BatteryIcon(batteryLevel: mostRecent.batteryLevel, font: .title, color: .accentColor) +// .padding(.bottom) +// if mostRecent.batteryLevel > 0 { +// +// Text("Battery") +// .font(.title2) +// .fixedSize() +// .textCase(.uppercase) +// Text(String(mostRecent.batteryLevel) + "%") +// .font(.title2) +// .foregroundColor(.gray) +// .symbolRenderingMode(.hierarchical) +// } else { +// +// Text("Powered") +// .font(.callout) +// .fixedSize() +// .textCase(.uppercase) +// } +// } +// .padding(5) +// } +// } +// .padding(4) +// +// Divider() +// +// HStack(alignment: .center) { +// VStack { +// HStack { +// Image(systemName: "person") +// .font(.title2) +// .foregroundColor(.accentColor) +// .symbolRenderingMode(.hierarchical) +// Text("User Id:").font(.title2) +// } +// Text(node.user?.userId ?? "??????").font(.title3).foregroundColor(.gray) +// } +// Divider() +// VStack { +// HStack { +// Image(systemName: "number") +// .font(.title2) +// .foregroundColor(.accentColor) +// .symbolRenderingMode(.hierarchical) +// Text("Node Number:").font(.title2) +// } +// Text(String(node.num)).font(.title3).foregroundColor(.gray) +// } +// } +// .padding(5) +// Divider() +// HStack { +// Image(systemName: "globe") +// .font(.headline) +// .foregroundColor(.accentColor) +// .symbolRenderingMode(.hierarchical) +// Text("MAC Address: ") +// Text(String(node.user?.macaddr?.macAddressString ?? "not a valid mac address")).foregroundColor(.gray) +// } +// .padding() +// +// if node.positions?.count ?? 0 > 1 { +// +// Divider() +// +// HStack { +// +// Image(systemName: "location.circle.fill") +// .font(.title) +// .foregroundColor(.accentColor) +// .symbolRenderingMode(.hierarchical) +// Text("Location History").font(.title2) +// } +// .padding() +// +// Divider() +// +// ForEach(node.positions!.array as! [PositionEntity], id: \.self) { (mappin: PositionEntity) in +// +// if mappin.coordinate != nil { +// +// VStack { +// +// HStack { +// +// Image(systemName: "mappin.and.ellipse").foregroundColor(.accentColor) // .font(.subheadline) +// Text("Lat/Long:").font(.caption) +// Text("\(String(mappin.latitude ?? 0)) \(String(mappin.longitude ?? 0))") +// .foregroundColor(.gray) +// .font(.caption) +// +// Image(systemName: "arrow.up.arrow.down.circle") +// .font(.subheadline) +// .foregroundColor(.accentColor) +// .symbolRenderingMode(.hierarchical) +// +// Text("Alt:") +// .font(.caption) +// +// Text("\(String(mappin.altitude))m") +// .foregroundColor(.gray) +// .font(.caption) +// } +// HStack { +// +// Image(systemName: "clock.badge.checkmark.fill") +// .font(.subheadline) +// .foregroundColor(.accentColor) +// .symbolRenderingMode(.hierarchical) +// Text("Time:") +// .font(.caption) +// Text("\(mappin.time!, style: .date) \(mappin.time!, style: .time)") +// .foregroundColor(.gray) +// .font(.caption) +// Divider() +// +// HStack { +// +// BatteryIcon(batteryLevel: mappin.batteryLevel, font: .subheadline, color: .accentColor) +// +// if mappin.batteryLevel > 0 { +// +// Text(String(mappin.batteryLevel) + "%") +// .font(.caption2) +// .foregroundColor(.gray) +// } +// } +// } +// } +// .padding(1) +// Divider() +// } +// } +// } +// } +// }.ignoresSafeArea(.all, edges: [.leading, .trailing]) +// } +// } +// .navigationTitle(node.user!.longName ?? "Unknown") +// .navigationBarTitleDisplayMode(.inline) +// .navigationBarItems(trailing: +// +// ZStack { +// +// ConnectedDevice( +// bluetoothOn: bleManager.isSwitchedOn, +// deviceConnected: bleManager.connectedPeripheral != nil, +// name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "???") +// } +// ) +// .onAppear(perform: { +// +// self.bleManager.context = context +// +// }) +// } +//} +// +//struct NodeInfoEntityDetail_Previews: PreviewProvider { +// +// static let bleManager = BLEManager() +// +// static var previews: some View { +// Group { +// +// // NodeDetail(node: node) +// } +// } +//} diff --git a/MeshtasticClient/Views/Nodes/NodeList.swift b/MeshtasticClient/Views/Nodes/NodeList.swift index f7b425f5..58a97a08 100644 --- a/MeshtasticClient/Views/Nodes/NodeList.swift +++ b/MeshtasticClient/Views/Nodes/NodeList.swift @@ -1,129 +1,129 @@ +//// +//// NodeList.swift +//// MeshtasticClient +//// +//// Created by Garth Vander Houwen on 8/7/21. +//// // -// NodeList.swift -// MeshtasticClient +//// Abstract: +//// A view showing a list of devices that have been seen on the mesh network from the perspective of the connected device. // -// Created by Garth Vander Houwen on 8/7/21. +//import SwiftUI // - -// Abstract: -// A view showing a list of devices that have been seen on the mesh network from the perspective of the connected device. - -import SwiftUI - -struct NodeList: View { - - @Environment(\.managedObjectContext) var context - @EnvironmentObject var bleManager: BLEManager - - @FetchRequest( - sortDescriptors: [NSSortDescriptor(key: "lastHeard", ascending: false)], - animation: .default) - - private var nodes: FetchedResults - - @State private var selection: String? - - var body: some View { - - NavigationView { - - List { - - if nodes.count == 0 { - - Text("Scan for Radios").font(.largeTitle) - Text("No LoRa Mesh Nodes Found").font(.title2) - Text("Go to the bluetooth section in the bottom right menu and click the Start Scanning button to scan for nearby radios and find your Meshtastic device. Make sure your device is powered on and near your phone or tablet.") - .font(.body) - Text("Once the device shows under Available Devices touch the device you want to connect to and it will pull node information over BLE and populate the node list and mesh map in the Meshtastic app.") - Text("Views with bluetooth functionality will show an indicator in the upper right hand corner show if bluetooth is on, and if a device is connected.") - .listRowSeparator(.visible) - - } else { - ForEach( nodes ) { node in - - let index = nodes.firstIndex(where: { $0.id == node.id }) - - NavigationLink(destination: NodeDetail(node: node), tag: String(index!), selection: $selection) { - - let connected: Bool = (bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.name == node.bleName) - - VStack(alignment: .leading) { - - HStack { - - CircleText(text: node.user?.shortName ?? "???", color: Color.accentColor).offset(y: 1).padding(.trailing, 5) - .offset(x: -15) - - if UIDevice.current.userInterfaceIdiom == .pad { Text(node.user?.longName ?? "Unknown").font(.headline) - .offset(x: -15) - } else { - Text(node.user?.longName ?? "Unknown").font(.title).offset(x: -15) - } - } - .padding(.bottom, 10) - - if connected { - HStack(alignment: .bottom) { - - Image(systemName: "repeat.circle.fill").font(.title3) - .foregroundColor(.accentColor).symbolRenderingMode(.hierarchical) - Text("Currently Connected").font(.title3).foregroundColor(Color.accentColor) - } - Spacer() - } - - HStack(alignment: .bottom) { - - Image(systemName: "clock.badge.checkmark.fill").font(.title3).foregroundColor(.accentColor).symbolRenderingMode(.hierarchical) - - if node.lastHeard != nil { - Text("Last Heard: \(node.lastHeard!, style: .relative) ago").font(.subheadline).foregroundColor(.gray) - } else { - Text("Last Heard: Unknown").font(.subheadline).foregroundColor(.gray) - } - } - } - .padding([.leading, .top, .bottom]) - } - .swipeActions { - - Button { - - context.delete(node) - - do { - - try context.save() - print("Successfully Deleted NodeInfoEntiy: \(node.num)") - - } catch { - - print("Failed to save context after deleting NodeInfoEntity Num: \(node.num)") - } - - } label: { - - Label("Delete from app", systemImage: "trash") - } - .tint(.red) - } - } - } - } - .navigationTitle("All Nodes") - .onAppear { - // self.nodes.returnsObjectsAsFaults = false - self.bleManager.context = context - - if UIDevice.current.userInterfaceIdiom == .pad { - if nodes.count > 0 { - selection = "0" - } - } - } - } - .ignoresSafeArea(.all, edges: [.leading, .trailing]) - .navigationViewStyle(DoubleColumnNavigationViewStyle()) - } -} +//struct NodeList: View { +// +// @Environment(\.managedObjectContext) var context +// @EnvironmentObject var bleManager: BLEManager +// +// @FetchRequest( +// sortDescriptors: [NSSortDescriptor(key: "lastHeard", ascending: false)], +// animation: .default) +// +// private var nodes: FetchedResults +// +// @State private var selection: String? +// +// var body: some View { +// +// NavigationView { +// +// List { +// +// if nodes.count == 0 { +// +// Text("Scan for Radios").font(.largeTitle) +// Text("No LoRa Mesh Nodes Found").font(.title2) +// Text("Go to the bluetooth section in the bottom right menu and click the Start Scanning button to scan for nearby radios and find your Meshtastic device. Make sure your device is powered on and near your phone or tablet.") +// .font(.body) +// Text("Once the device shows under Available Devices touch the device you want to connect to and it will pull node information over BLE and populate the node list and mesh map in the Meshtastic app.") +// Text("Views with bluetooth functionality will show an indicator in the upper right hand corner show if bluetooth is on, and if a device is connected.") +// .listRowSeparator(.visible) +// +// } else { +// ForEach( nodes ) { node in +// +// let index = nodes.firstIndex(where: { $0.id == node.id }) +// +// NavigationLink(destination: NodeDetail(node: node), tag: String(index!), selection: $selection) { +// +// let connected: Bool = (bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.name == node.bleName) +// +// VStack(alignment: .leading) { +// +// HStack { +// +// CircleText(text: node.user?.shortName ?? "???", color: Color.accentColor).offset(y: 1).padding(.trailing, 5) +// .offset(x: -15) +// +// if UIDevice.current.userInterfaceIdiom == .pad { Text(node.user?.longName ?? "Unknown").font(.headline) +// .offset(x: -15) +// } else { +// Text(node.user?.longName ?? "Unknown").font(.title).offset(x: -15) +// } +// } +// .padding(.bottom, 10) +// +// if connected { +// HStack(alignment: .bottom) { +// +// Image(systemName: "repeat.circle.fill").font(.title3) +// .foregroundColor(.accentColor).symbolRenderingMode(.hierarchical) +// Text("Currently Connected").font(.title3).foregroundColor(Color.accentColor) +// } +// Spacer() +// } +// +// HStack(alignment: .bottom) { +// +// Image(systemName: "clock.badge.checkmark.fill").font(.title3).foregroundColor(.accentColor).symbolRenderingMode(.hierarchical) +// +// if node.lastHeard != nil { +// Text("Last Heard: \(node.lastHeard!, style: .relative) ago").font(.subheadline).foregroundColor(.gray) +// } else { +// Text("Last Heard: Unknown").font(.subheadline).foregroundColor(.gray) +// } +// } +// } +// .padding([.leading, .top, .bottom]) +// } +// .swipeActions { +// +// Button { +// +// context.delete(node) +// +// do { +// +// try context.save() +// print("Successfully Deleted NodeInfoEntiy: \(node.num)") +// +// } catch { +// +// print("Failed to save context after deleting NodeInfoEntity Num: \(node.num)") +// } +// +// } label: { +// +// Label("Delete from app", systemImage: "trash") +// } +// .tint(.red) +// } +// } +// } +// } +// .navigationTitle("All Nodes") +// .onAppear { +// // self.nodes.returnsObjectsAsFaults = false +// self.bleManager.context = context +// +// if UIDevice.current.userInterfaceIdiom == .pad { +// if nodes.count > 0 { +// selection = "0" +// } +// } +// } +// } +// .ignoresSafeArea(.all, edges: [.leading, .trailing]) +// .navigationViewStyle(DoubleColumnNavigationViewStyle()) +// } +//}