diff --git a/Meshtastic Client.xcodeproj/project.pbxproj b/Meshtastic Client.xcodeproj/project.pbxproj index 31ab48b6..ca5981b1 100644 --- a/Meshtastic Client.xcodeproj/project.pbxproj +++ b/Meshtastic Client.xcodeproj/project.pbxproj @@ -660,7 +660,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.25.9; + MARKETING_VERSION = 1.25.10; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; @@ -687,7 +687,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.25.9; + MARKETING_VERSION = 1.25.10; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; diff --git a/MeshtasticClient/Views/Helpers/MessageBubble.swift b/MeshtasticClient/Views/Helpers/MessageBubble.swift index c8877227..e7b94e72 100644 --- a/MeshtasticClient/Views/Helpers/MessageBubble.swift +++ b/MeshtasticClient/Views/Helpers/MessageBubble.swift @@ -1,19 +1,26 @@ import SwiftUI struct MessageBubble: View { + + @State var showAlert = false var contentMessage: String var isCurrentUser: Bool var time: Int32 var shortName: String - + var id: UInt32 var body: some View { - - - + HStack (alignment: .top) { CircleText(text: shortName, color: isCurrentUser ? Color.blue : Color(.darkGray)).padding(.all, 5) + .gesture(LongPressGesture(minimumDuration: 2) + .onEnded {_ in + print("I want to delete message: \(id)") + self.showAlert = true + }) + + VStack (alignment: .leading) { Text(contentMessage) .textSelection(.enabled) @@ -37,13 +44,24 @@ struct MessageBubble: View { } Spacer() } + .alert(isPresented: $showAlert) { + Alert(title: Text("Are you sure you want to delete this message?"), message: Text("This action is permanent."), + primaryButton: .destructive (Text("OK")) { + print("OK button tapped") + //let messageIndex = meshData.nodes.firstIndex(where: { $0.id == node.id }) + //meshData.nodes.remove(at: nodeIndex!) + //meshData.save() + }, + secondaryButton: .cancel() + ) + } } } struct MessageBubble_Previews: PreviewProvider { static var previews: some View { Group { - MessageBubble(contentMessage: "this is the best text ever", isCurrentUser: true, time: 0, shortName: "EB") + MessageBubble(contentMessage: "this is the best text ever", isCurrentUser: true, time: 0, shortName: "EB", id: 12) } .previewLayout(.fixed(width: 300, height: 100)) } diff --git a/MeshtasticClient/Views/Messages/Messages.swift b/MeshtasticClient/Views/Messages/Messages.swift index 08313a4d..316f55ee 100644 --- a/MeshtasticClient/Views/Messages/Messages.swift +++ b/MeshtasticClient/Views/Messages/Messages.swift @@ -19,6 +19,8 @@ struct Messages: View { @FocusState private var focusedField: Field? @Namespace var topId @Namespace var bottomId + @State var showDeleteMessageAlert = false + @State private var deleteMessageId : UInt32 = 0 // Message Data and Bluetooth @EnvironmentObject var messageData: MessageData @@ -44,7 +46,61 @@ struct Messages: View { let currentUser: Bool = (bleManager.connectedNode != nil) && ((bleManager.connectedNode.id) == message.fromUserId) - MessageBubble(contentMessage: message.messagePayload, isCurrentUser: currentUser, time: Int32(message.messageTimestamp), shortName: message.fromUserShortName) + //MessageBubble(contentMessage: message.messagePayload, isCurrentUser: currentUser, time: Int32(message.messageTimestamp), shortName: message.fromUserShortName, id: message.messageId) + + HStack (alignment: .top) { + + CircleText(text: message.fromUserShortName, color: currentUser ? Color.blue : 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 + + }) + + + VStack (alignment: .leading) { + Text(message.messagePayload) + .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 messageIndex = messageData.messages.firstIndex(where: { $0.messageId == deleteMessageId }) + messageData.messages.remove(at: messageIndex!) + messageData.save() + print("Deleted message: \(message.messageId)") + showDeleteMessageAlert = false + deleteMessageId = 0 + } + }, + secondaryButton: .cancel() + ) + } } .onAppear(perform: { scrollView.scrollTo(bottomId) } ) Text("Hidden Bottom Anchor").hidden().frame(height: 0).id(bottomId) diff --git a/MeshtasticClient/Views/Settings/AppSettings.swift b/MeshtasticClient/Views/Settings/AppSettings.swift index 85716149..81573d01 100644 --- a/MeshtasticClient/Views/Settings/AppSettings.swift +++ b/MeshtasticClient/Views/Settings/AppSettings.swift @@ -18,7 +18,7 @@ enum KeyboardType: Int, CaseIterable, Identifiable { case .defaultKeyboard: return "Default" case .asciiCapable: - return "ascii Capable" + return "ASCII Capable" case .twitter: return "Twitter" case .emailAddress: @@ -36,6 +36,16 @@ class UserSettings: ObservableObject { UserDefaults.standard.set(username, forKey: "username") } } + @Published var preferredPeripheralName: String { + didSet { + UserDefaults.standard.set(preferredPeripheralName, forKey: "preferredPeripheralName") + } + } + @Published var preferredPeripheralId: String { + didSet { + UserDefaults.standard.set(preferredPeripheralId, forKey: "preferredPeripheralId") + } + } @Published var provideLocation: Bool { didSet { UserDefaults.standard.set(provideLocation, forKey: "provideLocation") @@ -49,6 +59,8 @@ class UserSettings: ObservableObject { init() { self.username = UserDefaults.standard.object(forKey: "username") as? String ?? "" + self.preferredPeripheralName = UserDefaults.standard.object(forKey: "preferredPeripheralName") as? String ?? "" + self.preferredPeripheralId = UserDefaults.standard.object(forKey: "preferredPeripheralId") as? String ?? "" self.provideLocation = UserDefaults.standard.object(forKey: "provideLocation") as? Bool ?? false self.keyboardType = UserDefaults.standard.object(forKey: "keyboardType") as? Int ?? 0 } @@ -76,6 +88,11 @@ struct AppSettings: View { Text("Provide location to mesh") } } + Section(header: Text("PREFERRED PERIPHERAL")) { + Text("The preferred peripheral will automatically reconnect if it becomes disconnected and is still within range. This device is assumed to be the primary messaging device for the app.").font(.caption).foregroundColor(.gray) + Text(userSettings.preferredPeripheralName) + + } Section(header: Text("MESSAGING OPTIONS")) { Picker("Keyboard Type", selection: $userSettings.keyboardType) {