Merge pull request #240 from meshtastic/2.0.3_Working_Changes
Add images for new heltec hardware, update protobufs
23
Meshtastic/Assets.xcassets/HELTECV20.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Heltec_turq 1.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "Heltec_turq 2.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "Heltec_turq.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
Meshtastic/Assets.xcassets/HELTECV20.imageset/Heltec_turq 1.png
vendored
Normal file
|
After Width: | Height: | Size: 4.2 MiB |
BIN
Meshtastic/Assets.xcassets/HELTECV20.imageset/Heltec_turq 2.png
vendored
Normal file
|
After Width: | Height: | Size: 4.2 MiB |
BIN
Meshtastic/Assets.xcassets/HELTECV20.imageset/Heltec_turq.png
vendored
Normal file
|
After Width: | Height: | Size: 4.2 MiB |
23
Meshtastic/Assets.xcassets/HELTECV3.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Heltec_turq 1.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "Heltec_turq 2.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "Heltec_turq.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
Meshtastic/Assets.xcassets/HELTECV3.imageset/Heltec_turq 1.png
vendored
Normal file
|
After Width: | Height: | Size: 4.2 MiB |
BIN
Meshtastic/Assets.xcassets/HELTECV3.imageset/Heltec_turq 2.png
vendored
Normal file
|
After Width: | Height: | Size: 4.2 MiB |
BIN
Meshtastic/Assets.xcassets/HELTECV3.imageset/Heltec_turq.png
vendored
Normal file
|
After Width: | Height: | Size: 4.2 MiB |
23
Meshtastic/Assets.xcassets/HELTECWSLV3.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "play_store_icon_114px-2.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "play_store_icon_114px-3.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "play_store_icon_114px-4.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
Meshtastic/Assets.xcassets/HELTECWSLV3.imageset/play_store_icon_114px-2.png
vendored
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
Meshtastic/Assets.xcassets/HELTECWSLV3.imageset/play_store_icon_114px-3.png
vendored
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
Meshtastic/Assets.xcassets/HELTECWSLV3.imageset/play_store_icon_114px-4.png
vendored
Normal file
|
After Width: | Height: | Size: 12 KiB |
|
|
@ -29,35 +29,34 @@ struct MeshtasticAppleApp: App {
|
|||
.onContinueUserActivity(NSUserActivityTypeBrowsingWeb) { userActivity in
|
||||
|
||||
print("URL received \(userActivity)")
|
||||
incomingUrl = userActivity.webpageURL
|
||||
self.incomingUrl = userActivity.webpageURL
|
||||
|
||||
if incomingUrl!.absoluteString.lowercased().contains("meshtastic.org/e/#") {
|
||||
if self.incomingUrl!.absoluteString.lowercased().contains("meshtastic.org/e/#") {
|
||||
|
||||
if let components = incomingUrl?.absoluteString.components(separatedBy: "#") {
|
||||
channelSettings = components.last!
|
||||
if let components = self.incomingUrl?.absoluteString.components(separatedBy: "#") {
|
||||
self.channelSettings = components.last!
|
||||
}
|
||||
saveChannels = true
|
||||
print("User wants to open a Channel Settings URL: \(incomingUrl?.absoluteString ?? "No QR Code Link")")
|
||||
self.saveChannels = true
|
||||
print("User wants to open a Channel Settings URL: \(self.incomingUrl?.absoluteString ?? "No QR Code Link")")
|
||||
}
|
||||
if saveChannels {
|
||||
print("User wants to open Channel Settings URL: \(String(describing: incomingUrl!.relativeString))")
|
||||
if self.saveChannels {
|
||||
print("User wants to open Channel Settings URL: \(String(describing: self.incomingUrl!.relativeString))")
|
||||
}
|
||||
}
|
||||
|
||||
.onOpenURL(perform: { (url) in
|
||||
|
||||
print("Some sort of URL was received \(url)")
|
||||
incomingUrl = url
|
||||
self.incomingUrl = url
|
||||
|
||||
if url.absoluteString.lowercased().contains("meshtastic.org/e/#") {
|
||||
if let components = incomingUrl?.absoluteString.components(separatedBy: "#") {
|
||||
channelSettings = components.last!
|
||||
if let components = self.incomingUrl?.absoluteString.components(separatedBy: "#") {
|
||||
self.channelSettings = components.last!
|
||||
}
|
||||
saveChannels = true
|
||||
print("User wants to open a Channel Settings URL: \(incomingUrl?.absoluteString ?? "No QR Code Link")")
|
||||
self.saveChannels = true
|
||||
print("User wants to open a Channel Settings URL: \(self.incomingUrl?.absoluteString ?? "No QR Code Link")")
|
||||
} else {
|
||||
saveChannels = false
|
||||
print("User wants to import a MBTILES offline map file: \(incomingUrl?.absoluteString ?? "No Tiles link")")
|
||||
print("User wants to import a MBTILES offline map file: \(self.incomingUrl?.absoluteString ?? "No Tiles link")")
|
||||
}
|
||||
|
||||
//we are expecting a .mbtiles map file that contains raster data
|
||||
|
|
@ -66,7 +65,7 @@ struct MeshtasticAppleApp: App {
|
|||
let documentsDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first!
|
||||
let destination = documentsDirectory.appendingPathComponent("offline_map.mbtiles", isDirectory: false)
|
||||
|
||||
if !saveChannels {
|
||||
if !self.saveChannels {
|
||||
//do we need to delete an old one?
|
||||
if (fileManager.fileExists(atPath: destination.path)) {
|
||||
print("ℹ️ Found an old map file. Deleting it")
|
||||
|
|
@ -91,7 +90,6 @@ struct MeshtasticAppleApp: App {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
.onChange(of: scenePhase) { (newScenePhase) in
|
||||
switch newScenePhase {
|
||||
case .background:
|
||||
|
|
|
|||
|
|
@ -142,6 +142,14 @@ enum HardwareModel: SwiftProtobuf.Enum {
|
|||
/// M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, Paper) https://m5stack.com/
|
||||
case m5Stack // = 42
|
||||
|
||||
///
|
||||
/// New Heltec LoRA32 with ESP32-S3 CPU
|
||||
case heltecV3 // = 43
|
||||
|
||||
///
|
||||
/// New Heltec Wireless Stick Lite with ESP32-S3 CPU
|
||||
case heltecWslV3 // = 44
|
||||
|
||||
///
|
||||
/// Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits.
|
||||
case privateHw // = 255
|
||||
|
|
@ -181,6 +189,8 @@ enum HardwareModel: SwiftProtobuf.Enum {
|
|||
case 40: self = .nrf52840Pca10059
|
||||
case 41: self = .drDev
|
||||
case 42: self = .m5Stack
|
||||
case 43: self = .heltecV3
|
||||
case 44: self = .heltecWslV3
|
||||
case 255: self = .privateHw
|
||||
default: self = .UNRECOGNIZED(rawValue)
|
||||
}
|
||||
|
|
@ -216,6 +226,8 @@ enum HardwareModel: SwiftProtobuf.Enum {
|
|||
case .nrf52840Pca10059: return 40
|
||||
case .drDev: return 41
|
||||
case .m5Stack: return 42
|
||||
case .heltecV3: return 43
|
||||
case .heltecWslV3: return 44
|
||||
case .privateHw: return 255
|
||||
case .UNRECOGNIZED(let i): return i
|
||||
}
|
||||
|
|
@ -256,6 +268,8 @@ extension HardwareModel: CaseIterable {
|
|||
.nrf52840Pca10059,
|
||||
.drDev,
|
||||
.m5Stack,
|
||||
.heltecV3,
|
||||
.heltecWslV3,
|
||||
.privateHw,
|
||||
]
|
||||
}
|
||||
|
|
@ -2144,6 +2158,8 @@ extension HardwareModel: SwiftProtobuf._ProtoNameProviding {
|
|||
40: .same(proto: "NRF52840_PCA10059"),
|
||||
41: .same(proto: "DR_DEV"),
|
||||
42: .same(proto: "M5STACK"),
|
||||
43: .same(proto: "HELTEC_V3"),
|
||||
44: .same(proto: "HELTEC_WSL_V3"),
|
||||
255: .same(proto: "PRIVATE_HW"),
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,6 +68,10 @@ enum TelemetrySensorType: SwiftProtobuf.Enum {
|
|||
///
|
||||
/// 6-Axis inertial measurement sensor
|
||||
case qmi8658 // = 10
|
||||
|
||||
///
|
||||
/// 3-Axis magnetic sensor
|
||||
case qmc5883L // = 11
|
||||
case UNRECOGNIZED(Int)
|
||||
|
||||
init() {
|
||||
|
|
@ -87,6 +91,7 @@ enum TelemetrySensorType: SwiftProtobuf.Enum {
|
|||
case 8: self = .lps22
|
||||
case 9: self = .qmc6310
|
||||
case 10: self = .qmi8658
|
||||
case 11: self = .qmc5883L
|
||||
default: self = .UNRECOGNIZED(rawValue)
|
||||
}
|
||||
}
|
||||
|
|
@ -104,6 +109,7 @@ enum TelemetrySensorType: SwiftProtobuf.Enum {
|
|||
case .lps22: return 8
|
||||
case .qmc6310: return 9
|
||||
case .qmi8658: return 10
|
||||
case .qmc5883L: return 11
|
||||
case .UNRECOGNIZED(let i): return i
|
||||
}
|
||||
}
|
||||
|
|
@ -126,6 +132,7 @@ extension TelemetrySensorType: CaseIterable {
|
|||
.lps22,
|
||||
.qmc6310,
|
||||
.qmi8658,
|
||||
.qmc5883L,
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -288,6 +295,7 @@ extension TelemetrySensorType: SwiftProtobuf._ProtoNameProviding {
|
|||
8: .same(proto: "LPS22"),
|
||||
9: .same(proto: "QMC6310"),
|
||||
10: .same(proto: "QMI8658"),
|
||||
11: .same(proto: "QMC5883L"),
|
||||
]
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,9 +29,7 @@ struct Connect: View {
|
|||
List {
|
||||
if bleManager.isSwitchedOn {
|
||||
Section(header: Text("Connected Radio").font(.title)) {
|
||||
|
||||
if bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.peripheral.state == .connected {
|
||||
|
||||
HStack {
|
||||
Image(systemName: "antenna.radiowaves.left.and.right")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
|
|
|
|||
|
|
@ -11,12 +11,27 @@ struct MeshtasticLogo: View {
|
|||
@Environment(\.colorScheme) var colorScheme
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
Image(colorScheme == .dark ? "logo-white" : "logo-black")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
}
|
||||
.padding(.bottom, 5)
|
||||
.offset(x: -15)
|
||||
|
||||
#if targetEnvironment(macCatalyst)
|
||||
VStack {
|
||||
Image("logo-white")
|
||||
.resizable()
|
||||
.renderingMode(.template)
|
||||
.foregroundColor(.accentColor)
|
||||
.scaledToFit()
|
||||
}
|
||||
.padding(.bottom, 5)
|
||||
.padding(.top, 5)
|
||||
.offset(x: -15)
|
||||
#else
|
||||
VStack {
|
||||
Image(colorScheme == .dark ? "logo-white" : "logo-black")
|
||||
.resizable()
|
||||
.renderingMode(.template)
|
||||
.scaledToFit()
|
||||
}
|
||||
.padding(.bottom, 5)
|
||||
.offset(x: -15)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ struct MessageBubble: View {
|
|||
|
||||
HStack(alignment: .top) {
|
||||
|
||||
CircleText(text: shortName, color: isCurrentUser ? Color.blue : Color(.darkGray)).padding(.all, 5)
|
||||
CircleText(text: shortName, color: isCurrentUser ? .accentColor : Color(.gray)).padding(.all, 5)
|
||||
.gesture(LongPressGesture(minimumDuration: 2)
|
||||
.onEnded {_ in
|
||||
print("I want to delete message: \(id)")
|
||||
|
|
@ -25,7 +25,7 @@ struct MessageBubble: View {
|
|||
.textSelection(.enabled)
|
||||
.padding(10)
|
||||
.foregroundColor(.white)
|
||||
.background(isCurrentUser ? Color.blue : Color(.darkGray))
|
||||
.background(isCurrentUser ? .accentColor : Color(.gray))
|
||||
.cornerRadius(10)
|
||||
HStack(spacing: 4) {
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ struct ChannelMessageList: View {
|
|||
if message.replyID > 0 {
|
||||
let messageReply = channel.allPrivateMessages.first(where: { $0.messageId == message.replyID })
|
||||
HStack {
|
||||
Text(messageReply?.messagePayload ?? "EMPTY MESSAGE").foregroundColor(.blue).font(.caption2)
|
||||
Text(messageReply?.messagePayload ?? "EMPTY MESSAGE").foregroundColor(.accentColor).font(.caption2)
|
||||
.padding(10)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 18)
|
||||
|
|
@ -49,14 +49,14 @@ struct ChannelMessageList: View {
|
|||
)
|
||||
Image(systemName: "arrowshape.turn.up.left.fill")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
.imageScale(.large).foregroundColor(.blue)
|
||||
.imageScale(.large).foregroundColor(.accentColor)
|
||||
.padding(.trailing)
|
||||
}
|
||||
}
|
||||
HStack (alignment: .top) {
|
||||
if currentUser { Spacer(minLength:50) }
|
||||
if !currentUser {
|
||||
CircleText(text: message.fromUser?.shortName ?? "????", color: currentUser ? .accentColor : Color(.darkGray), circleSize: 44, fontSize: 14)
|
||||
CircleText(text: message.fromUser?.shortName ?? "????", color: currentUser ? .accentColor : Color(.gray), circleSize: 44, fontSize: 14)
|
||||
.padding(.all, 5)
|
||||
.offset(y: -5)
|
||||
}
|
||||
|
|
@ -64,7 +64,7 @@ struct ChannelMessageList: View {
|
|||
Text(message.messagePayload ?? "EMPTY MESSAGE")
|
||||
.padding(10)
|
||||
.foregroundColor(.white)
|
||||
.background(currentUser ? Color.blue : Color(.darkGray))
|
||||
.background(currentUser ? .accentColor : Color(.gray))
|
||||
.cornerRadius(15)
|
||||
.contextMenu {
|
||||
VStack{
|
||||
|
|
@ -332,7 +332,7 @@ struct ChannelMessageList: View {
|
|||
}
|
||||
}
|
||||
}) {
|
||||
Image(systemName: "arrow.up.circle.fill").font(.largeTitle).foregroundColor(.blue)
|
||||
Image(systemName: "arrow.up.circle.fill").font(.largeTitle).foregroundColor(.accentColor)
|
||||
}
|
||||
}
|
||||
.padding(.all, 15)
|
||||
|
|
@ -342,7 +342,7 @@ struct ChannelMessageList: View {
|
|||
.toolbar {
|
||||
ToolbarItem(placement: .principal) {
|
||||
HStack {
|
||||
CircleText(text: String(channel.index), color: .blue, circleSize: 44, fontSize: 30).fixedSize()
|
||||
CircleText(text: String(channel.index), color: .accentColor, circleSize: 44, fontSize: 30).fixedSize()
|
||||
Text(String(channel.name ?? "Unknown").camelCaseToWords()).font(.headline)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,48 +42,48 @@ struct Contacts: View {
|
|||
HStack {
|
||||
VStack(alignment: .leading) {
|
||||
HStack {
|
||||
CircleText(text: String(channel.index), color: Color.blue, circleSize: 52, fontSize: 40)
|
||||
CircleText(text: String(channel.index), color: .gray, circleSize: 52, fontSize: 40)
|
||||
.padding(.trailing, 5)
|
||||
VStack {
|
||||
Text(String(channel.name ?? "Channel \(channel.index)").camelCaseToWords()).font(.headline)
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
|
||||
if channel.allPrivateMessages.count > 0 {
|
||||
VStack (alignment: .trailing) {
|
||||
if lastMessageDay == currentDay {
|
||||
Text(lastMessageTime, style: .time )
|
||||
.font(.callout)
|
||||
.foregroundColor(.gray)
|
||||
} else if lastMessageDay == (currentDay - 1) {
|
||||
Text("Yesterday")
|
||||
.font(.callout)
|
||||
.foregroundColor(.gray)
|
||||
} else if lastMessageDay < (currentDay - 1) && lastMessageDay > (currentDay - 5) {
|
||||
Text(lastMessageTime.formattedDate(format: "MM/dd/yy"))
|
||||
.font(.callout)
|
||||
.foregroundColor(.gray)
|
||||
} else if lastMessageDay < (currentDay - 1800) {
|
||||
Text(lastMessageTime.formattedDate(format: "MM/dd/yy"))
|
||||
.font(.callout)
|
||||
.foregroundColor(.gray)
|
||||
HStack {
|
||||
Text(String(channel.name ?? "Channel \(channel.index)").camelCaseToWords()).font(.headline)
|
||||
Spacer()
|
||||
if channel.allPrivateMessages.count > 0 {
|
||||
VStack (alignment: .trailing) {
|
||||
if lastMessageDay == currentDay {
|
||||
Text(lastMessageTime, style: .time )
|
||||
.font(.subheadline)
|
||||
} else if lastMessageDay == (currentDay - 1) {
|
||||
Text("Yesterday")
|
||||
.font(.subheadline)
|
||||
} else if lastMessageDay < (currentDay - 1) && lastMessageDay > (currentDay - 5) {
|
||||
Text(lastMessageTime.formattedDate(format: "MM/dd/yy"))
|
||||
.font(.subheadline)
|
||||
} else if lastMessageDay < (currentDay - 1800) {
|
||||
Text(lastMessageTime.formattedDate(format: "MM/dd/yy"))
|
||||
.font(.subheadline)
|
||||
}
|
||||
}
|
||||
.brightness(-0.20)
|
||||
}
|
||||
}
|
||||
if channel.allPrivateMessages.count > 0 {
|
||||
HStack(alignment: .top) {
|
||||
Text("\(mostRecent != nil ? mostRecent!.messagePayload! : " ")")
|
||||
.truncationMode(.tail)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.brightness(-0.20)
|
||||
.font(.body)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if channel.allPrivateMessages.count > 0 {
|
||||
HStack(alignment: .top) {
|
||||
Text("\(mostRecent != nil ? mostRecent!.messagePayload! : " ")")
|
||||
.truncationMode(.tail)
|
||||
.foregroundColor(Color.gray)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.frame(maxWidth: .infinity, maxHeight: 80, alignment: .leading)
|
||||
}
|
||||
}
|
||||
.padding(.top, 10)
|
||||
|
|
@ -99,44 +99,44 @@ struct Contacts: View {
|
|||
let lastMessageDay = Calendar.current.dateComponents([.day], from: lastMessageTime).day ?? 0
|
||||
let currentDay = Calendar.current.dateComponents([.day], from: Date()).day ?? 0
|
||||
HStack {
|
||||
VStack(alignment: .leading) {
|
||||
VStack {
|
||||
HStack {
|
||||
CircleText(text: user.shortName ?? "???", color: Color.blue, circleSize: 52, fontSize: 16)
|
||||
CircleText(text: user.shortName ?? "???", color: .gray, circleSize: 52, fontSize: 16)
|
||||
.padding(.trailing, 5)
|
||||
VStack {
|
||||
Text(user.longName ?? "Unknown").font(.headline)
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
|
||||
if user.messageList.count > 0 {
|
||||
VStack (alignment: .trailing) {
|
||||
if lastMessageDay == currentDay {
|
||||
Text(lastMessageTime, style: .time )
|
||||
.font(.callout)
|
||||
.foregroundColor(.gray)
|
||||
} else if lastMessageDay == (currentDay - 1) {
|
||||
Text("Yesterday")
|
||||
.font(.callout)
|
||||
.foregroundColor(.gray)
|
||||
} else if lastMessageDay < (currentDay - 1) && lastMessageDay > (currentDay - 5) {
|
||||
Text(lastMessageTime.formattedDate(format: "MM/dd/yy"))
|
||||
.font(.callout)
|
||||
.foregroundColor(.gray)
|
||||
} else if lastMessageDay < (currentDay - 1800) {
|
||||
Text(lastMessageTime.formattedDate(format: "MM/dd/yy"))
|
||||
.font(.callout)
|
||||
.foregroundColor(.gray)
|
||||
HStack {
|
||||
Text(user.longName ?? "Unknown").font(.headline)
|
||||
Spacer()
|
||||
if user.messageList.count > 0 {
|
||||
VStack (alignment: .trailing) {
|
||||
if lastMessageDay == currentDay {
|
||||
Text(lastMessageTime, style: .time )
|
||||
.font(.subheadline)
|
||||
} else if lastMessageDay == (currentDay - 1) {
|
||||
Text("Yesterday")
|
||||
.font(.subheadline)
|
||||
} else if lastMessageDay < (currentDay - 1) && lastMessageDay > (currentDay - 5) {
|
||||
Text(lastMessageTime.formattedDate(format: "MM/dd/yy"))
|
||||
.font(.subheadline)
|
||||
} else if lastMessageDay < (currentDay - 1800) {
|
||||
Text(lastMessageTime.formattedDate(format: "MM/dd/yy"))
|
||||
.font(.subheadline)
|
||||
}
|
||||
}
|
||||
.brightness(-0.2)
|
||||
}
|
||||
}
|
||||
if user.messageList.count > 0 {
|
||||
HStack(alignment: .top) {
|
||||
Text("\(mostRecent != nil ? mostRecent!.messagePayload! : " ")")
|
||||
.truncationMode(.tail)
|
||||
.font(.body)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.brightness(-0.2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if user.messageList.count > 0 {
|
||||
HStack(alignment: .top) {
|
||||
Text("\(mostRecent != nil ? mostRecent!.messagePayload! : " ")")
|
||||
.truncationMode(.tail)
|
||||
.foregroundColor(Color.gray)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: 80, alignment: .leading)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -147,10 +147,7 @@ struct Contacts: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
.tint(Color(UIColor.systemGray))
|
||||
.navigationSplitViewStyle(.automatic)
|
||||
.navigationTitle("Contacts")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.navigationBarItems(leading:
|
||||
MeshtasticLogo()
|
||||
)
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ struct UserMessageList: View {
|
|||
if message.replyID > 0 {
|
||||
let messageReply = user.messageList.first(where: { $0.messageId == message.replyID })
|
||||
HStack {
|
||||
Text(messageReply?.messagePayload ?? "EMPTY MESSAGE").foregroundColor(.blue).font(.caption2)
|
||||
Text(messageReply?.messagePayload ?? "EMPTY MESSAGE").foregroundColor(.accentColor).font(.caption2)
|
||||
.padding(10)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 18)
|
||||
|
|
@ -50,14 +50,14 @@ struct UserMessageList: View {
|
|||
)
|
||||
Image(systemName: "arrowshape.turn.up.left.fill")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
.imageScale(.large).foregroundColor(.blue)
|
||||
.imageScale(.large).foregroundColor(.accentColor)
|
||||
.padding(.trailing)
|
||||
}
|
||||
}
|
||||
HStack (alignment: .top) {
|
||||
if currentUser { Spacer(minLength:50) }
|
||||
if !currentUser {
|
||||
CircleText(text: message.fromUser?.shortName ?? "????", color: currentUser ? .accentColor : Color(.darkGray), circleSize: 44, fontSize: 14)
|
||||
CircleText(text: message.fromUser?.shortName ?? "????", color: currentUser ? .accentColor : Color(.gray), circleSize: 44, fontSize: 14)
|
||||
.padding(.all, 5)
|
||||
.offset(y: -5)
|
||||
}
|
||||
|
|
@ -65,7 +65,7 @@ struct UserMessageList: View {
|
|||
Text(message.messagePayload ?? "EMPTY MESSAGE")
|
||||
.padding(10)
|
||||
.foregroundColor(.white)
|
||||
.background(currentUser ? Color.blue : Color(.darkGray))
|
||||
.background(currentUser ? .accentColor : Color(.gray))
|
||||
.cornerRadius(15)
|
||||
.contextMenu {
|
||||
VStack{
|
||||
|
|
@ -327,7 +327,7 @@ struct UserMessageList: View {
|
|||
}
|
||||
}
|
||||
}) {
|
||||
Image(systemName: "arrow.up.circle.fill").font(.largeTitle).foregroundColor(.blue)
|
||||
Image(systemName: "arrow.up.circle.fill").font(.largeTitle).foregroundColor(.accentColor)
|
||||
}
|
||||
}
|
||||
.padding(.all, 15)
|
||||
|
|
@ -337,7 +337,7 @@ struct UserMessageList: View {
|
|||
.toolbar {
|
||||
ToolbarItem(placement: .principal) {
|
||||
HStack {
|
||||
CircleText(text: user.shortName ?? "???", color: .blue, circleSize: 44, fontSize: 14).fixedSize()
|
||||
CircleText(text: user.shortName ?? "???", color: .accentColor, circleSize: 44, fontSize: 14).fixedSize()
|
||||
Text(user.longName ?? "Unknown").font(.headline)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,10 +15,8 @@ struct DeviceMetricsLog: View {
|
|||
@EnvironmentObject var bleManager: BLEManager
|
||||
|
||||
@State private var isPresentingClearLogConfirm: Bool = false
|
||||
|
||||
@State var isExporting = false
|
||||
@State var exportString = ""
|
||||
|
||||
var node: NodeInfoEntity
|
||||
|
||||
var body: some View {
|
||||
|
|
@ -43,14 +41,10 @@ struct DeviceMetricsLog: View {
|
|||
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
|
||||
//Add a table for mac and ipad
|
||||
Table(node.telemetries!.reversed() as! [TelemetryEntity]) {
|
||||
|
||||
TableColumn("Battery Level") { dm in
|
||||
|
||||
if dm.metricsType == 0 {
|
||||
if dm.batteryLevel == 0 {
|
||||
|
||||
Text("Powered")
|
||||
|
||||
} else {
|
||||
|
||||
Text("\(String(dm.batteryLevel))%")
|
||||
|
|
@ -80,7 +74,6 @@ struct DeviceMetricsLog: View {
|
|||
}
|
||||
|
||||
} else {
|
||||
|
||||
ScrollView {
|
||||
Grid(alignment: .topLeading, horizontalSpacing: 2) {
|
||||
GridRow {
|
||||
|
|
@ -129,13 +122,9 @@ struct DeviceMetricsLog: View {
|
|||
}
|
||||
}
|
||||
HStack {
|
||||
|
||||
Button(role: .destructive) {
|
||||
|
||||
isPresentingClearLogConfirm = true
|
||||
|
||||
} label: {
|
||||
|
||||
Label("Clear Log", systemImage: "trash.fill")
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
|
|
@ -148,21 +137,16 @@ struct DeviceMetricsLog: View {
|
|||
titleVisibility: .visible
|
||||
) {
|
||||
Button("Delete all device metrics?", role: .destructive) {
|
||||
|
||||
if clearTelemetry(destNum: node.num, metricsType: 0, context: context) {
|
||||
|
||||
print("Clear Device Metrics Log Failed")
|
||||
|
||||
print("Cleared Device Metrics for \(node.num)")
|
||||
} else {
|
||||
|
||||
print("Clear Device Metrics Log Failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
exportString = TelemetryToCsvFile(telemetry: node.telemetries!.array as! [TelemetryEntity], metricsType: 0)
|
||||
isExporting = true
|
||||
|
||||
} label: {
|
||||
Label("Save", systemImage: "square.and.arrow.down")
|
||||
}
|
||||
|
|
@ -171,17 +155,13 @@ struct DeviceMetricsLog: View {
|
|||
.controlSize(.large)
|
||||
.padding()
|
||||
}
|
||||
|
||||
.navigationTitle("Device Metrics Log")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.navigationBarItems(trailing:
|
||||
|
||||
ZStack {
|
||||
|
||||
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????")
|
||||
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????")
|
||||
})
|
||||
.onAppear {
|
||||
|
||||
self.bleManager.context = context
|
||||
}
|
||||
.fileExporter(
|
||||
|
|
@ -190,14 +170,11 @@ struct DeviceMetricsLog: View {
|
|||
contentType: .commaSeparatedText,
|
||||
defaultFilename: String("\(node.user!.longName ?? "Node") Device Telemetry Log"),
|
||||
onCompletion: { result in
|
||||
|
||||
if case .success = result {
|
||||
|
||||
print("Device Telemetry log download succeeded.")
|
||||
self.isExporting = false
|
||||
|
||||
} else {
|
||||
|
||||
print("Device Telemetry log download failed: \(result).")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ struct NodeList: View {
|
|||
let connected: Bool = (bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.num == node.num)
|
||||
VStack(alignment: .leading) {
|
||||
HStack {
|
||||
CircleText(text: node.user?.shortName ?? "???", color: .blue, circleSize: 52, fontSize: 16).offset(y: 1).padding(.trailing, 5)
|
||||
CircleText(text: node.user?.shortName ?? "???", color: .gray, circleSize: 52, fontSize: 16).offset(y: 1).padding(.trailing, 5)
|
||||
.offset(x: -15)
|
||||
|
||||
Text(node.user?.longName ?? "Unknown").font(.headline).offset(x: -15)
|
||||
|
|
@ -50,9 +50,10 @@ struct NodeList: View {
|
|||
.padding(.bottom, 5)
|
||||
if connected {
|
||||
HStack(alignment: .bottom) {
|
||||
Image(systemName: "repeat.circle.fill").font(.title2)
|
||||
.foregroundColor(.accentColor).symbolRenderingMode(.hierarchical)
|
||||
Text("Currently Connected").font(.callout).foregroundColor(Color.accentColor)
|
||||
Image(systemName: "repeat.circle.fill")
|
||||
.font(.title2)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
Text("Currently Connected").font(.callout)
|
||||
}
|
||||
.padding(.bottom, 2)
|
||||
}
|
||||
|
|
@ -63,25 +64,27 @@ struct NodeList: View {
|
|||
if lastPostion.coordinate != nil {
|
||||
let nodeCoord = CLLocation(latitude: lastPostion.coordinate!.latitude, longitude: lastPostion.coordinate!.longitude)
|
||||
let metersAway = nodeCoord.distance(from: myCoord)
|
||||
Image(systemName: "lines.measurement.horizontal").font(.title3)
|
||||
.foregroundColor(.accentColor).symbolRenderingMode(.hierarchical)
|
||||
Image(systemName: "lines.measurement.horizontal")
|
||||
.font(.title3)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
|
||||
DistanceText(meters: metersAway).font(.subheadline).foregroundColor(.gray)
|
||||
DistanceText(meters: metersAway).font(.subheadline)
|
||||
}
|
||||
}
|
||||
.padding(.bottom, 2)
|
||||
}
|
||||
HStack(alignment: .bottom) {
|
||||
Image(systemName: "clock.badge.checkmark.fill").font(.headline)
|
||||
.foregroundColor(.accentColor).symbolRenderingMode(.hierarchical)
|
||||
LastHeardText(lastHeard: node.lastHeard).font(.subheadline).foregroundColor(.gray)
|
||||
Image(systemName: "clock.badge.checkmark.fill")
|
||||
.font(.headline)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
LastHeardText(lastHeard: node.lastHeard)
|
||||
.font(.subheadline)
|
||||
}
|
||||
}
|
||||
.padding([.leading, .top, .bottom])
|
||||
}
|
||||
}
|
||||
}
|
||||
.tint(Color(UIColor.systemGray))
|
||||
.navigationTitle("All Nodes")
|
||||
.navigationBarItems(leading:
|
||||
MeshtasticLogo()
|
||||
|
|
|
|||