2022-09-27 08:09:35 -07:00
//
// S h a r e C h a n n e l . s w i f t
// M e s h t a s t i c A p p l e
//
2022-10-14 22:18:28 -07:00
// C o p y r i g h t ( c ) G a r t h V a n d e r H o u w e n 4 / 8 / 2 2 .
2022-09-27 08:09:35 -07:00
//
import SwiftUI
import CoreData
import CoreImage . CIFilterBuiltins
struct QrCodeImage {
let context = CIContext ( )
2023-03-06 10:33:18 -08:00
2022-09-27 08:09:35 -07:00
func generateQRCode ( from text : String ) -> UIImage {
var qrImage = UIImage ( systemName : " xmark.circle " ) ? ? UIImage ( )
let data = Data ( text . utf8 )
let filter = CIFilter . qrCodeGenerator ( )
filter . setValue ( data , forKey : " inputMessage " )
2023-03-06 10:33:18 -08:00
2022-09-27 08:09:35 -07:00
let transform = CGAffineTransform ( scaleX : 20 , y : 20 )
if let outputImage = filter . outputImage ? . transformed ( by : transform ) {
if let image = context . createCGImage (
outputImage ,
from : outputImage . extent ) {
qrImage = UIImage ( cgImage : image )
}
}
return qrImage
}
}
2022-10-10 13:40:18 -07:00
2022-09-27 08:09:35 -07:00
struct ShareChannels : View {
2023-03-06 10:33:18 -08:00
2022-09-27 08:09:35 -07:00
@ Environment ( \ . managedObjectContext ) var context
@ EnvironmentObject var bleManager : BLEManager
2022-12-09 19:15:15 -08:00
@ Environment ( \ . dismiss ) private var dismiss
2022-10-10 07:17:57 -07:00
@ State var channelSet : ChannelSet = ChannelSet ( )
2022-09-29 22:24:05 -07:00
@ State var includeChannel0 = true
2022-10-29 07:31:50 -07:00
@ State var includeChannel1 = true
@ State var includeChannel2 = true
@ State var includeChannel3 = true
@ State var includeChannel4 = true
@ State var includeChannel5 = true
@ State var includeChannel6 = true
@ State var includeChannel7 = true
2022-10-10 07:17:57 -07:00
@ State var isPresentingHelp = false
2022-09-27 08:09:35 -07:00
var node : NodeInfoEntity ?
2022-10-10 14:22:26 -07:00
@ State private var channelsUrl = " https://www.meshtastic.org/e/# "
2022-09-27 08:09:35 -07:00
var qrCodeImage = QrCodeImage ( )
2023-03-06 10:33:18 -08:00
2022-09-27 08:09:35 -07:00
var body : some View {
2022-11-08 12:48:16 -08:00
GeometryReader { bounds in
let smallest = min ( bounds . size . width , bounds . size . height )
ScrollView {
if node != nil && node ? . myInfo != nil {
2023-03-06 10:33:18 -08:00
Grid {
2022-11-08 12:48:16 -08:00
GridRow {
Spacer ( )
2022-12-29 16:36:43 -08:00
Text ( " include " )
2022-11-08 12:48:16 -08:00
. font ( . caption )
. fontWeight ( . bold )
. padding ( . trailing )
2022-12-29 16:36:43 -08:00
Text ( " channel " )
2022-11-08 12:48:16 -08:00
. font ( . caption )
. fontWeight ( . bold )
. padding ( . trailing )
2022-12-29 16:36:43 -08:00
Text ( " encrypted " )
2022-11-08 12:48:16 -08:00
. font ( . caption )
. fontWeight ( . bold )
}
2023-03-14 12:44:10 -07:00
ForEach ( node ? . myInfo ? . channels ? . array as ? [ ChannelEntity ] ? ? [ ] , id : \ . self ) { ( channel : ChannelEntity ) in
2022-09-29 07:17:07 -07:00
GridRow {
Spacer ( )
2022-11-08 12:48:16 -08:00
if channel . index = = 0 {
Toggle ( " Channel 0 Included " , isOn : $ includeChannel0 )
. toggleStyle ( . switch )
. labelsHidden ( )
2022-12-17 19:14:53 -08:00
Text ( ( ( channel . name ! . isEmpty ? " Primary " : channel . name ) ? ? " Primary " ) . camelCaseToWords ( ) )
2022-11-08 12:48:16 -08:00
if channel . psk ? . hexDescription . count ? ? 0 < 3 {
Image ( systemName : " lock.slash " )
. foregroundColor ( . red )
} else {
Image ( systemName : " lock.fill " )
. foregroundColor ( . green )
}
} else if channel . index = = 1 && channel . role > 0 {
Toggle ( " Channel 1 Included " , isOn : $ includeChannel1 )
. toggleStyle ( . switch )
. labelsHidden ( )
. disabled ( channel . role = = 1 )
Text ( ( ( channel . name ! . isEmpty ? " Channel \( channel . index ) " : channel . name ) ? ? " Channel \( channel . index ) " ) . camelCaseToWords ( ) ) . fixedSize ( )
if channel . psk ? . hexDescription . count ? ? 0 < 3 {
Image ( systemName : " lock.slash " )
. foregroundColor ( . red )
} else {
Image ( systemName : " lock.fill " )
. foregroundColor ( . green )
}
} else if channel . index = = 2 && channel . role > 0 {
Toggle ( " Channel 2 Included " , isOn : $ includeChannel2 )
. toggleStyle ( . switch )
. labelsHidden ( )
. disabled ( channel . role = = 1 )
Text ( ( ( channel . name ! . isEmpty ? " Channel \( channel . index ) " : channel . name ) ? ? " Channel \( channel . index ) " ) . camelCaseToWords ( ) ) . fixedSize ( )
if channel . psk ? . hexDescription . count ? ? 0 < 3 {
Image ( systemName : " lock.slash " )
. foregroundColor ( . red )
} else {
Image ( systemName : " lock.fill " )
. foregroundColor ( . green )
}
} else if channel . index = = 3 && channel . role > 0 {
Toggle ( " Channel 3 Included " , isOn : $ includeChannel3 )
. toggleStyle ( . switch )
. labelsHidden ( )
. disabled ( channel . role = = 1 )
Text ( ( ( channel . name ! . isEmpty ? " Channel \( channel . index ) " : channel . name ) ? ? " Channel \( channel . index ) " ) . camelCaseToWords ( ) ) . fixedSize ( )
if channel . psk ? . hexDescription . count ? ? 0 < 3 {
Image ( systemName : " lock.slash " )
. foregroundColor ( . red )
} else {
Image ( systemName : " lock.fill " )
. foregroundColor ( . green )
}
} else if channel . index = = 4 && channel . role > 0 {
Toggle ( " Channel 4 Included " , isOn : $ includeChannel4 )
. toggleStyle ( . switch )
. labelsHidden ( )
. disabled ( channel . role = = 1 )
Text ( ( ( channel . name ! . isEmpty ? " Channel \( channel . index ) " : channel . name ) ? ? " Channel \( channel . index ) " ) . camelCaseToWords ( ) ) . fixedSize ( )
if channel . psk ? . hexDescription . count ? ? 0 < 3 {
Image ( systemName : " lock.slash " )
. foregroundColor ( . red )
} else {
Image ( systemName : " lock.fill " )
. foregroundColor ( . green )
}
} else if channel . index = = 5 && channel . role > 0 {
Toggle ( " Channel 5 Included " , isOn : $ includeChannel5 )
. toggleStyle ( . switch )
. labelsHidden ( )
. disabled ( channel . role = = 1 )
Text ( ( ( channel . name ! . isEmpty ? " Channel \( channel . index ) " : channel . name ) ? ? " Channel \( channel . index ) " ) . camelCaseToWords ( ) ) . fixedSize ( )
if channel . psk ? . hexDescription . count ? ? 0 < 3 {
Image ( systemName : " lock.slash " )
. foregroundColor ( . red )
} else {
Image ( systemName : " lock.fill " )
. foregroundColor ( . green )
}
} else if channel . index = = 6 && channel . role > 0 {
Toggle ( " Channel 6 Included " , isOn : $ includeChannel6 )
. toggleStyle ( . switch )
. labelsHidden ( )
. disabled ( channel . role = = 1 )
Text ( ( ( channel . name ! . isEmpty ? " Channel \( channel . index ) " : channel . name ) ? ? " Channel \( channel . index ) " ) . camelCaseToWords ( ) ) . fixedSize ( )
if channel . psk ? . hexDescription . count ? ? 0 < 3 {
Image ( systemName : " lock.slash " )
. foregroundColor ( . red )
} else {
Image ( systemName : " lock.fill " )
. foregroundColor ( . green )
}
} else if channel . index = = 7 && channel . role > 0 {
Toggle ( " Channel 7 Included " , isOn : $ includeChannel7 )
. toggleStyle ( . switch )
. labelsHidden ( )
. disabled ( channel . role = = 1 )
Text ( ( ( channel . name ! . isEmpty ? " Channel \( channel . index ) " : channel . name ) ? ? " Channel \( channel . index ) " ) . camelCaseToWords ( ) ) . fixedSize ( )
if channel . psk ? . hexDescription . count ? ? 0 < 3 {
Image ( systemName : " lock.slash " )
. foregroundColor ( . red )
} else {
Image ( systemName : " lock.fill " )
. foregroundColor ( . green )
2022-10-12 15:26:25 -07:00
}
2022-09-29 07:17:07 -07:00
}
2022-11-08 12:48:16 -08:00
Spacer ( )
2022-09-29 07:17:07 -07:00
}
}
2022-11-08 12:48:16 -08:00
}
2022-11-05 08:26:27 -07:00
2022-11-08 12:48:16 -08:00
let qrImage = qrCodeImage . generateQRCode ( from : channelsUrl )
VStack {
if node != nil {
ShareLink ( " Share QR Code & Link " ,
item : Image ( uiImage : qrImage ) ,
subject : Text ( " Meshtastic Node \( node ? . user ? . shortName ? ? " ???? " ) has shared channels with you " ) ,
message : Text ( channelsUrl ) ,
preview : SharePreview ( " Meshtastic Node \( node ? . user ? . shortName ? ? " ???? " ) has shared channels with you " ,
image : Image ( uiImage : qrImage ) )
)
. buttonStyle ( . bordered )
. buttonBorderShape ( . capsule )
. controlSize ( . large )
. padding ( . bottom )
2023-03-06 10:33:18 -08:00
2022-11-08 12:48:16 -08:00
Image ( uiImage : qrImage )
. resizable ( )
. scaledToFit ( )
. frame (
minWidth : smallest * 0.95 ,
maxWidth : smallest * 0.95 ,
minHeight : smallest * 0.95 ,
maxHeight : smallest * 0.95 ,
alignment : . top
2022-10-15 20:22:13 -07:00
)
2022-11-08 12:48:16 -08:00
Button {
isPresentingHelp = true
} label : {
Label ( " Help Me! " , systemImage : " lifepreserver " )
2022-10-15 20:22:13 -07:00
}
2022-11-08 12:48:16 -08:00
. buttonStyle ( . bordered )
. buttonBorderShape ( . capsule )
. controlSize ( . small )
2022-10-14 22:18:28 -07:00
}
2022-10-10 07:17:57 -07:00
}
2022-09-27 08:09:35 -07:00
}
2022-11-08 12:48:16 -08:00
}
. sheet ( isPresented : $ isPresentingHelp ) {
VStack {
Text ( " Meshtastic Channels " ) . font ( . title )
Text ( " A Meshtastic LoRa Mesh network can have up to 8 distinct channels. " )
. font ( . headline )
. padding ( . bottom )
Text ( " Primary Channel " ) . font ( . title2 )
2023-03-24 10:35:54 -07:00
Text ( " The first channel is the Primary channel and is where much of the mesh activity takes place. DM's are only available on the primary channel and it can not be disabled. If you don't share your primary channel, the first channel will become the primary channel on the other network and will allow communication with your mesh on the group channel. " )
2022-11-08 12:48:16 -08:00
. font ( . callout )
2023-03-06 10:33:18 -08:00
. padding ( [ . leading , . trailing , . bottom ] )
2022-11-08 12:48:16 -08:00
Text ( " Admin Channel " ) . font ( . title2 )
Text ( " A channel with the name 'admin' is the Admin channel and can be used to remotely administer nodes on your mesh, text messages can not be sent over the admin channel. " )
. font ( . callout )
2023-03-06 10:33:18 -08:00
. padding ( [ . leading , . trailing , . bottom ] )
2022-11-08 12:48:16 -08:00
Text ( " Private Channels " ) . font ( . title2 )
Text ( " The other channels can be used for private group converations. Each of these groups has its own encryption key. " )
. font ( . callout )
2023-03-06 10:33:18 -08:00
. padding ( [ . leading , . trailing , . bottom ] )
2022-11-08 12:48:16 -08:00
Divider ( )
2022-10-10 18:59:33 -07:00
}
2022-11-08 12:48:16 -08:00
. padding ( )
. presentationDetents ( [ . large ] )
. presentationDragIndicator ( . automatic )
2023-03-06 10:33:18 -08:00
2022-12-09 19:15:15 -08:00
#if targetEnvironment ( macCatalyst )
Button {
2022-12-11 14:14:08 -08:00
isPresentingHelp = false
2022-12-09 19:15:15 -08:00
} label : {
2022-12-29 16:36:43 -08:00
Label ( " close " , systemImage : " xmark " )
2022-12-09 19:15:15 -08:00
}
. buttonStyle ( . bordered )
. buttonBorderShape ( . capsule )
. controlSize ( . large )
. padding ( )
#endif
2022-11-08 12:48:16 -08:00
}
2022-12-29 16:36:43 -08:00
. navigationTitle ( " generate.qr.code " )
2022-11-08 12:48:16 -08:00
. navigationBarTitleDisplayMode ( . inline )
. navigationBarItems ( trailing :
ZStack {
ConnectedDevice ( bluetoothOn : bleManager . isSwitchedOn , deviceConnected : bleManager . connectedPeripheral != nil , name : ( bleManager . connectedPeripheral != nil ) ? bleManager . connectedPeripheral . shortName : " ???? " )
} )
. onAppear {
bleManager . context = context
2023-03-06 13:26:04 -08:00
generateChannelSet ( )
2022-09-27 08:09:35 -07:00
}
2023-03-21 17:53:53 -07:00
. onChange ( of : includeChannel0 ) { _ in generateChannelSet ( ) }
2023-03-06 13:26:04 -08:00
. onChange ( of : includeChannel1 ) { _ in generateChannelSet ( ) }
. onChange ( of : includeChannel2 ) { _ in generateChannelSet ( ) }
. onChange ( of : includeChannel3 ) { _ in generateChannelSet ( ) }
. onChange ( of : includeChannel4 ) { _ in generateChannelSet ( ) }
. onChange ( of : includeChannel5 ) { _ in generateChannelSet ( ) }
. onChange ( of : includeChannel6 ) { _ in generateChannelSet ( ) }
. onChange ( of : includeChannel7 ) { _ in generateChannelSet ( ) }
2022-09-27 08:09:35 -07:00
}
}
2023-03-06 13:26:04 -08:00
func generateChannelSet ( ) {
2022-10-10 18:59:33 -07:00
channelSet = ChannelSet ( )
2022-10-10 13:40:18 -07:00
var loRaConfig = Config . LoRaConfig ( )
2022-10-14 22:18:28 -07:00
loRaConfig . region = RegionCodes ( rawValue : Int ( node ? . loRaConfig ? . regionCode ? ? 0 ) ) ! . protoEnumValue ( )
loRaConfig . modemPreset = ModemPresets ( rawValue : Int ( node ? . loRaConfig ? . modemPreset ? ? 0 ) ) ! . protoEnumValue ( )
loRaConfig . bandwidth = UInt32 ( node ? . loRaConfig ? . bandwidth ? ? 0 )
loRaConfig . spreadFactor = UInt32 ( node ? . loRaConfig ? . spreadFactor ? ? 0 )
loRaConfig . codingRate = UInt32 ( node ? . loRaConfig ? . codingRate ? ? 0 )
loRaConfig . frequencyOffset = node ? . loRaConfig ? . frequencyOffset ? ? 0
loRaConfig . hopLimit = UInt32 ( node ? . loRaConfig ? . hopLimit ? ? 3 )
loRaConfig . txEnabled = node ? . loRaConfig ? . txEnabled ? ? false
loRaConfig . txPower = node ? . loRaConfig ? . txPower ? ? 0
2022-10-22 13:05:54 -07:00
loRaConfig . usePreset = node ? . loRaConfig ? . usePreset ? ? true
2022-10-14 22:18:28 -07:00
loRaConfig . channelNum = UInt32 ( node ? . loRaConfig ? . channelNum ? ? 0 )
2023-03-11 09:26:52 -08:00
loRaConfig . sx126XRxBoostedGain = node ? . loRaConfig ? . sx126xRxBoostedGain ? ? false
2022-10-10 13:40:18 -07:00
channelSet . loraConfig = loRaConfig
2023-02-12 20:11:50 -08:00
if node ? . myInfo ? . channels != nil && node ? . myInfo ? . channels ? . count ? ? 0 > 0 {
2023-03-14 14:33:27 -07:00
for ch in node ? . myInfo ? . channels ? . array as ? [ ChannelEntity ] ? ? [ ] {
2022-10-14 22:18:28 -07:00
if ch . role > 0 {
2023-03-06 10:33:18 -08:00
2022-10-14 22:18:28 -07:00
if ch . index = = 0 && includeChannel0 || ch . index = = 1 && includeChannel1 || ch . index = = 2 && includeChannel2 || ch . index = = 3 && includeChannel3 ||
2023-03-06 10:33:18 -08:00
ch . index = = 4 && includeChannel4 || ch . index = = 5 && includeChannel5 || ch . index = = 6 && includeChannel6 || ch . index = = 7 && includeChannel7 {
2022-10-14 22:18:28 -07:00
var channelSettings = ChannelSettings ( )
channelSettings . name = ch . name !
channelSettings . psk = ch . psk !
channelSettings . id = UInt32 ( ch . id )
channelSettings . uplinkEnabled = ch . uplinkEnabled
channelSettings . downlinkEnabled = ch . downlinkEnabled
channelSet . settings . append ( channelSettings )
}
2022-10-10 18:59:33 -07:00
}
2022-10-10 13:40:18 -07:00
}
2022-10-14 22:18:28 -07:00
let settingsString = try ! channelSet . serializedData ( ) . base64EncodedString ( )
channelsUrl = ( " https://meshtastic.org/e/# " + settingsString . base64ToBase64url ( ) )
2022-10-10 13:40:18 -07:00
}
}
2022-09-27 08:09:35 -07:00
}