Fix app icons, refactor device pages to node pages based on new data models
|
|
@ -12,7 +12,13 @@
|
|||
DD47E3D026F1073F00029299 /* NodeRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD47E3CF26F1073F00029299 /* NodeRow.swift */; };
|
||||
DD47E3D226F1210600029299 /* HelperFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD47E3D126F1210600029299 /* HelperFunctions.swift */; };
|
||||
DD47E3D626F17ED900029299 /* CircleText.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD47E3D526F17ED900029299 /* CircleText.swift */; };
|
||||
DD47E3D926F3093800029299 /* MessageBubble.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD47E3D826F3093800029299 /* MessageBubble.swift */; };
|
||||
DD47E3DB26F3901B00029299 /* MessageList.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD47E3DA26F3901A00029299 /* MessageList.swift */; };
|
||||
DD47E3DD26F390A000029299 /* MessageDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD47E3DC26F390A000029299 /* MessageDetail.swift */; };
|
||||
DD47E3DF26F39D9F00029299 /* MyInfoModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD47E3DE26F39D9F00029299 /* MyInfoModel.swift */; };
|
||||
DD7AA3F326F05C120077AF76 /* NodeInfoModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD7AA3F226F05C120077AF76 /* NodeInfoModel.swift */; };
|
||||
DD90860C26F684AF00DC5189 /* BatteryIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD90860B26F684AF00DC5189 /* BatteryIcon.swift */; };
|
||||
DD90860E26F69BAE00DC5189 /* NodeMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD90860D26F69BAE00DC5189 /* NodeMap.swift */; };
|
||||
DDAF8C5326EB1DF10058C060 /* BLEManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDAF8C5226EB1DF10058C060 /* BLEManager.swift */; };
|
||||
DDAF8C5826ED07FD0058C060 /* mesh.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDAF8C5726ED07FD0058C060 /* mesh.pb.swift */; };
|
||||
DDAF8C5B26ED08D30058C060 /* SwiftProtobuf in Frameworks */ = {isa = PBXBuildFile; productRef = DDAF8C5A26ED08D30058C060 /* SwiftProtobuf */; };
|
||||
|
|
@ -33,10 +39,6 @@
|
|||
DDC2E17A26CE248F0042C5E4 /* MeshtasticClientUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC2E17926CE248F0042C5E4 /* MeshtasticClientUITests.swift */; };
|
||||
DDC2E18F26CE25FE0042C5E4 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC2E18E26CE25FE0042C5E4 /* ContentView.swift */; };
|
||||
DDC2E19126CE26290042C5E4 /* Messages.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC2E19026CE26290042C5E4 /* Messages.swift */; };
|
||||
DDC2E19326CE266B0042C5E4 /* DeviceHome.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC2E19226CE266B0042C5E4 /* DeviceHome.swift */; };
|
||||
DDC2E19526CE26760042C5E4 /* DeviceDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC2E19426CE26760042C5E4 /* DeviceDetail.swift */; };
|
||||
DDC2E19726CE26840042C5E4 /* DeviceRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC2E19626CE26840042C5E4 /* DeviceRow.swift */; };
|
||||
DDC2E19926CE26940042C5E4 /* DeviceMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC2E19826CE26940042C5E4 /* DeviceMap.swift */; };
|
||||
DDC2E19B26CE27150042C5E4 /* CircleImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC2E19A26CE27150042C5E4 /* CircleImage.swift */; };
|
||||
DDC2E19D26CE27580042C5E4 /* ModelData.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC2E19C26CE27580042C5E4 /* ModelData.swift */; };
|
||||
DDC2E19F26CE27630042C5E4 /* Device.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC2E19E26CE27630042C5E4 /* Device.swift */; };
|
||||
|
|
@ -69,8 +71,15 @@
|
|||
DD47E3CF26F1073F00029299 /* NodeRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeRow.swift; sourceTree = "<group>"; };
|
||||
DD47E3D126F1210600029299 /* HelperFunctions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HelperFunctions.swift; sourceTree = "<group>"; };
|
||||
DD47E3D526F17ED900029299 /* CircleText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircleText.swift; sourceTree = "<group>"; };
|
||||
DD47E3D826F3093800029299 /* MessageBubble.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageBubble.swift; sourceTree = "<group>"; };
|
||||
DD47E3DA26F3901A00029299 /* MessageList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageList.swift; sourceTree = "<group>"; };
|
||||
DD47E3DC26F390A000029299 /* MessageDetail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageDetail.swift; sourceTree = "<group>"; };
|
||||
DD47E3DE26F39D9F00029299 /* MyInfoModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyInfoModel.swift; sourceTree = "<group>"; };
|
||||
DD7AA3F226F05C120077AF76 /* NodeInfoModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeInfoModel.swift; sourceTree = "<group>"; };
|
||||
DD7AA3F426F05D660077AF76 /* nodeInfoModel.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = nodeInfoModel.json; sourceTree = "<group>"; };
|
||||
DD90860A26F645B700DC5189 /* MeshtasticClient.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = MeshtasticClient.entitlements; sourceTree = "<group>"; };
|
||||
DD90860B26F684AF00DC5189 /* BatteryIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatteryIcon.swift; sourceTree = "<group>"; };
|
||||
DD90860D26F69BAE00DC5189 /* NodeMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeMap.swift; sourceTree = "<group>"; };
|
||||
DDAF8C5226EB1DF10058C060 /* BLEManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BLEManager.swift; sourceTree = "<group>"; };
|
||||
DDAF8C5726ED07FD0058C060 /* mesh.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = mesh.pb.swift; sourceTree = "<group>"; };
|
||||
DDAF8C5C26ED09490058C060 /* portnums.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = portnums.pb.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -98,10 +107,6 @@
|
|||
DDC2E18A26CE25690042C5E4 /* deviceData.json */ = {isa = PBXFileReference; explicitFileType = text.json; fileEncoding = 4; path = deviceData.json; sourceTree = "<group>"; };
|
||||
DDC2E18E26CE25FE0042C5E4 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
||||
DDC2E19026CE26290042C5E4 /* Messages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Messages.swift; sourceTree = "<group>"; };
|
||||
DDC2E19226CE266B0042C5E4 /* DeviceHome.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceHome.swift; sourceTree = "<group>"; };
|
||||
DDC2E19426CE26760042C5E4 /* DeviceDetail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceDetail.swift; sourceTree = "<group>"; };
|
||||
DDC2E19626CE26840042C5E4 /* DeviceRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceRow.swift; sourceTree = "<group>"; };
|
||||
DDC2E19826CE26940042C5E4 /* DeviceMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceMap.swift; sourceTree = "<group>"; };
|
||||
DDC2E19A26CE27150042C5E4 /* CircleImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircleImage.swift; sourceTree = "<group>"; };
|
||||
DDC2E19C26CE27580042C5E4 /* ModelData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModelData.swift; sourceTree = "<group>"; };
|
||||
DDC2E19E26CE27630042C5E4 /* Device.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Device.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -142,10 +147,18 @@
|
|||
DD47E3CB26F0E51D00029299 /* NodeDetail.swift */,
|
||||
DD47E3CD26F103C600029299 /* NodeList.swift */,
|
||||
DD47E3CF26F1073F00029299 /* NodeRow.swift */,
|
||||
DD90860D26F69BAE00DC5189 /* NodeMap.swift */,
|
||||
);
|
||||
path = Nodes;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DD47E3D726F2F21A00029299 /* BlueTooth */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
);
|
||||
path = BlueTooth;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DDAF8C5626ED07740058C060 /* Protobufs */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
|
@ -185,6 +198,7 @@
|
|||
DDC2E15626CE248E0042C5E4 /* MeshtasticClient */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DD90860A26F645B700DC5189 /* MeshtasticClient.entitlements */,
|
||||
DDAF8C5626ED07740058C060 /* Protobufs */,
|
||||
DDC2E1A526CEB32B0042C5E4 /* Helpers */,
|
||||
DDC2E18726CE24E40042C5E4 /* Views */,
|
||||
|
|
@ -227,6 +241,7 @@
|
|||
DDC2E18726CE24E40042C5E4 /* Views */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DD47E3D726F2F21A00029299 /* BlueTooth */,
|
||||
DD47E3CA26F0E50300029299 /* Nodes */,
|
||||
DDC2E18C26CE25B00042C5E4 /* Devices */,
|
||||
DDC2E18B26CE25A70042C5E4 /* Messages */,
|
||||
|
|
@ -243,6 +258,7 @@
|
|||
DDC2E19E26CE27630042C5E4 /* Device.swift */,
|
||||
DDAF8C6F26ED1DD20058C060 /* Device2.swift */,
|
||||
DD7AA3F226F05C120077AF76 /* NodeInfoModel.swift */,
|
||||
DD47E3DE26F39D9F00029299 /* MyInfoModel.swift */,
|
||||
);
|
||||
path = Model;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -262,6 +278,8 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
DDC2E19026CE26290042C5E4 /* Messages.swift */,
|
||||
DD47E3DA26F3901A00029299 /* MessageList.swift */,
|
||||
DD47E3DC26F390A000029299 /* MessageDetail.swift */,
|
||||
);
|
||||
path = Messages;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -269,10 +287,6 @@
|
|||
DDC2E18C26CE25B00042C5E4 /* Devices */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DDC2E19226CE266B0042C5E4 /* DeviceHome.swift */,
|
||||
DDC2E19426CE26760042C5E4 /* DeviceDetail.swift */,
|
||||
DDC2E19626CE26840042C5E4 /* DeviceRow.swift */,
|
||||
DDC2E19826CE26940042C5E4 /* DeviceMap.swift */,
|
||||
DDC2E1A326CE2F940042C5E4 /* DeviceBLE.swift */,
|
||||
);
|
||||
path = Devices;
|
||||
|
|
@ -283,6 +297,8 @@
|
|||
children = (
|
||||
DDC2E19A26CE27150042C5E4 /* CircleImage.swift */,
|
||||
DD47E3D526F17ED900029299 /* CircleText.swift */,
|
||||
DD47E3D826F3093800029299 /* MessageBubble.swift */,
|
||||
DD90860B26F684AF00DC5189 /* BatteryIcon.swift */,
|
||||
);
|
||||
path = Helpers;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -442,28 +458,30 @@
|
|||
DD7AA3F326F05C120077AF76 /* NodeInfoModel.swift in Sources */,
|
||||
DDC2E19F26CE27630042C5E4 /* Device.swift in Sources */,
|
||||
DD47E3D226F1210600029299 /* HelperFunctions.swift in Sources */,
|
||||
DDC2E19926CE26940042C5E4 /* DeviceMap.swift in Sources */,
|
||||
DDC2E19526CE26760042C5E4 /* DeviceDetail.swift in Sources */,
|
||||
DDAF8C5F26ED09B50058C060 /* radioconfig.pb.swift in Sources */,
|
||||
DDC2E19726CE26840042C5E4 /* DeviceRow.swift in Sources */,
|
||||
DDAF8C5326EB1DF10058C060 /* BLEManager.swift in Sources */,
|
||||
DD90860E26F69BAE00DC5189 /* NodeMap.swift in Sources */,
|
||||
DDC2E19126CE26290042C5E4 /* Messages.swift in Sources */,
|
||||
DD47E3DB26F3901B00029299 /* MessageList.swift in Sources */,
|
||||
DDAF8C6926ED0D070058C060 /* deviceonly.pb.swift in Sources */,
|
||||
DDC2E19D26CE27580042C5E4 /* ModelData.swift in Sources */,
|
||||
DDAF8C6B26ED0DD80058C060 /* environmental_measurement.pb.swift in Sources */,
|
||||
DD90860C26F684AF00DC5189 /* BatteryIcon.swift in Sources */,
|
||||
DDAF8C6226ED0A230058C060 /* mqtt.pb.swift in Sources */,
|
||||
DDAF8C5D26ED09490058C060 /* portnums.pb.swift in Sources */,
|
||||
DD47E3DF26F39D9F00029299 /* MyInfoModel.swift in Sources */,
|
||||
DD47E3CE26F103C600029299 /* NodeList.swift in Sources */,
|
||||
DD47E3D026F1073F00029299 /* NodeRow.swift in Sources */,
|
||||
DD47E3D626F17ED900029299 /* CircleText.swift in Sources */,
|
||||
DDC2E18F26CE25FE0042C5E4 /* ContentView.swift in Sources */,
|
||||
DDAF8C6326ED0A230058C060 /* admin.pb.swift in Sources */,
|
||||
DDAF8C5826ED07FD0058C060 /* mesh.pb.swift in Sources */,
|
||||
DDC2E19326CE266B0042C5E4 /* DeviceHome.swift in Sources */,
|
||||
DD47E3D926F3093800029299 /* MessageBubble.swift in Sources */,
|
||||
DDC2E19B26CE27150042C5E4 /* CircleImage.swift in Sources */,
|
||||
DDAF8C6726ED0C8C0058C060 /* remote_hardware.pb.swift in Sources */,
|
||||
DDC2E1A426CE2F940042C5E4 /* DeviceBLE.swift in Sources */,
|
||||
DDAF8C6526ED0A490058C060 /* channel.pb.swift in Sources */,
|
||||
DD47E3DD26F390A000029299 /* MessageDetail.swift in Sources */,
|
||||
DDC2E15826CE248E0042C5E4 /* MeshtasticClientApp.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
|
@ -621,6 +639,7 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_ENTITLEMENTS = MeshtasticClient/MeshtasticClient.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"MeshtasticClient/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = GCH7VS5Y9R;
|
||||
|
|
@ -631,8 +650,10 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.04;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
|
|
@ -643,6 +664,7 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_ENTITLEMENTS = MeshtasticClient/MeshtasticClient.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"MeshtasticClient/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = GCH7VS5Y9R;
|
||||
|
|
@ -653,8 +675,10 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.04;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
|
|
|
|||
BIN
MeshtasticClient/Assets.xcassets/AppIcon.appiconset/1024.png
Normal file
|
After Width: | Height: | Size: 99 KiB |
BIN
MeshtasticClient/Assets.xcassets/AppIcon.appiconset/120-1.png
Normal file
|
After Width: | Height: | Size: 5.5 KiB |
BIN
MeshtasticClient/Assets.xcassets/AppIcon.appiconset/120.png
Normal file
|
After Width: | Height: | Size: 5.5 KiB |
BIN
MeshtasticClient/Assets.xcassets/AppIcon.appiconset/152.png
Normal file
|
After Width: | Height: | Size: 7 KiB |
BIN
MeshtasticClient/Assets.xcassets/AppIcon.appiconset/167.png
Normal file
|
After Width: | Height: | Size: 8.2 KiB |
BIN
MeshtasticClient/Assets.xcassets/AppIcon.appiconset/180.png
Normal file
|
After Width: | Height: | Size: 8.6 KiB |
BIN
MeshtasticClient/Assets.xcassets/AppIcon.appiconset/20.png
Normal file
|
After Width: | Height: | Size: 858 B |
BIN
MeshtasticClient/Assets.xcassets/AppIcon.appiconset/29.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
MeshtasticClient/Assets.xcassets/AppIcon.appiconset/40-1.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
MeshtasticClient/Assets.xcassets/AppIcon.appiconset/40-2.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
MeshtasticClient/Assets.xcassets/AppIcon.appiconset/40.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
MeshtasticClient/Assets.xcassets/AppIcon.appiconset/58-1.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
MeshtasticClient/Assets.xcassets/AppIcon.appiconset/58.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
MeshtasticClient/Assets.xcassets/AppIcon.appiconset/60.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
MeshtasticClient/Assets.xcassets/AppIcon.appiconset/76.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
MeshtasticClient/Assets.xcassets/AppIcon.appiconset/80-1.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
MeshtasticClient/Assets.xcassets/AppIcon.appiconset/80.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
MeshtasticClient/Assets.xcassets/AppIcon.appiconset/87.png
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
|
|
@ -1,97 +1,109 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "40-2.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "play_store_icon_114px-4.png",
|
||||
"filename" : "60.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "play_store_icon_114px-3.png",
|
||||
"filename" : "58.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "87.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "80.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "play_store_icon_114px-1.png",
|
||||
"filename" : "120-1.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "play_store_icon_114px.png",
|
||||
"filename" : "120.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "60x60"
|
||||
},
|
||||
{
|
||||
"filename" : "play_store_icon_114px-2.png",
|
||||
"filename" : "180.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "60x60"
|
||||
},
|
||||
{
|
||||
"filename" : "20.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "40-1.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "29.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "58-1.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "40.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "80-1.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "play_store_icon_114px-5.png",
|
||||
"filename" : "76.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "76x76"
|
||||
},
|
||||
{
|
||||
"filename" : "152.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "76x76"
|
||||
},
|
||||
{
|
||||
"filename" : "167.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "83.5x83.5"
|
||||
},
|
||||
{
|
||||
"filename" : "1024.png",
|
||||
"idiom" : "ios-marketing",
|
||||
"scale" : "1x",
|
||||
"size" : "1024x1024"
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 8.3 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 8.3 KiB |
|
|
@ -5,11 +5,12 @@
|
|||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "RAK7205_Enclosure-With-Solar-Panel_Top-View_01_9ed42002-fb51-4c49-a69e-43fcef692ef6_739x@2x.progressive-1.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "rak4631@3x.png",
|
||||
"filename" : "RAK7205_Enclosure-With-Solar-Panel_Top-View_01_9ed42002-fb51-4c49-a69e-43fcef692ef6_739x@2x.progressive.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
|
|
|
|||
|
After Width: | Height: | Size: 586 KiB |
|
After Width: | Height: | Size: 586 KiB |
|
Before Width: | Height: | Size: 680 KiB |
|
|
@ -1,15 +1,17 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "tbeam-2.jpg",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "tbeam-1.jpg",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "TTGO T-Beam ESP32 LoRa 923MHz+OLED WiFi GPS NEO-M8N 18650-1-550x550.jpg",
|
||||
"filename" : "tbeam.jpg",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 45 KiB |
BIN
MeshtasticClient/Assets.xcassets/tbeam.imageset/tbeam-1.jpg
vendored
Normal file
|
After Width: | Height: | Size: 244 KiB |
BIN
MeshtasticClient/Assets.xcassets/tbeam.imageset/tbeam-2.jpg
vendored
Normal file
|
After Width: | Height: | Size: 244 KiB |
BIN
MeshtasticClient/Assets.xcassets/tbeam.imageset/tbeam.jpg
vendored
Normal file
|
After Width: | Height: | Size: 244 KiB |
|
|
@ -17,13 +17,21 @@
|
|||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<string>$(MARKETING_VERSION)</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.utilities</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSBluetoothAlwaysUsageDescription</key>
|
||||
<string>We use bluetooth to connect to nearby Meshtastic Devices</string>
|
||||
<key>NSBluetoothPeripheralUsageDescription</key>
|
||||
<string>Bluetooth is used to connect an iPhone to a user's meshtastic device to allow text messaging and location data for the mesh network.</string>
|
||||
<key>NSLocationWhenInUseUsageDescription</key>
|
||||
<string>We use your location to center maps of the mesh</string>
|
||||
<key>Privacy – Bluetooth Always Usage Description</key>
|
||||
<string>We use bluetooth to connect to nearby Meshtastic Devices</string>
|
||||
<key>UIApplicationSceneManifest</key>
|
||||
<dict>
|
||||
<key>UIApplicationSupportsMultipleScenes</key>
|
||||
|
|
@ -37,16 +45,14 @@
|
|||
<array>
|
||||
<string>armv7</string>
|
||||
</array>
|
||||
<key>NSBluetoothAlwaysUsageDescription</key>
|
||||
<string>We use bluetooth to connect to nearby Meshtastic Devices</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>Privacy – Bluetooth Always Usage Description</key>
|
||||
<string>We use bluetooth to connect to nearby Meshtastic Devices</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
|
|
|
|||
14
MeshtasticClient/MeshtasticClient.entitlements
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.device.bluetooth</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
<key>com.apple.security.personal-information.location</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
22
MeshtasticClient/Model/MyInfoModel.swift
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
//
|
||||
// MyInfoModel.swift
|
||||
// MeshtasticClient
|
||||
//
|
||||
// Created by Garth Vander Houwen on 9/16/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct MyInfoModel: Hashable, Codable, Identifiable {
|
||||
|
||||
let id = UUID()
|
||||
var myNodeNum: UInt32
|
||||
var hasGps: Bool
|
||||
var numBands: UInt32
|
||||
var maxChannels: UInt32
|
||||
var firmwareVersion: String
|
||||
var rebootCount: UInt32
|
||||
var messageTimeoutMsec: UInt32
|
||||
var minAppVersion: UInt32
|
||||
|
||||
}
|
||||
|
|
@ -26,7 +26,37 @@ struct NodeInfoModel: Hashable, Codable, Identifiable {
|
|||
var position: Position
|
||||
struct Position: Hashable, Codable {
|
||||
var latitudeI: Int32?
|
||||
var latitude: Double? {
|
||||
if let unwrappedLat = latitudeI {
|
||||
let d = Double(unwrappedLat)
|
||||
|
||||
return d / 10000000
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
var longitudeI: Int32?
|
||||
var longitude: Double? {
|
||||
if let unwrappedLong = longitudeI {
|
||||
let d = Double(unwrappedLong)
|
||||
|
||||
return d / 10000000
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
var coordinate: CLLocationCoordinate2D? {
|
||||
if longitude != nil {
|
||||
let coord = CLLocationCoordinate2D(latitude: latitude!, longitude: longitude!)
|
||||
|
||||
return coord
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
var altitude: Int32?
|
||||
var batteryLevel: Int32?
|
||||
var time: Int32?
|
||||
|
|
|
|||
|
|
@ -5,14 +5,11 @@ Abstract: Default App View
|
|||
import SwiftUI
|
||||
|
||||
struct ContentView: View {
|
||||
@State private var selection: Tab = .ble
|
||||
@State private var selection: Tab = .nodes
|
||||
|
||||
enum Tab {
|
||||
case messages
|
||||
case devices
|
||||
case map
|
||||
case featured
|
||||
case list
|
||||
case ble
|
||||
case nodes
|
||||
}
|
||||
|
|
@ -24,16 +21,11 @@ struct ContentView: View {
|
|||
Label("Messages", systemImage: "message")
|
||||
}
|
||||
.tag(Tab.messages)
|
||||
DeviceMap()
|
||||
NodeMap()
|
||||
.tabItem {
|
||||
Label("Mesh Map", systemImage: "map")
|
||||
}
|
||||
.tag(Tab.map)
|
||||
DeviceHome()
|
||||
.tabItem {
|
||||
Label("Devices", systemImage: "flipphone")
|
||||
}
|
||||
.tag(Tab.devices)
|
||||
NodeList()
|
||||
.tabItem {
|
||||
Label("Nodes", systemImage: "flipphone")
|
||||
|
|
|
|||
|
|
@ -24,86 +24,89 @@ struct DeviceBLE: View {
|
|||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
|
||||
VStack {
|
||||
List {
|
||||
Section(header: Text("Connected Device").font(.title)) {
|
||||
if(bleManager.connectedPeripheral != nil){
|
||||
HStack{
|
||||
Image(systemName: "dot.radiowaves.left.and.right").imageScale(.large).foregroundColor(.green)
|
||||
Text(bleManager.connectedPeripheral.name!).font(.title2)
|
||||
}
|
||||
}
|
||||
else {
|
||||
Text("No device connected").font(.title2)
|
||||
}
|
||||
|
||||
}.textCase(nil)
|
||||
Section(header: Text("Other Meshtastic Devices").font(.title)) {
|
||||
ForEach(bleManager.peripherals.sorted(by: { $0.rssi > $1.rssi })) { peripheral in
|
||||
HStack {
|
||||
Image(systemName: "circle.fill").imageScale(.large).foregroundColor(.gray)
|
||||
Button(action: {
|
||||
self.bleManager.stopScanning()
|
||||
self.bleManager.disconnectDevice()
|
||||
self.bleManager.connectToDevice(id: peripheral.id)
|
||||
}) {
|
||||
Text(peripheral.name).font(.title2)
|
||||
if bleManager.isSwitchedOn {
|
||||
|
||||
List {
|
||||
Section(header: Text("Connected Device").font(.largeTitle)) {
|
||||
if(bleManager.connectedPeripheral != nil){
|
||||
HStack{
|
||||
Image(systemName: "dot.radiowaves.left.and.right").imageScale(.large).foregroundColor(.green)
|
||||
Text(bleManager.connectedPeripheral.name!).font(.title)
|
||||
}
|
||||
Spacer()
|
||||
Text(String(peripheral.rssi) + " dB").font(.title3)
|
||||
}
|
||||
else {
|
||||
Text("No device connected").font(.title)
|
||||
}
|
||||
|
||||
}.textCase(nil)
|
||||
|
||||
Section(header: Text("Other Meshtastic Devices").font(.title)) {
|
||||
ForEach(bleManager.peripherals.sorted(by: { $0.rssi > $1.rssi })) { peripheral in
|
||||
HStack {
|
||||
Image(systemName: "circle.fill").imageScale(.large).foregroundColor(.gray)
|
||||
Button(action: {
|
||||
self.bleManager.stopScanning()
|
||||
self.bleManager.disconnectDevice()
|
||||
self.bleManager.connectToDevice(id: peripheral.id)
|
||||
}) {
|
||||
Text(peripheral.name).font(.title2)
|
||||
}
|
||||
Spacer()
|
||||
Text(String(peripheral.rssi) + " dB").font(.title3)
|
||||
}
|
||||
}
|
||||
}.textCase(nil)
|
||||
}
|
||||
|
||||
HStack (alignment: .center) {
|
||||
Spacer()
|
||||
Button(action: {
|
||||
self.bleManager.startScanning()
|
||||
}) {
|
||||
Image(systemName: "play.fill").imageScale(.large).foregroundColor(.gray)
|
||||
Text("Start Scanning").font(.caption)
|
||||
.font(.caption)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
}.textCase(nil)
|
||||
}
|
||||
Spacer()
|
||||
HStack (spacing: 15) {
|
||||
Button(action: {
|
||||
self.bleManager.startScanning()
|
||||
}) {
|
||||
Image(systemName: "play.fill").imageScale(.large).foregroundColor(.gray)
|
||||
Text("Start Scanning").font(.caption)
|
||||
.font(.caption)
|
||||
.padding()
|
||||
.background(Color(.systemGray6))
|
||||
.clipShape(Capsule())
|
||||
Spacer()
|
||||
Button(action: {
|
||||
self.bleManager.stopScanning()
|
||||
}) {
|
||||
Image(systemName: "stop.fill").imageScale(.large).foregroundColor(.gray)
|
||||
Text("Stop Scanning")
|
||||
.font(.caption)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
.padding()
|
||||
.background(Color(.systemGray6))
|
||||
.clipShape(Capsule())
|
||||
Spacer(minLength: 10)
|
||||
Button(action: {
|
||||
self.bleManager.stopScanning()
|
||||
}) {
|
||||
Image(systemName: "stop.fill").imageScale(.large).foregroundColor(.gray)
|
||||
Text("Stop Scanning")
|
||||
.font(.caption)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
.padding()
|
||||
.background(Color(.systemGray6))
|
||||
.clipShape(Capsule())
|
||||
}.padding()
|
||||
Spacer()
|
||||
}
|
||||
.navigationTitle("Nearby BLE Devices")
|
||||
.navigationBarItems(leading:
|
||||
HStack {
|
||||
Button(action: {
|
||||
self.bleManager.startScanning()
|
||||
}) {
|
||||
Image(systemName: "arrow.clockwise.circle").imageScale(.large)
|
||||
}}, trailing:
|
||||
HStack {
|
||||
if bleManager.isSwitchedOn {
|
||||
Text("Bluetooth: ON")
|
||||
.foregroundColor(.green)
|
||||
.font(.caption2)
|
||||
}
|
||||
else {
|
||||
Text("Bluetooth: OFF")
|
||||
.foregroundColor(.red)
|
||||
.font(.caption2)
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
.background(Color(.systemGray6))
|
||||
.clipShape(Capsule())
|
||||
Spacer()
|
||||
}.padding(.bottom, 25)
|
||||
|
||||
}
|
||||
)
|
||||
else {
|
||||
Text("Bluetooth: OFF")
|
||||
.foregroundColor(.red)
|
||||
.font(.title)
|
||||
}
|
||||
}
|
||||
.navigationTitle("Bluetooth Radios")
|
||||
//.navigationBarItems(leading:
|
||||
//HStack {
|
||||
//Button(action: {
|
||||
// self.bleManager.startScanning()
|
||||
//}) {
|
||||
// Image(systemName: "arrow.clockwise.circle").imageScale(.large)
|
||||
//}}, trailing:
|
||||
//HStack {
|
||||
|
||||
//}
|
||||
//)
|
||||
}.navigationViewStyle(StackNavigationViewStyle())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,143 +0,0 @@
|
|||
/*
|
||||
See LICENSE folder for this sample’s licensing information.
|
||||
|
||||
Abstract:
|
||||
A view showing the details for a device.
|
||||
*/
|
||||
|
||||
import SwiftUI
|
||||
import MapKit
|
||||
import CoreLocation
|
||||
import CoreBluetooth
|
||||
|
||||
struct DeviceDetail: View {
|
||||
@EnvironmentObject var modelData: ModelData
|
||||
var device: Device
|
||||
|
||||
var deviceIndex: Int {
|
||||
modelData.devices.firstIndex(where: { $0.id == device.id })!
|
||||
}
|
||||
struct MapLocation: Identifiable {
|
||||
let id = UUID()
|
||||
let name: String
|
||||
let coordinate: CLLocationCoordinate2D
|
||||
}
|
||||
var body: some View {
|
||||
|
||||
let currentCoordinatePosition = CLLocationCoordinate2D(latitude: device.position.latitude, longitude: device.position.longitude)
|
||||
let regionBinding = Binding<MKCoordinateRegion>(
|
||||
get: {
|
||||
MKCoordinateRegion(center: currentCoordinatePosition, span: MKCoordinateSpan(latitudeDelta: 0.005, longitudeDelta: 0.005))
|
||||
},
|
||||
set: { _ in }
|
||||
)
|
||||
|
||||
VStack{
|
||||
// Map or Device Image
|
||||
if(device.hasGPS) {
|
||||
|
||||
let annotations = [
|
||||
MapLocation(name: device.shortName, coordinate: CLLocationCoordinate2D(latitude:device.position.latitude, longitude: device.position.longitude))
|
||||
]
|
||||
|
||||
Map(coordinateRegion: regionBinding, annotationItems: annotations) { location in
|
||||
MapAnnotation(
|
||||
coordinate: location.coordinate,
|
||||
content: {
|
||||
Text(device.shortName).font(.subheadline).foregroundColor(.white)
|
||||
.background(Circle()
|
||||
.fill(Color.blue)
|
||||
.frame(width: 40, height: 40))
|
||||
}
|
||||
)
|
||||
}.frame(minHeight: 150, maxHeight: 1000)
|
||||
}
|
||||
else
|
||||
{
|
||||
device.image
|
||||
.resizable()
|
||||
.frame(minHeight: 300, maxHeight: 1000)
|
||||
}
|
||||
}
|
||||
|
||||
ZStack {
|
||||
|
||||
VStack(alignment: .leading) {
|
||||
|
||||
HStack {
|
||||
if(device.hasGPS){
|
||||
device.image
|
||||
.resizable()
|
||||
.frame(width: 100, height: 100)
|
||||
}
|
||||
Text(device.longName).font(.largeTitle)
|
||||
}
|
||||
Divider()
|
||||
HStack{
|
||||
Image(systemName: "clock").font(.title3).foregroundColor(.blue)
|
||||
let lastHeard = Date(timeIntervalSince1970: device.lastHeard)
|
||||
Text("Last Heard:").font(.headline)
|
||||
Text(lastHeard, style: .date).font(.subheadline)
|
||||
Text(lastHeard, style: .time).font(.subheadline)
|
||||
}
|
||||
Divider()
|
||||
HStack {
|
||||
Text("Meshtastic Version: " + device.firmwareVersion)
|
||||
Spacer()
|
||||
Text("Id: " + device.id)
|
||||
}
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
HStack {
|
||||
Text("Hardware Model: " + device.hardwareModel)
|
||||
Spacer()
|
||||
Text("Region: " + device.region)
|
||||
|
||||
}
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
Divider()
|
||||
HStack(alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/, spacing: 14) {
|
||||
Image(systemName: "antenna.radiowaves.left.and.right").font(.title).foregroundColor(.blue)
|
||||
VStack(alignment: .leading) {
|
||||
|
||||
Text("AKA").font(.caption)
|
||||
Text(device.shortName).font(.caption2).foregroundColor(.gray)
|
||||
}
|
||||
VStack(alignment: .leading) {
|
||||
|
||||
Text("Latitude").font(.caption)
|
||||
Text(String(format: "%.4f", device.position.latitude) + "°").font(.caption2).foregroundColor(.gray)
|
||||
}
|
||||
VStack(alignment: .leading) {
|
||||
|
||||
Text("Longitude").font(.caption)
|
||||
let fourDecimalPlaces = String(format: "%.4f", device.position.longitude)
|
||||
Text(String(fourDecimalPlaces) + "°").font(.caption2).foregroundColor(.gray)
|
||||
}
|
||||
VStack(alignment: .leading) {
|
||||
|
||||
Text("Altitude").font(.caption)
|
||||
Text(String(device.position.altitude) + " m").font(.caption2).foregroundColor(.gray)
|
||||
}
|
||||
VStack(alignment: .leading) {
|
||||
Text("Battery").font(.caption)
|
||||
Text(String(device.position.batteryLevel) + "%").font(.caption2).foregroundColor(.gray)
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
.navigationTitle(device.longName)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
}
|
||||
}
|
||||
|
||||
struct DeviceDetail_Previews: PreviewProvider {
|
||||
static let modelData = ModelData()
|
||||
|
||||
static var previews: some View {
|
||||
DeviceDetail(device: modelData.devices[0])
|
||||
.environmentObject(modelData)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
//
|
||||
// DeviceHome.swift
|
||||
// MeshtasticClient
|
||||
//
|
||||
// Created by Garth Vander Houwen on 8/7/21.
|
||||
//
|
||||
|
||||
// Abstract:
|
||||
// A view showing a list of devices that have been seen on the mesh network
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct DeviceHome: View {
|
||||
@EnvironmentObject var modelData: ModelData
|
||||
@State private var showGPSOnly = false
|
||||
|
||||
var filteredDevices: [Device] {
|
||||
modelData.devices.filter { device in
|
||||
(!showGPSOnly || device.hasGPS)
|
||||
}
|
||||
}
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
|
||||
List {
|
||||
Toggle(isOn: $showGPSOnly) {
|
||||
Text("GPS only")
|
||||
}
|
||||
|
||||
ForEach(filteredDevices) { device in
|
||||
NavigationLink(destination: DeviceDetail(device: device)) {
|
||||
DeviceRow(device: device)
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle("All Devices")
|
||||
|
||||
|
||||
}.navigationViewStyle(StackNavigationViewStyle()) // Force Full screen master details
|
||||
}
|
||||
}
|
||||
|
||||
struct DeviceHome_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
DeviceHome()
|
||||
.environmentObject(ModelData())
|
||||
}
|
||||
}
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
//
|
||||
// DeviceMap.swift
|
||||
// MeshtasticClient
|
||||
//
|
||||
// Created by Garth Vander Houwen on 8/7/21.
|
||||
//
|
||||
// Abstract:
|
||||
// A single row to be displayed in a list of landmarks.
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct DeviceRow: View {
|
||||
var device: Device
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
|
||||
device.image.resizable().frame(width: 150, height: 150)
|
||||
|
||||
VStack(alignment: .leading) {
|
||||
|
||||
Text(device.longName).font(.title2)
|
||||
HStack {
|
||||
if device.hasGPS {
|
||||
Image(systemName: "location.fill.viewfinder")
|
||||
.foregroundColor(.blue).font(.title3)
|
||||
}
|
||||
if device.isRouter {
|
||||
Image(systemName: "dot.radiowaves.left.and.right")
|
||||
.foregroundColor(.blue).font(.title3)
|
||||
}
|
||||
if device.hardwareModel == "TBEAM" || device.hardwareModel == "TLORA" {
|
||||
Image(systemName: "wifi")
|
||||
.foregroundColor(.blue).font(.title3)
|
||||
}
|
||||
if false {
|
||||
Image(systemName: "rectangle.connected.to.line.below")
|
||||
.foregroundColor(.green).font(.title2)
|
||||
}
|
||||
}
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct DeviceRow_Previews: PreviewProvider {
|
||||
static var devices = ModelData().devices
|
||||
|
||||
static var previews: some View {
|
||||
Group {
|
||||
DeviceRow(device: devices[0])
|
||||
DeviceRow(device: devices[1])
|
||||
}
|
||||
.previewLayout(.fixed(width: 300, height: 70))
|
||||
}
|
||||
}
|
||||
47
MeshtasticClient/Views/Helpers/BatteryIcon.swift
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import SwiftUI
|
||||
|
||||
struct BatteryIcon: View {
|
||||
var batteryLevel: Int32?
|
||||
var font: Font
|
||||
var color: Color
|
||||
|
||||
var body: some View {
|
||||
|
||||
if batteryLevel == 100 {
|
||||
|
||||
Image(systemName: "battery.100")
|
||||
.font(font)
|
||||
.foregroundColor(color)
|
||||
}
|
||||
else if batteryLevel! < 100 && batteryLevel! > 74 {
|
||||
|
||||
Image(systemName: "battery.75")
|
||||
.font(font)
|
||||
.foregroundColor(color)
|
||||
}
|
||||
else if batteryLevel! < 75 && batteryLevel! > 49 {
|
||||
|
||||
Image(systemName: "battery.50")
|
||||
.font(font)
|
||||
.foregroundColor(color)
|
||||
}
|
||||
else if batteryLevel! < 50 && batteryLevel! > 24 {
|
||||
|
||||
Image(systemName: "battery.25")
|
||||
.font(font)
|
||||
.foregroundColor(color)
|
||||
}
|
||||
else {
|
||||
|
||||
Image(systemName: "battery.0")
|
||||
.font(font)
|
||||
.foregroundColor(color)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct BatteryIcon_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
BatteryIcon(batteryLevel: 100, font: .title2, color: Color.blue)
|
||||
}
|
||||
}
|
||||
|
|
@ -9,10 +9,11 @@ import SwiftUI
|
|||
|
||||
struct CircleImage: View {
|
||||
var image: Image
|
||||
|
||||
|
||||
var body: some View {
|
||||
image
|
||||
.resizable()
|
||||
//.resizable()
|
||||
.clipShape(/*@START_MENU_TOKEN@*/Circle()/*@END_MENU_TOKEN@*/)
|
||||
.overlay(Circle().stroke(Color.white, lineWidth: 4))
|
||||
.shadow(radius: 7)
|
||||
|
|
|
|||
|
|
@ -1,26 +1,25 @@
|
|||
/*
|
||||
See LICENSE folder for this sample’s licensing information.
|
||||
|
||||
Abstract:
|
||||
A view that clips an image to a circle and adds a stroke and shadow.
|
||||
A view draws a circle in the background of the shortName text
|
||||
*/
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct CircleText: View {
|
||||
var text: String
|
||||
var color: Color
|
||||
|
||||
var body: some View {
|
||||
|
||||
Text(text).font(.subheadline).foregroundColor(.white)
|
||||
Text(text).font(.caption2).foregroundColor(.white)
|
||||
.background(Circle()
|
||||
.fill(Color.blue)
|
||||
.frame(width: 40, height: 40, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/))
|
||||
.fill(color)
|
||||
.frame(width: 36, height: 36, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/))
|
||||
}
|
||||
}
|
||||
|
||||
struct CircleText_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
CircleText(text: "RDN")
|
||||
CircleText(text: "RDN", color: Color.blue)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
35
MeshtasticClient/Views/Helpers/MessageBubble.swift
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
import SwiftUI
|
||||
|
||||
struct MessageBubble: View {
|
||||
var contentMessage: String
|
||||
var isCurrentUser: Bool
|
||||
var time: Int32
|
||||
var shortName: String
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: isCurrentUser ? .leading : .trailing) {
|
||||
HStack {
|
||||
|
||||
CircleText(text: shortName, color: isCurrentUser ? Color.blue : Color(.darkGray)).padding(.all, 5)
|
||||
|
||||
Text(contentMessage)
|
||||
.padding(10)
|
||||
.foregroundColor(.white)
|
||||
.background(isCurrentUser ? Color.blue : Color(.darkGray))
|
||||
.cornerRadius(10)
|
||||
Spacer()
|
||||
}.padding(isCurrentUser ? .leading : .trailing, 70)
|
||||
}.padding(.bottom, 1)
|
||||
}
|
||||
}
|
||||
|
||||
struct MessageBubble_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
Group {
|
||||
MessageBubble(contentMessage: "this is the best text ever", isCurrentUser: true, time: 0, shortName: "EB")
|
||||
}
|
||||
.previewLayout(.fixed(width: 300, height: 100))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
35
MeshtasticClient/Views/Messages/MessageDetail.swift
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
import SwiftUI
|
||||
import MapKit
|
||||
import CoreLocation
|
||||
|
||||
struct MessageDetail: View {
|
||||
|
||||
@State var typingMessage: String = ""
|
||||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
|
||||
VStack(alignment: .leading) {
|
||||
ScrollView {
|
||||
MessageBubble(contentMessage: "I sent a super great message with amazing text", isCurrentUser: true, time: 1, shortName: "GVH")
|
||||
MessageBubble(contentMessage: "It was amazing to read such a fantastical text", isCurrentUser: false, time: 1, shortName: "RS1")
|
||||
MessageBubble(contentMessage: "It was the best message", isCurrentUser: false, time: 1, shortName: "RDN")
|
||||
MessageBubble(contentMessage: "This is a terse response to an amazing text", isCurrentUser: true, time: 1, shortName: "GVH")
|
||||
MessageBubble(contentMessage: "yo", isCurrentUser: true, time: 1, shortName: "GVH")
|
||||
}.padding([.top, .leading])
|
||||
HStack (alignment: .bottom) {
|
||||
|
||||
TextField("Message", text: $typingMessage)
|
||||
.textFieldStyle(RoundedBorderTextFieldStyle())
|
||||
.frame(minHeight: CGFloat(30))
|
||||
Button(action: sendMessage) {
|
||||
Image(systemName: "arrow.up.circle.fill").font(.title).foregroundColor(.blue)
|
||||
}
|
||||
}.padding(5)
|
||||
}
|
||||
.navigationTitle("Broadcast Channel")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
}
|
||||
//.navigationViewStyle//(StackNavigationViewStyle())
|
||||
}
|
||||
}
|
||||
38
MeshtasticClient/Views/Messages/MessageList.swift
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
struct MessageList: View {
|
||||
|
||||
@State var typingMessage: String = ""
|
||||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
|
||||
GeometryReader { bounds in
|
||||
|
||||
ScrollView {
|
||||
|
||||
}.padding(.all)
|
||||
|
||||
}
|
||||
.navigationTitle("Channels")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct MessageList_Previews: PreviewProvider {
|
||||
static let modelData = ModelData()
|
||||
|
||||
static var previews: some View {
|
||||
Group {
|
||||
//NodeDetail(node: modelData.nodes[0]).environmentObject(modelData)
|
||||
// NodeDetail(node: modelData.nodes[1]).environmentObject(modelData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func sendMessage() {
|
||||
//chatHelper.sendMessage(Message(content: typingMessage, user: DataSource.secondUser))
|
||||
// typingMessage = ""
|
||||
}
|
||||
|
|
@ -3,55 +3,34 @@ import MapKit
|
|||
import CoreLocation
|
||||
|
||||
struct Messages: View {
|
||||
|
||||
@State var typingMessage: String = ""
|
||||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
|
||||
List {
|
||||
HStack {
|
||||
Text("RGN").font(.subheadline).foregroundColor(.white).background(Circle().fill(Color.blue).frame(width: 40, height: 40))
|
||||
VStack(alignment: .trailing) {
|
||||
Text("I sent a super great message with amazing text").padding().foregroundColor(.white).background(Capsule().fill(Color.green))
|
||||
Text("8/7/21 8:39 PM").font(.subheadline).foregroundColor(.gray)
|
||||
}
|
||||
VStack(alignment: .leading) {
|
||||
ScrollView {
|
||||
MessageBubble(contentMessage: "I sent a super great message with amazing text", isCurrentUser: true, time: 1, shortName: "GVH")
|
||||
MessageBubble(contentMessage: "It was amazing to read such a fantastical text", isCurrentUser: false, time: 1, shortName: "RS1")
|
||||
MessageBubble(contentMessage: "It was the best message", isCurrentUser: false, time: 1, shortName: "RDN")
|
||||
MessageBubble(contentMessage: "This is a terse response to an amazing text", isCurrentUser: true, time: 1, shortName: "GVH")
|
||||
MessageBubble(contentMessage: "yo", isCurrentUser: true, time: 1, shortName: "GVH")
|
||||
}.padding([.top, .leading])
|
||||
HStack (alignment: .bottom) {
|
||||
|
||||
}
|
||||
VStack {
|
||||
HStack {
|
||||
Text("RGN").font(.subheadline).foregroundColor(.white).background(Circle().fill(Color.blue).frame(width: 40, height: 40))
|
||||
HStack {
|
||||
Text("I sent a super great message with amazing text").padding().foregroundColor(.white).background(Capsule().fill(Color.green))
|
||||
Text("8/7/21 8:39 PM").font(.subheadline).foregroundColor(.gray)
|
||||
TextField("Message", text: $typingMessage)
|
||||
.keyboardType(.asciiCapable)
|
||||
.textFieldStyle(RoundedBorderTextFieldStyle())
|
||||
.frame(minHeight: CGFloat(30))
|
||||
Button(action: sendMessage) {
|
||||
Image(systemName: "arrow.up.circle.fill").font(.title).foregroundColor(.blue)
|
||||
}
|
||||
}
|
||||
HStack {
|
||||
Text("8/7/21 8:39 PM").font(.subheadline).foregroundColor(.gray)
|
||||
}
|
||||
|
||||
}
|
||||
VStack {
|
||||
HStack {
|
||||
Text("RS1").font(.subheadline).foregroundColor(.white).background(Circle().fill(Color.blue).frame(width: 40, height: 40))
|
||||
Text("the best message").padding().foregroundColor(.white).background(Capsule().fill(Color.green))
|
||||
}
|
||||
HStack {
|
||||
Text("8/7/21 8:45 PM").font(.subheadline).foregroundColor(.gray)
|
||||
}
|
||||
|
||||
}
|
||||
VStack {
|
||||
HStack{
|
||||
Text("YB").font(.subheadline).foregroundColor(.white).background(Circle().fill(Color.green).frame(width: 40, height: 40))
|
||||
Spacer(minLength: 50)
|
||||
Text("This is a terse response to an amazing text").padding().foregroundColor(.white).background(Capsule().fill(Color.green))
|
||||
}
|
||||
HStack {
|
||||
|
||||
Text("8/7/21 8:53 PM").font(.subheadline).foregroundColor(.gray)
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
}.navigationTitle("Broadcast Channel")
|
||||
}.padding(5)
|
||||
}
|
||||
.navigationTitle("Broadcast Channel")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
}.navigationViewStyle(StackNavigationViewStyle())
|
||||
}
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
|
||||
/*
|
||||
See LICENSE folder for this sample’s licensing information.
|
||||
|
||||
Abstract:
|
||||
A view showing the details for a device.
|
||||
A view showing the details for a node.
|
||||
*/
|
||||
|
||||
import SwiftUI
|
||||
|
|
@ -16,34 +13,45 @@ struct NodeDetail: View {
|
|||
@EnvironmentObject var modelData: ModelData
|
||||
var node: NodeInfoModel
|
||||
|
||||
var nodeIndex: Int {
|
||||
modelData.nodes.firstIndex(where: { $0.id == node.id })!
|
||||
}
|
||||
var coord = CLLocationCoordinate2D()
|
||||
|
||||
struct MapLocation: Identifiable {
|
||||
let id = UUID()
|
||||
let name: String
|
||||
let coordinate: CLLocationCoordinate2D
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
let location = LocationHelper.currentLocation
|
||||
let currentCoordinatePosition = CLLocationCoordinate2D(latitude: location.latitude, longitude: location.longitude)
|
||||
let regionBinding = Binding<MKCoordinateRegion>(
|
||||
get: {
|
||||
MKCoordinateRegion(center: currentCoordinatePosition, span: MKCoordinateSpan(latitudeDelta: 0.02, longitudeDelta: 0.02))
|
||||
},
|
||||
set: { _ in }
|
||||
)
|
||||
|
||||
|
||||
GeometryReader { bounds in
|
||||
|
||||
VStack {
|
||||
|
||||
// Map or Device Image
|
||||
if(node.position.latitudeI != nil && node.position.latitudeI! > 0) {
|
||||
Map(coordinateRegion: regionBinding, showsUserLocation: true, userTrackingMode: .constant(.follow))
|
||||
|
||||
.frame(idealWidth: bounds.size.width, minHeight: bounds.size.height / 2)
|
||||
if(node.position.coordinate != nil) {
|
||||
|
||||
let nodeCoordinatePosition = CLLocationCoordinate2D(latitude: node.position.latitude!, longitude: node.position.longitude!)
|
||||
|
||||
let regionBinding = Binding<MKCoordinateRegion>(
|
||||
get: {
|
||||
MKCoordinateRegion(center: nodeCoordinatePosition, span: MKCoordinateSpan(latitudeDelta: 0.002, longitudeDelta: 0.002))
|
||||
},
|
||||
set: { _ in }
|
||||
)
|
||||
|
||||
let annotations = [MapLocation(name: node.user.shortName, coordinate: node.position.coordinate!)]
|
||||
|
||||
Map(coordinateRegion: regionBinding, showsUserLocation: true, userTrackingMode: .constant(.none), annotationItems: annotations) { location in
|
||||
MapAnnotation(
|
||||
coordinate: location.coordinate,
|
||||
content: {
|
||||
Text(node.user.shortName).font(.subheadline).foregroundColor(.white)
|
||||
.background(Circle()
|
||||
.fill(Color.blue)
|
||||
.frame(width: 36, height: 36))
|
||||
}
|
||||
)
|
||||
}.frame(idealWidth: bounds.size.width, minHeight: bounds.size.height / 2)
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -53,34 +61,48 @@ struct NodeDetail: View {
|
|||
.frame(width: bounds.size.width, height: bounds.size.height / 2)
|
||||
}
|
||||
ScrollView {
|
||||
|
||||
HStack {
|
||||
Spacer()
|
||||
Image(systemName: "flipphone").font(.largeTitle).foregroundColor(.blue)
|
||||
Text("Model: " + String(node.user.hwModel)).font(.title)
|
||||
Spacer()
|
||||
}.padding()
|
||||
|
||||
Image(node.user.hwModel.lowercased())
|
||||
.resizable()
|
||||
.frame(width: 70, height: 70)
|
||||
Text("Model: " + String(node.user.hwModel))
|
||||
.font(.title)
|
||||
}
|
||||
.padding()
|
||||
Divider()
|
||||
HStack {
|
||||
Image(systemName: "antenna.radiowaves.left.and.right").font(.title2).foregroundColor(.blue)
|
||||
|
||||
VStack(alignment: .center) {
|
||||
|
||||
Text("SNR").font(.title3)
|
||||
Text(String(node.snr!)).font(.title3).foregroundColor(.gray)
|
||||
}
|
||||
Divider()
|
||||
VStack(alignment: .center) {
|
||||
|
||||
Text("AKA").font(.title3)
|
||||
Text(node.user.shortName).font(.title3).foregroundColor(.gray)
|
||||
CircleText(text: node.user.shortName, color: Color.blue)
|
||||
.offset(y:10)
|
||||
}
|
||||
.padding([.leading, .trailing, .bottom])
|
||||
Divider()
|
||||
VStack(alignment: .center) {
|
||||
|
||||
Image(systemName: "waveform.path")
|
||||
.font(.title2)
|
||||
.foregroundColor(.blue)
|
||||
Text("SNR").font(.title3)
|
||||
Text(String(node.snr ?? 0))
|
||||
.font(.title3)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
Divider()
|
||||
VStack(alignment: .leading) {
|
||||
VStack(alignment: .center) {
|
||||
BatteryIcon(batteryLevel: node.position.batteryLevel, font: .title, color: Color.blue)
|
||||
Text("Battery").font(.title3)
|
||||
Text(String(node.position.batteryLevel!) + "%").font(.title3).foregroundColor(.gray)
|
||||
Text(String(node.position.batteryLevel!) + "%")
|
||||
.font(.title3)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
}.padding(4)
|
||||
Divider()
|
||||
HStack{
|
||||
|
||||
Image(systemName: "clock").font(.title2).foregroundColor(.blue)
|
||||
let lastHeard = Date(timeIntervalSince1970: node.lastHeard)
|
||||
Text("Last Heard:").font(.title3)
|
||||
|
|
@ -88,42 +110,58 @@ struct NodeDetail: View {
|
|||
Text("ago").font(.title3)
|
||||
}.padding()
|
||||
Divider()
|
||||
HStack(alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/, spacing: 14) {
|
||||
Image(systemName: "mappin").font(.title).foregroundColor(.blue)
|
||||
VStack(alignment: .leading) {
|
||||
Text("Latitude").font(.headline)
|
||||
Text(String(node.position.latitudeI!)).font(.caption).foregroundColor(.gray)
|
||||
}
|
||||
VStack(alignment: .leading) {
|
||||
Text("Longitude").font(.headline)
|
||||
Text(String(node.position.longitudeI!)).font(.caption).foregroundColor(.gray)
|
||||
}
|
||||
VStack(alignment: .leading) {
|
||||
Text("Altitude").font(.headline)
|
||||
Text(String(node.position.altitude!) + " m").font(.caption).foregroundColor(.gray)
|
||||
}
|
||||
}.padding()
|
||||
Divider()
|
||||
HStack {
|
||||
Spacer()
|
||||
Image(systemName: "person").font(.title3).foregroundColor(.blue)
|
||||
Text("Unique Id: " + String(node.user.id)).font(.headline)
|
||||
if node.position.latitudeI != nil {
|
||||
HStack(alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/, spacing: 14) {
|
||||
Image(systemName: "mappin").font(.title).foregroundColor(.blue)
|
||||
VStack(alignment: .leading) {
|
||||
Text("Latitude").font(.headline)
|
||||
Text(String(node.position.latitude ?? 0)).font(.caption).foregroundColor(.gray)
|
||||
}
|
||||
Divider()
|
||||
VStack(alignment: .leading) {
|
||||
Text("Longitude").font(.headline)
|
||||
Text(String(node.position.longitude ?? 0)).font(.caption).foregroundColor(.gray)
|
||||
}
|
||||
Divider()
|
||||
VStack(alignment: .leading) {
|
||||
Text("Altitude").font(.headline)
|
||||
Text(String(node.position.altitude ?? 0) + " m").font(.caption).foregroundColor(.gray)
|
||||
}
|
||||
}.padding()
|
||||
Divider()
|
||||
Image(systemName: "number").font(.title3).foregroundColor(.blue)
|
||||
Text("Node Num: " + String(node.num)).font(.headline)
|
||||
Spacer()
|
||||
}
|
||||
HStack (alignment: .center) {
|
||||
VStack {
|
||||
HStack{
|
||||
Image(systemName: "person").font(.title3).foregroundColor(.blue)
|
||||
Text("Unique Id:").font(.title3)
|
||||
}
|
||||
Text(node.user.id).font(.headline).foregroundColor(.gray)
|
||||
}
|
||||
Divider()
|
||||
VStack {
|
||||
HStack {
|
||||
Image(systemName: "number").font(.title3).foregroundColor(.blue)
|
||||
Text("Node Number:").font(.title3)
|
||||
}
|
||||
Text(String(node.num)).font(.headline).foregroundColor(.gray)
|
||||
}
|
||||
}.padding()
|
||||
}
|
||||
}.navigationTitle(node.user.longName)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.navigationBarItems(trailing:
|
||||
HStack {
|
||||
|
||||
CircleText(text: node.user.shortName).offset(y: -2)
|
||||
|
||||
}
|
||||
)
|
||||
}.ignoresSafeArea(.all, edges: [.leading, .trailing])
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct NodeDetail_Previews: PreviewProvider {
|
||||
static let modelData = ModelData()
|
||||
|
||||
static var previews: some View {
|
||||
Group {
|
||||
NodeDetail(node: modelData.nodes[0]).environmentObject(modelData)
|
||||
NodeDetail(node: modelData.nodes[1]).environmentObject(modelData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,23 +13,29 @@ import SwiftUI
|
|||
struct NodeList: View {
|
||||
@EnvironmentObject var modelData: ModelData
|
||||
|
||||
@State private var showLocationOnly = false
|
||||
|
||||
var filteredDevices: [NodeInfoModel] {
|
||||
modelData.nodes.filter { node in
|
||||
(!showLocationOnly || node.position.coordinate != nil)
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
|
||||
List {
|
||||
|
||||
|
||||
ForEach(modelData.nodes) { node in
|
||||
Toggle(isOn: $showLocationOnly) {
|
||||
Text("Nodes with Location only")
|
||||
}
|
||||
ForEach(filteredDevices.sorted(by: { $0.lastHeard > $1.lastHeard })) { node in
|
||||
NavigationLink(destination: NodeDetail(node: node)) {
|
||||
NodeRow(node: node)
|
||||
NodeRow(node: node, index : 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle("All Nodes")
|
||||
|
||||
|
||||
}.navigationViewStyle(StackNavigationViewStyle()) // Force Full screen master details
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,14 +10,15 @@ import SwiftUI
|
|||
import MapKit
|
||||
import CoreLocation
|
||||
|
||||
struct DeviceMap: View {
|
||||
struct NodeMap: View {
|
||||
|
||||
@EnvironmentObject var modelData: ModelData
|
||||
|
||||
var devices: [Device] {
|
||||
modelData.devices
|
||||
var locationNodes: [NodeInfoModel] {
|
||||
modelData.nodes.filter { node in
|
||||
(node.position.coordinate != nil)
|
||||
}
|
||||
}
|
||||
|
||||
struct MapLocation: Identifiable {
|
||||
let id = UUID()
|
||||
let name: String
|
||||
|
|
@ -33,27 +34,20 @@ struct DeviceMap: View {
|
|||
},
|
||||
set: { _ in }
|
||||
)
|
||||
let annotations = [
|
||||
MapLocation(name: devices[0].shortName, coordinate: CLLocationCoordinate2D(latitude: devices[0].position.latitude, longitude: devices[0].position.longitude)),
|
||||
MapLocation(name: devices[1].shortName, coordinate: CLLocationCoordinate2D(latitude: devices[1].position.latitude, longitude: devices[1].position.longitude)),
|
||||
MapLocation(name: devices[2].shortName, coordinate: CLLocationCoordinate2D(latitude: devices[2].position.latitude, longitude: devices[2].position.longitude)),
|
||||
MapLocation(name: devices[3].shortName, coordinate: CLLocationCoordinate2D(latitude: devices[3].position.latitude, longitude: devices[3].position.longitude))
|
||||
]
|
||||
|
||||
|
||||
NavigationView {
|
||||
|
||||
ZStack {
|
||||
Map(coordinateRegion: regionBinding,
|
||||
interactionModes: [.all],
|
||||
showsUserLocation: true,
|
||||
userTrackingMode: .constant(.follow), annotationItems: annotations) { location in
|
||||
userTrackingMode: .constant(.follow), annotationItems: locationNodes) { location in
|
||||
|
||||
MapAnnotation(
|
||||
coordinate: location.coordinate,
|
||||
coordinate: location.position.coordinate!,
|
||||
content: {
|
||||
Text(location.name).font(.caption2).foregroundColor(.white)
|
||||
.background(Circle()
|
||||
.fill(Color.blue)
|
||||
.frame(width: 40, height: 40)) }
|
||||
CircleText(text: location.user.shortName, color: Color.blue)
|
||||
}
|
||||
)
|
||||
}.frame(maxHeight:.infinity)
|
||||
}
|
||||
|
|
@ -1,37 +1,24 @@
|
|||
//
|
||||
// DeviceMap.swift
|
||||
// MeshtasticClient
|
||||
//
|
||||
// Created by Garth Vander Houwen on 8/7/21.
|
||||
//
|
||||
// Abstract:
|
||||
// A single row to be displayed in a list of landmarks.
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct NodeRow: View {
|
||||
var node: NodeInfoModel
|
||||
var index: Int
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
Image(node.user.hwModel.lowercased()).resizable().frame(width: 150, height: 150)
|
||||
|
||||
VStack(alignment: .leading) {
|
||||
VStack (alignment: .leading) {
|
||||
HStack() {
|
||||
|
||||
Text(node.user.longName).font(.title2)
|
||||
HStack {
|
||||
if node.user.hwModel == "TBEAM" || node.user.hwModel == "TLORA" {
|
||||
Image(systemName: "wifi")
|
||||
.foregroundColor(.blue).font(.title3)
|
||||
}
|
||||
if false {
|
||||
Image(systemName: "rectangle.connected.to.line.below")
|
||||
.foregroundColor(.green).font(.title2)
|
||||
}
|
||||
}
|
||||
CircleText(text: node.user.shortName, color: Color.blue).offset(y: 1).padding(.trailing, 5)
|
||||
Text(node.user.longName).font(.title)
|
||||
}.padding(.bottom, 2)
|
||||
HStack (alignment: .top){
|
||||
|
||||
Image(systemName: "clock").font(.subheadline).foregroundColor(.blue)
|
||||
let lastHeard = Date(timeIntervalSince1970: node.lastHeard)
|
||||
Text("Last Heard:").font(.subheadline).foregroundColor(.gray)
|
||||
Text(lastHeard, style: .relative).font(.subheadline).foregroundColor(.gray)
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
}.padding([.leading, .top, .bottom])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -40,9 +27,7 @@ struct NodeRow_Previews: PreviewProvider {
|
|||
|
||||
static var previews: some View {
|
||||
Group {
|
||||
NodeRow(node: nodes[0])
|
||||
NodeRow(node: nodes[1])
|
||||
NodeRow(node: nodes[2])
|
||||
NodeRow(node: nodes[0], index: 0)
|
||||
}
|
||||
.previewLayout(.fixed(width: 300, height: 70))
|
||||
}
|
||||
|
|
|
|||