This commit is contained in:
Garth Vander Houwen 2024-08-15 16:31:58 -07:00
parent 7e80accfad
commit ce3aacba63
8 changed files with 258 additions and 35 deletions

View file

@ -4810,9 +4810,6 @@
}
}
}
},
"Contacts" : {
},
"contacts %@" : {
"extractionState" : "migrated",
@ -6315,6 +6312,15 @@
},
"Direct" : {
},
"Direct Message Help" : {
},
"Direct messages are using the new public key infrastructure to encrypt the message." : {
},
"Direct messages are using the shared key for the channel when communicating with this node." : {
},
"direct.messages" : {
"localizations" : {
@ -7204,7 +7210,7 @@
"Favorites" : {
},
"Favorites and nodes with recent messages show up at the top of the list. Contacts using the shared key display an open lock, nodes with a private key show a green lock and a red key with a slash will show up if a key has changed for a contact. Long press to favorite or mute the contact or delete a conversation." : {
"Favorites and nodes with recent messages show up at the top of the contact list." : {
},
"Fifteen Minutes" : {
@ -11272,6 +11278,9 @@
},
"Long Name: %@" : {
},
"Long press to favorite or mute the contact or delete a conversation." : {
},
"Longitude" : {
@ -14527,6 +14536,9 @@
},
"Message" : {
},
"Message Status Options" : {
},
"message.details" : {
"localizations" : {
@ -15406,6 +15418,9 @@
}
}
}
},
"Node Encryption Status" : {
},
"Node History" : {
@ -16665,6 +16680,9 @@
},
"Public Key" : {
},
"Public Key Encryption" : {
},
"Public Key Mismatch" : {
@ -19763,6 +19781,9 @@
}
}
}
},
"Shared Key" : {
},
"Short Name" : {
@ -20995,6 +21016,9 @@
},
"The public key authorized to send admin messages to this node." : {
},
"The public key does not match the key that was used previously, delete the node and let it negotatiate keys again. Usually the other user did a factory reset, but it could indicate a security issue." : {
},
"The region where you will be using your radios." : {
@ -22519,6 +22543,9 @@
},
"Website" : {
},
"What does the lock mean?" : {
},
"What is Meshtastic?" : {

View file

@ -94,6 +94,9 @@
DD6193792863875F00E59241 /* SerialConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6193782863875F00E59241 /* SerialConfig.swift */; };
DD6F65722C6AB8EC0053C113 /* SecureInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6F65712C6AB8EC0053C113 /* SecureInput.swift */; };
DD6F65742C6CB80A0053C113 /* View.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6F65732C6CB80A0053C113 /* View.swift */; };
DD6F65762C6EA5490053C113 /* AckErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6F65752C6EA5490053C113 /* AckErrors.swift */; };
DD6F65792C6EADE60053C113 /* DirectMessagesHelp.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6F65782C6EADE60053C113 /* DirectMessagesHelp.swift */; };
DD6F657B2C6EC2900053C113 /* LockLegend.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6F657A2C6EC2900053C113 /* LockLegend.swift */; };
DD73FD1128750779000852D6 /* PositionLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD73FD1028750779000852D6 /* PositionLog.swift */; };
DD769E0328D18BF1001A3F05 /* DeviceMetricsLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD769E0228D18BF0001A3F05 /* DeviceMetricsLog.swift */; };
DD77093B2AA1ABB8007A8BF0 /* BluetoothTips.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD77093A2AA1ABB8007A8BF0 /* BluetoothTips.swift */; };
@ -349,6 +352,9 @@
DD68BAE72C417A74004C01A0 /* MeshtasticDataModelV 40.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModelV 40.xcdatamodel"; sourceTree = "<group>"; };
DD6F65712C6AB8EC0053C113 /* SecureInput.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureInput.swift; sourceTree = "<group>"; };
DD6F65732C6CB80A0053C113 /* View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = View.swift; sourceTree = "<group>"; };
DD6F65752C6EA5490053C113 /* AckErrors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AckErrors.swift; sourceTree = "<group>"; };
DD6F65782C6EADE60053C113 /* DirectMessagesHelp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DirectMessagesHelp.swift; sourceTree = "<group>"; };
DD6F657A2C6EC2900053C113 /* LockLegend.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockLegend.swift; sourceTree = "<group>"; };
DD73FD1028750779000852D6 /* PositionLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PositionLog.swift; sourceTree = "<group>"; };
DD769E0228D18BF0001A3F05 /* DeviceMetricsLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceMetricsLog.swift; sourceTree = "<group>"; };
DD77093A2AA1ABB8007A8BF0 /* BluetoothTips.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BluetoothTips.swift; sourceTree = "<group>"; };
@ -710,6 +716,16 @@
path = Module;
sourceTree = "<group>";
};
DD6F65772C6EAB860053C113 /* Help */ = {
isa = PBXGroup;
children = (
DD6F65752C6EA5490053C113 /* AckErrors.swift */,
DD6F65782C6EADE60053C113 /* DirectMessagesHelp.swift */,
DD6F657A2C6EC2900053C113 /* LockLegend.swift */,
);
path = Help;
sourceTree = "<group>";
};
DD7709392AA1ABA1007A8BF0 /* Tips */ = {
isa = PBXGroup;
children = (
@ -902,6 +918,7 @@
DDC2E18D26CE25CB0042C5E4 /* Helpers */ = {
isa = PBXGroup;
children = (
DD6F65772C6EAB860053C113 /* Help */,
DD3CC6BD28E4CD9800FA9159 /* BatteryGauge.swift */,
DD3CC24B2C498D6C001BD3A2 /* BatteryCompact.swift */,
DDB75A222A13CDA9006ED576 /* BatteryLevelCompact.swift */,
@ -1300,6 +1317,7 @@
DD3CC6BE28E4CD9800FA9159 /* BatteryGauge.swift in Sources */,
DD6193772862F90F00E59241 /* CannedMessagesConfig.swift in Sources */,
DD3619152B1EF9F900C41C8C /* LocationsHandler.swift in Sources */,
DD6F65792C6EADE60053C113 /* DirectMessagesHelp.swift in Sources */,
25F5D5C02C3F6DA6008036E3 /* Router.swift in Sources */,
DDDB444A29F8AA3A00EE2349 /* CLLocationCoordinate2D.swift in Sources */,
25C49D902C471AEA0024FBD1 /* Constants.swift in Sources */,
@ -1358,6 +1376,7 @@
251926852C3BA97800249DF5 /* FavoriteNodeButton.swift in Sources */,
D9C983A02B79D0E800BDBE6A /* AlertButton.swift in Sources */,
DD86D4112881D16900BAEB7A /* WriteCsvFile.swift in Sources */,
DD6F65762C6EA5490053C113 /* AckErrors.swift in Sources */,
DDDB445029F8AC9C00EE2349 /* UIImage.swift in Sources */,
DD86D40F2881BE4C00BAEB7A /* CsvDocument.swift in Sources */,
DDB75A142A0593E2006ED576 /* OfflineTileManager.swift in Sources */,
@ -1385,6 +1404,7 @@
DD15E4F52B8BFC8E00654F61 /* PaxCounterLog.swift in Sources */,
25F5D5C22C3F6E4B008036E3 /* AppState.swift in Sources */,
DD3CC6C028E7A60700FA9159 /* MessagingEnums.swift in Sources */,
DD6F657B2C6EC2900053C113 /* LockLegend.swift in Sources */,
DD97E96628EFD9820056DDA4 /* MeshtasticLogo.swift in Sources */,
DDAB580D2B0DAA9E00147258 /* Routes.swift in Sources */,
D93068D52B812B700066FBC8 /* MessageDestination.swift in Sources */,

View file

@ -54,7 +54,7 @@ enum RoutingError: Int, CaseIterable, Identifiable {
case .notAuthorized:
return "routing.notauthorized".localized
case .pkiFailed:
return "routing.pkiFailed".localized
return "routing.pkifailed".localized
case .pkiUnknownPubkey:
return "routing.pkiunknownpubkey".localized
}
@ -77,7 +77,7 @@ enum RoutingError: Int, CaseIterable, Identifiable {
case .noChannel:
return Color.orange
case .tooLarge:
return Color.red
return Color.orange
case .noResponse:
return Color.orange
case .dutyCycleLimit:

View file

@ -25,22 +25,3 @@ struct MessagesTip: Tip {
Image(systemName: "bubble.left.and.bubble.right")
}
}
@available(iOS 17.0, macOS 14.0, *)
struct ContactsTip: Tip {
var id: String {
return "tip.messages.contacts"
}
var title: Text {
// Text("tip.messages.contacts.title")
Text("Contacts")
}
var message: Text? {
// Text("tip.messages.contacts.message")
Text("Favorites and nodes with recent messages show up at the top of the list. Contacts using the shared key display an open lock, nodes with a private key show a green lock and a red key with a slash will show up if a key has changed for a contact. Long press to favorite or mute the contact or delete a conversation.")
}
var image: Image? {
Image(systemName: "person.circle")
}
}

View file

@ -0,0 +1,44 @@
//
// IAQScale.swift
// Meshtastic
//
// Copyright Garth Vander Houwen 4/24/24.
//
import SwiftUI
struct AckErrors: View {
var body: some View {
VStack(alignment: .leading) {
Text("Message Status Options")
.font(.title2)
HStack {
RoundedRectangle(cornerRadius: 5)
.fill(.orange)
.frame(width: 20, height: 12)
Text("Acknowledged by another node")
.font(.caption)
.foregroundStyle(.orange)
}
ForEach(RoutingError.allCases) { re in
HStack {
RoundedRectangle(cornerRadius: 5)
.fill(re.color)
.frame(width: 20, height: 12)
Text(re.display)
.font(.caption)
.foregroundStyle(re.color)
}
}
}
}
}
struct AckErrorsPreviews: PreviewProvider {
static var previews: some View {
VStack {
AckErrors()
}
}
}

View file

@ -0,0 +1,75 @@
//
// DirectMessagesHelp.swift
// Meshtastic
//
// Copyright Garth Vander Houwen on 8/15/24.
//
import SwiftUI
struct DirectMessagesHelp: View {
private var idiom: UIUserInterfaceIdiom { UIDevice.current.userInterfaceIdiom }
@Environment(\.dismiss) private var dismiss
var body: some View {
ScrollView {
Text("Direct Message Help")
.font(.title)
.padding(.vertical)
VStack(alignment: .leading) {
HStack {
Image(systemName: "star.fill")
.foregroundColor(.yellow)
.padding(.bottom)
Text("Favorites and nodes with recent messages show up at the top of the contact list.")
.fixedSize(horizontal: false, vertical: true)
.padding(.bottom)
}
HStack {
Image(systemName: "hand.tap")
.padding(.bottom)
Text("Long press to favorite or mute the contact or delete a conversation.")
.fixedSize(horizontal: false, vertical: true)
.padding(.bottom)
}
}
if idiom == .phone {
VStack(alignment: .leading) {
LockLegend()
AckErrors()
}
} else {
HStack(alignment: .top) {
LockLegend()
AckErrors()
}
}
#if targetEnvironment(macCatalyst)
Spacer()
Button {
dismiss()
} label: {
Label("close", systemImage: "xmark")
}
.buttonStyle(.bordered)
.buttonBorderShape(.capsule)
.controlSize(.large)
.padding(.bottom)
#endif
}
.frame(minHeight: 0, maxHeight: .infinity, alignment: .leading)
.padding()
.presentationDetents([.large])
.presentationContentInteraction(.scrolls)
.presentationDragIndicator(.visible)
.presentationBackgroundInteraction(.enabled(upThrough: .large))
}
}
struct DirectMessagesHelpPreviews: PreviewProvider {
static var previews: some View {
VStack {
AckErrors()
}
}
}

View file

@ -0,0 +1,61 @@
//
// LockLegend.swift
// Meshtastic
//
// Copyright Garth Vander Houwen 8/15/24.
//
import SwiftUI
struct LockLegend: View {
var body: some View {
VStack(alignment: .leading) {
Text("Node Encryption Status")
.font(.title2)
Text("What does the lock mean?")
.padding(.bottom)
VStack(alignment: .leading) {
HStack {
Image(systemName: "lock.open.fill")
.foregroundColor(.yellow)
Text("Shared Key")
.fontWeight(.semibold)
}
Text("Direct messages are using the shared key for the channel when communicating with this node.")
.fixedSize(horizontal: false, vertical: true)
}
.padding(.bottom)
VStack(alignment: .leading) {
HStack {
Image(systemName: "lock.fill")
.foregroundColor(.green)
Text("Public Key Encryption")
.fontWeight(.semibold)
}
Text("Direct messages are using the new public key infrastructure to encrypt the message.")
.fixedSize(horizontal: false, vertical: true)
}
.padding(.bottom)
VStack(alignment: .leading) {
HStack {
Image(systemName: "key.slash")
.foregroundColor(.red)
Text("Public Key Mismatch")
.fontWeight(.semibold)
}
Text("The public key does not match the key that was used previously, delete the node and let it negotatiate keys again. Usually the other user did a factory reset, but it could indicate a security issue.")
.fixedSize(horizontal: false, vertical: true)
}
.padding(.bottom)
}
}
}
struct LockLegendPreviews: PreviewProvider {
static var previews: some View {
VStack {
LockLegend()
}
}
}

View file

@ -28,9 +28,10 @@ struct UserList: View {
@State private var hopsAway: Double = -1.0
@State private var roleFilter = false
@State private var deviceRoles: Set<Int> = []
@State var isEditingFilters = false
@State private var editingFilters = false
@State private var showingHelp = false
@State private var showingTrustConfirm: Bool = false
var boolFilters: [Bool] {[
isFavorite,
isOnline,
@ -59,9 +60,6 @@ struct UserList: View {
let dateFormatString = (localeDateFormat ?? "MM/dd/YY")
VStack {
List(selection: $userSelection) {
if #available(iOS 17.0, macOS 14.0, *) {
TipView(ContactsTip(), arrowEdge: .bottom)
}
ForEach(users) { (user: UserEntity) in
let mostRecent = user.messageList.last
let lastMessageTime = Date(timeIntervalSince1970: TimeInterval(Int64((mostRecent?.messageTimestamp ?? 0 ))))
@ -196,9 +194,12 @@ struct UserList: View {
}
.listStyle(.plain)
.navigationTitle(String.localizedStringWithFormat("contacts %@".localized, String(users.count == 0 ? 0 : users.count)))
.sheet(isPresented: $isEditingFilters) {
.sheet(isPresented: $editingFilters) {
NodeListFilter(filterTitle: "Contact Filters", viaLora: $viaLora, viaMqtt: $viaMqtt, isOnline: $isOnline, isPkiEncrypted: $isPkiEncrypted, isFavorite: $isFavorite, isEnvironment: $isEnvironment, distanceFilter: $distanceFilter, maximumDistance: $maxDistance, hopsAway: $hopsAway, roleFilter: $roleFilter, deviceRoles: $deviceRoles)
}
.sheet(isPresented: $showingHelp) {
DirectMessagesHelp()
}
.onChange(of: searchText) { _ in
searchUserList()
}
@ -229,25 +230,39 @@ struct UserList: View {
.onFirstAppear {
searchUserList()
}
.safeAreaInset(edge: .bottom, alignment: .trailing) {
.safeAreaInset(edge: .bottom, alignment: .leading) {
HStack {
Button(action: {
withAnimation {
isEditingFilters = !isEditingFilters
showingHelp = !showingHelp
}
}) {
Image(systemName: !isEditingFilters ? "line.3.horizontal.decrease.circle" : "line.3.horizontal.decrease.circle.fill")
Image(systemName: !editingFilters ? "questionmark.circle" : "questionmark.circle.fill")
.padding(.vertical, 5)
}
.tint(Color(UIColor.secondarySystemBackground))
.foregroundColor(.accentColor)
.buttonStyle(.borderedProminent)
Spacer()
Button(action: {
withAnimation {
editingFilters = !editingFilters
}
}) {
Image(systemName: !editingFilters ? "line.3.horizontal.decrease.circle" : "line.3.horizontal.decrease.circle.fill")
.padding(.vertical, 5)
}
.tint(Color(UIColor.secondarySystemBackground))
.foregroundColor(.accentColor)
.buttonStyle(.borderedProminent)
}
.controlSize(.regular)
.padding(5)
}
.padding(.bottom, 5)
.padding(.bottom, 5)
.searchable(text: $searchText, placement: users.count > 10 ? .navigationBarDrawer(displayMode: .always) : .automatic, prompt: "Find a contact")
.disableAutocorrection(true)
.scrollDismissesKeyboard(.immediately)