diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 6c4f3df1..2836e221 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -470,7 +470,6 @@ DDC2E18B26CE25A70042C5E4 /* Messages */, DD4A911C2708C57100501B7E /* Settings */, DDC2E18E26CE25FE0042C5E4 /* ContentView.swift */, - DD4033C128B286B70096A444 /* Onboarding.swift */, ); path = Views; sourceTree = ""; @@ -498,6 +497,7 @@ children = ( DD882F5C2772E4640005BF05 /* Contacts.swift */, DD1BF2F82776FE2E008C8D2F /* MessageList.swift */, + DD4033C128B286B70096A444 /* Onboarding.swift */, ); path = Messages; sourceTree = ""; diff --git a/Meshtastic/Persistence/UserEntityExtension.swift b/Meshtastic/Persistence/UserEntityExtension.swift index 0298cffc..698dbf6d 100644 --- a/Meshtastic/Persistence/UserEntityExtension.swift +++ b/Meshtastic/Persistence/UserEntityExtension.swift @@ -2,7 +2,7 @@ // UserEntityExtension.swift // MeshtasticApple // -// Created by Garth Vander Houwen on 6/3/22. +// Copyright(c) Garth Vander Houwen 6/3/22. // import Foundation diff --git a/Meshtastic/Views/Bluetooth/Connect.swift b/Meshtastic/Views/Bluetooth/Connect.swift index b0c80909..391b81fe 100644 --- a/Meshtastic/Views/Bluetooth/Connect.swift +++ b/Meshtastic/Views/Bluetooth/Connect.swift @@ -16,7 +16,6 @@ struct Connect: View { @EnvironmentObject var bleManager: BLEManager @EnvironmentObject var userSettings: UserSettings - @State var initialLoad: Bool = true @State var isPreferredRadio: Bool = false @State var invalidFirmwareVersion = false @@ -30,15 +29,6 @@ struct Connect: View { List { if bleManager.isSwitchedOn { - - if bleManager.lastConnectionError.count > 0 { - - Section(header: Text("Connection Error").font(.title)) { - - Text(bleManager.lastConnectionError).font(.callout).foregroundColor(.red) - } - .textCase(nil) - } Section(header: Text("Connected Radio").font(.title)) { @@ -161,6 +151,9 @@ struct Connect: View { } else { + if bleManager.lastConnectionError.count > 0 { + Text(bleManager.lastConnectionError).font(.callout).foregroundColor(.red) + } HStack { Image(systemName: "antenna.radiowaves.left.and.right.slash") .symbolRenderingMode(.hierarchical) @@ -269,78 +262,60 @@ struct Connect: View { .buttonBorderShape(.capsule) .controlSize(.large) .padding() - } #endif - Spacer() } .padding(.bottom, 10) - - } .navigationTitle("Bluetooth") - .navigationBarItems(leading: - MeshtasticLogo(), - trailing: - - ZStack { - - ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") + .navigationBarItems(leading: MeshtasticLogo(), trailing: + ZStack { + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") }) } - .navigationViewStyle(StackNavigationViewStyle()) + // .navigationViewStyle(StackNavigationViewStyle()) .sheet(isPresented: $invalidFirmwareVersion, onDismiss: didDismissSheet) { InvalidVersion(minimumVersion: self.bleManager.minimumVersion, version: self.bleManager.connectedVersion) .presentationDetents([.large]) .presentationDragIndicator(.automatic) } - - .onChange(of: (self.bleManager.invalidVersion)) { cv in - invalidFirmwareVersion = self.bleManager.invalidVersion - } .onAppear(perform: { - - if initialLoad { + + self.bleManager.context = context + self.bleManager.userSettings = userSettings - self.bleManager.context = context - self.bleManager.userSettings = userSettings - - // Ask for notification permission - UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { success, error in - if success { - print("Notifications are all set!") - } else if let error = error { - print(error.localizedDescription) - } + // Ask for notification permission + UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { success, error in + if success { + print("Notifications are all set!") + } else if let error = error { + print(error.localizedDescription) } - - initialLoad = false } - if self.bleManager.connectedPeripheral != nil && userSettings.preferredPeripheralId == self.bleManager.connectedPeripheral.peripheral.identifier.uuidString { + if self.bleManager.connectedPeripheral != nil { + print(self.bleManager.connectedPeripheral.id) + print(userSettings.preferredPeripheralId) + } + if self.bleManager.connectedPeripheral != nil && userSettings.preferredPeripheralId == self.bleManager.connectedPeripheral.id { isPreferredRadio = true + if userSettings.preferredNodeNum > 0 { + + print("I wanna set my prefered node") + } + } else { isPreferredRadio = false } }) } func didDismissSheet() { - bleManager.disconnectPeripheral() } } - -struct Connect_Previews: PreviewProvider { - - static var previews: some View { - Connect() - - .environmentObject(BLEManager()) - } -} diff --git a/Meshtastic/Views/Messages/Contacts.swift b/Meshtastic/Views/Messages/Contacts.swift index f62613d2..7e31f0a9 100644 --- a/Meshtastic/Views/Messages/Contacts.swift +++ b/Meshtastic/Views/Messages/Contacts.swift @@ -11,19 +11,25 @@ struct Contacts: View { @Environment(\.managedObjectContext) var context @EnvironmentObject var bleManager: BLEManager - + @ObservedObject private var userSettings: UserSettings = UserSettings() + @FetchRequest( sortDescriptors: [NSSortDescriptor(key: "longName", ascending: true)], animation: .default) - - private var users: FetchedResults + private var users: FetchedResults + + + + private var prefferedNode: NodeInfoEntity? + @FetchRequest( - sortDescriptors: [NSSortDescriptor(key: "lastHeard", ascending: true)], + sortDescriptors: [NSSortDescriptor(key: "num", ascending: true)], animation: .default) private var nodes: FetchedResults + @State private var selection: UserEntity? = nil // Nothing selected by default. var body: some View { diff --git a/Meshtastic/Views/Messages/MessageList.swift b/Meshtastic/Views/Messages/MessageList.swift index b10fcdfa..310326c6 100644 --- a/Meshtastic/Views/Messages/MessageList.swift +++ b/Meshtastic/Views/Messages/MessageList.swift @@ -29,16 +29,13 @@ struct MessageList: View { @State private var deleteMessageId: Int64 = 0 @State private var replyMessageId: Int64 = 0 @State private var sendPositionWithMessage: Bool = false - @State private var messageCount = 0 @State private var refreshId = UUID() - var body: some View { - NavigationStack { ScrollViewReader { scrollView in ScrollView { - if user.messageList.count > 0 { + LazyVStack { ForEach( user.messageList ) { (message: MessageEntity) in if user.num != userSettings.preferredNodeNum { let currentUser: Bool = (userSettings.preferredNodeNum == message.fromUser?.num ? true : false) @@ -59,235 +56,233 @@ struct MessageList: View { } } HStack (alignment: .top) { - if currentUser { Spacer(minLength:50) } - if !currentUser { - CircleText(text: message.fromUser?.shortName ?? "????", color: currentUser ? .accentColor : Color(.darkGray), circleSize: 44, fontSize: 14) - .padding(.all, 5) - .offset(y: -5) - } - VStack(alignment: currentUser ? .trailing : .leading) { - Text(message.messagePayload ?? "EMPTY MESSAGE") - .padding(10) - .foregroundColor(.white) - .background(currentUser ? Color.blue : Color(.darkGray)) - .cornerRadius(15) - .contextMenu { - Menu("Tapback response") { - - Button(action: { - if bleManager.sendMessage(message: "❤️", toUserNum: user.num, isEmoji: true, replyID: message.messageId) { - print("Sent ❤️ Tapback") - self.context.refresh(user, mergeChanges: true) - } else { print("❤️ Tapback Failed") } + if currentUser { Spacer(minLength:50) } + if !currentUser { + CircleText(text: message.fromUser?.shortName ?? "????", color: currentUser ? .accentColor : Color(.darkGray), circleSize: 44, fontSize: 14) + .padding(.all, 5) + .offset(y: -5) + } + VStack(alignment: currentUser ? .trailing : .leading) { + Text(message.messagePayload ?? "EMPTY MESSAGE") + .padding(10) + .foregroundColor(.white) + .background(currentUser ? Color.blue : Color(.darkGray)) + .cornerRadius(15) + .contextMenu { + Menu("Tapback response") { - }) { - Text("Heart") - let image = "❤️".image() - Image(uiImage: image!) + Button(action: { + if bleManager.sendMessage(message: "❤️", toUserNum: user.num, isEmoji: true, replyID: message.messageId) { + print("Sent ❤️ Tapback") + self.context.refresh(user, mergeChanges: true) + } else { print("❤️ Tapback Failed") } + + }) { + Text("Heart") + let image = "❤️".image() + Image(uiImage: image!) + } + Button(action: { + + if bleManager.sendMessage(message: "👍", toUserNum: user.num, isEmoji: true, replyID: message.messageId) { + + print("Sent 👍 Tapback") + self.context.refresh(user, mergeChanges: true) + + } else { print("👍 Tapback Failed")} + + }) { + Text("Thumbs Up") + let image = "👍".image() + Image(uiImage: image!) + } + Button(action: { + + if bleManager.sendMessage(message: "👎", toUserNum: user.num, isEmoji: true, replyID: message.messageId) { + + print("Sent 👎 Tapback") + self.context.refresh(user, mergeChanges: true) + + } else { print("👎 Tapback Failed") } + + }) { + Text("Thumbs Down") + let image = "👎".image() + Image(uiImage: image!) + } + Button(action: { + + if bleManager.sendMessage(message: "🤣", toUserNum: user.num, isEmoji: true, replyID: message.messageId) { + + print("Sent 🤣 Tapback") + self.context.refresh(user, mergeChanges: true) + + } else { print("🤣 Tapback Failed") } + + }) { + Text("HaHa") + let image = "🤣".image() + Image(uiImage: image!) + } + Button(action: { + + if bleManager.sendMessage(message: "‼️", toUserNum: user.num, isEmoji: true, replyID: message.messageId) { + + print("Sent ‼️ Tapback") + self.context.refresh(user, mergeChanges: true) + + } else { print("‼️ Tapback Failed") } + + }) { + Text("Exclamation Mark") + let image = "‼️".image() + Image(uiImage: image!) + } + Button(action: { + if bleManager.sendMessage(message: "❓", toUserNum: user.num, isEmoji: true, replyID: message.messageId) { + self.context.refresh(user, mergeChanges: true) + print("Sent ❓ Tapback") + } else { print("❓ Tapback Failed") } + }) { + Text("Question Mark") + let image = "❓".image() + Image(uiImage: image!) + } + Button(action: { + if bleManager.sendMessage(message: "💩", toUserNum: user.num, isEmoji: true, replyID: message.messageId) { + self.context.refresh(user, mergeChanges: true) + print("Sent 💩 Tapback") + } else { print("💩 Tapback Failed") } + }) { + Text("Poop") + let image = "💩".image() + Image(uiImage: image!) + } } Button(action: { + self.replyMessageId = message.messageId + self.focusedField = .messageText - if bleManager.sendMessage(message: "👍", toUserNum: user.num, isEmoji: true, replyID: message.messageId) { - - print("Sent 👍 Tapback") - self.context.refresh(user, mergeChanges: true) - - } else { print("👍 Tapback Failed")} - + print("I want to reply to \(message.messageId)") }) { - Text("Thumbs Up") - let image = "👍".image() - Image(uiImage: image!) + Text("Reply") + Image(systemName: "arrowshape.turn.up.left.2.fill") } Button(action: { - - if bleManager.sendMessage(message: "👎", toUserNum: user.num, isEmoji: true, replyID: message.messageId) { - - print("Sent 👎 Tapback") - self.context.refresh(user, mergeChanges: true) - - } else { print("👎 Tapback Failed") } - + UIPasteboard.general.string = message.messagePayload }) { - Text("Thumbs Down") - let image = "👎".image() - Image(uiImage: image!) + Text("Copy") + Image(systemName: "doc.on.doc") } - Button(action: { - - if bleManager.sendMessage(message: "🤣", toUserNum: user.num, isEmoji: true, replyID: message.messageId) { - - print("Sent 🤣 Tapback") - self.context.refresh(user, mergeChanges: true) - - } else { print("🤣 Tapback Failed") } - - }) { - Text("HaHa") - let image = "🤣".image() - Image(uiImage: image!) - } - Button(action: { - - if bleManager.sendMessage(message: "‼️", toUserNum: user.num, isEmoji: true, replyID: message.messageId) { - - print("Sent ‼️ Tapback") - self.context.refresh(user, mergeChanges: true) - - } else { print("‼️ Tapback Failed") } - - }) { - Text("Exclamation Mark") - let image = "‼️".image() - Image(uiImage: image!) - } - Button(action: { - if bleManager.sendMessage(message: "❓", toUserNum: user.num, isEmoji: true, replyID: message.messageId) { - self.context.refresh(user, mergeChanges: true) - print("Sent ❓ Tapback") - } else { print("❓ Tapback Failed") } - }) { - Text("Question Mark") - let image = "❓".image() - Image(uiImage: image!) - } - Button(action: { - if bleManager.sendMessage(message: "💩", toUserNum: user.num, isEmoji: true, replyID: message.messageId) { - self.context.refresh(user, mergeChanges: true) - print("Sent 💩 Tapback") - } else { print("💩 Tapback Failed") } - }) { - Text("Poop") - let image = "💩".image() - Image(uiImage: image!) - } - } - Button(action: { - self.replyMessageId = message.messageId - self.focusedField = .messageText - - print("I want to reply to \(message.messageId)") - }) { - Text("Reply") - Image(systemName: "arrowshape.turn.up.left.2.fill") - } - Button(action: { - UIPasteboard.general.string = message.messagePayload - }) { - Text("Copy") - Image(systemName: "doc.on.doc") - } - Menu("Message Details") { - VStack { - let messageDate = Date(timeIntervalSince1970: TimeInterval(message.messageTimestamp)) - Text("Date \(messageDate, style: .date) \(messageDate.formattedDate(format: "h:mm:ss a"))").font(.caption2).foregroundColor(.gray) - } - if currentUser && message.receivedACK { - + Menu("Message Details") { VStack { + let messageDate = Date(timeIntervalSince1970: TimeInterval(message.messageTimestamp)) + Text("Date \(messageDate, style: .date) \(messageDate.formattedDate(format: "h:mm:ss a"))").font(.caption2).foregroundColor(.gray) + } + if currentUser && message.receivedACK { - Text("Received Ack \(message.receivedACK ? "✔️" : "")") + VStack { + + Text("Received Ack \(message.receivedACK ? "✔️" : "")") + } + + } else if currentUser && message.ackError == 0 { + + // Empty Error + Text("Waiting. . .") + + } else if currentUser && message.ackError > 0 { + + let ackErrorVal = RoutingError(rawValue: Int(message.ackError)) + Text("\(ackErrorVal?.display ?? "No Error" )").fixedSize(horizontal: false, vertical: true) } - } else if currentUser && message.ackError == 0 { - - // Empty Error - Text("Waiting. . .") - - } else if currentUser && message.ackError > 0 { - - let ackErrorVal = RoutingError(rawValue: Int(message.ackError)) - Text("\(ackErrorVal?.display ?? "No Error" )").fixedSize(horizontal: false, vertical: true) - } - - if currentUser { - - VStack { + if currentUser { - let ackDate = Date(timeIntervalSince1970: TimeInterval(message.ackTimestamp)) - - let sixMonthsAgo = Calendar.current.date(byAdding: .month, value: -6, to: Date()) - if ackDate >= sixMonthsAgo! { + VStack { - Text((ackDate.formattedDate(format: "h:mm:ss a"))).font(.caption2).foregroundColor(.gray) + let ackDate = Date(timeIntervalSince1970: TimeInterval(message.ackTimestamp)) - } else { + let sixMonthsAgo = Calendar.current.date(byAdding: .month, value: -6, to: Date()) + if ackDate >= sixMonthsAgo! { + + Text((ackDate.formattedDate(format: "h:mm:ss a"))).font(.caption2).foregroundColor(.gray) + + } else { + + Text("Unknown Age").font(.caption2).foregroundColor(.gray) + } + } + } + + if message.ackSNR != 0 { + VStack { - Text("Unknown Age").font(.caption2).foregroundColor(.gray) + Text("Ack SNR \(String(message.ackSNR))") + .font(.caption2) + .foregroundColor(.gray) } } } - - if message.ackSNR != 0 { - VStack { - - Text("Ack SNR \(String(message.ackSNR))") - .font(.caption2) - .foregroundColor(.gray) - } + Divider() + Button(role: .destructive, action: { + self.showDeleteMessageAlert = true + self.deleteMessageId = message.messageId + print(deleteMessageId) + }) { + Text("Delete") + Image(systemName: "trash") } } - Divider() - Button(role: .destructive, action: { - self.showDeleteMessageAlert = true - self.deleteMessageId = message.messageId - print(deleteMessageId) - }) { - Text("Delete") - Image(systemName: "trash") - } - } - - let tapbacks = message.value(forKey: "tapbacks") as! [MessageEntity] - - if tapbacks.count > 0 { - VStack (alignment: .trailing) { + let tapbacks = message.value(forKey: "tapbacks") as! [MessageEntity] + if tapbacks.count > 0 { - HStack { + VStack (alignment: .trailing) { - ForEach( tapbacks ) { (tapback: MessageEntity) in + HStack { - VStack { + ForEach( tapbacks ) { (tapback: MessageEntity) in - let image = tapback.messagePayload!.image(fontSize: 20) - Image(uiImage: image!).font(.caption) - Text("\(tapback.fromUser?.shortName ?? "????")") - .font(.caption2) - .foregroundColor(.gray) - .fixedSize() - .padding(.bottom, 1) + VStack { + + let image = tapback.messagePayload!.image(fontSize: 20) + Image(uiImage: image!).font(.caption) + Text("\(tapback.fromUser?.shortName ?? "????")") + .font(.caption2) + .foregroundColor(.gray) + .fixedSize() + .padding(.bottom, 1) + } } } + .padding(10) + .overlay( + RoundedRectangle(cornerRadius: 18) + .stroke(Color.gray, lineWidth: 1) + ) + } + } + HStack { + if currentUser && message.receivedACK { + // Ack Received + Text("Acknowledged").font(.caption2).foregroundColor(.gray) + } else if currentUser && message.ackError == 0 { + // Empty Error + Text("Waiting to be acknowledged. . .").font(.caption2).foregroundColor(.orange) + } else if currentUser && message.ackError > 0 { + let ackErrorVal = RoutingError(rawValue: Int(message.ackError)) + Text("\(ackErrorVal?.display ?? "No Error" )").fixedSize(horizontal: false, vertical: true) + .font(.caption2).foregroundColor(.red) } - .padding(10) - .overlay( - RoundedRectangle(cornerRadius: 18) - .stroke(Color.gray, lineWidth: 1) - ) } } - - HStack { - if currentUser && message.receivedACK { - // Ack Received - Text("Acknowledged").font(.caption2).foregroundColor(.gray) - } else if currentUser && message.ackError == 0 { - // Empty Error - Text("Waiting to be acknowledged. . .").font(.caption2).foregroundColor(.orange) - } else if currentUser && message.ackError > 0 { - let ackErrorVal = RoutingError(rawValue: Int(message.ackError)) - Text("\(ackErrorVal?.display ?? "No Error" )").fixedSize(horizontal: false, vertical: true) - .font(.caption2).foregroundColor(.red) - } + .padding(.bottom) + .id(user.messageList.firstIndex(of: message)) + if !currentUser { + Spacer(minLength:50) } } - .padding(.bottom) - .id(user.messageList.firstIndex(of: message)) - if !currentUser { - Spacer(minLength:50) - } - } .padding([.leading, .trailing]) .frame(maxWidth: .infinity) .id(message.messageId) @@ -311,31 +306,24 @@ struct MessageList: View { } } } - .listRowSeparator(.hidden) } } .padding([.top]) .scrollDismissesKeyboard(.immediately) .onAppear(perform: { - self.bleManager.context = context - - messageCount = user.messageList.count refreshId = UUID() - - }) - .onChange(of: messageCount, perform: { value in - if messageCount > 0 { + if user.messageList.count > 0 { scrollView.scrollTo(user.messageList.last!.messageId) } }) .onChange(of: user.messageList, perform: { messages in refreshId = UUID() - messageCount = messages.count + if user.messageList.count > 0 { + scrollView.scrollTo(user.messageList.last!.messageId) + } }) } - - HStack(alignment: .top) { ZStack { diff --git a/Meshtastic/Views/Onboarding.swift b/Meshtastic/Views/Messages/Onboarding.swift similarity index 73% rename from Meshtastic/Views/Onboarding.swift rename to Meshtastic/Views/Messages/Onboarding.swift index 89769c08..911dba3b 100644 --- a/Meshtastic/Views/Onboarding.swift +++ b/Meshtastic/Views/Messages/Onboarding.swift @@ -21,6 +21,15 @@ struct Onboarding: View { .font(.callout) .padding() + NavigationLink() { + + LoRaConfig(node: nil) + + } label: { + Image(systemName: "dot.radiowaves.left.and.right") + .symbolRenderingMode(.hierarchical) + Text("LoRa") + } } } }