Compare commits

..

7 commits

Author SHA1 Message Date
TrevKnows
7e2ee8733b First Commit 2023-12-11 15:38:22 -05:00
TrevKnows
51e7ef0573 Testing 2023-08-08 06:55:08 -04:00
TrevKnows
2ecc9a2ec9 Added PTR to Bluetooth Module 2023-06-27 12:48:46 -04:00
TrevKnows
0646453175 Added PTR to Main and WiFi Modules 2023-06-27 12:33:40 -04:00
TrevKnows
a34e2bf6d3 Board ID Fix - Pass 1
BLE boards running CircuitPython 7+ are now compatible with PyLeap.

It was tested using Circuit Playground Bluefruit Express, CLUE nRF52840 Express, and Feather nRF52840.

I also added a placeholder for boards without specific images.
2023-05-24 00:59:59 -04:00
TrevKnows
e5bb6df771 First Commit 2023-05-17 12:08:13 -04:00
TrevKnows
976bf512fa Removed local reference from source file 2023-03-29 15:34:07 -04:00
80 changed files with 976 additions and 1297 deletions

View file

@ -98,7 +98,7 @@ public class FileTransferClient {
// Set current peripheral
self.blePeripheral = blePeripheral
// Setup services
// Setup servic*es
let servicesGroup = DispatchGroup()
var error: Error? = nil

View file

@ -3,19 +3,19 @@
archiveVersion = 1;
classes = {
};
objectVersion = 52;
objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
D505B99C2755323C00386E9F /* NetworkMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = D505B99B2755323C00386E9F /* NetworkMonitor.swift */; };
D505B99E2756894300386E9F /* ViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = D505B99D2756894300386E9F /* ViewModifier.swift */; };
D50887632A2E35510002B798 /* Wifi_ifaddrs.m in Sources */ = {isa = PBXBuildFile; fileRef = D50887622A2E35510002B798 /* Wifi_ifaddrs.m */; };
D517F68126C5771D002996E8 /* FillerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D517F68026C5771D002996E8 /* FillerView.swift */; };
D5199A2F28DD16F100ACC34C /* BleContentTransfer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5199A2E28DD16F100ACC34C /* BleContentTransfer.swift */; };
D51D1413293A53BD0028AEDD /* WifiCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D51D1412293A53BD0028AEDD /* WifiCellViewModel.swift */; };
D520D69029D4C9380022048D /* WifiServiceCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D520D68F29D4C9380022048D /* WifiServiceCellView.swift */; };
D520D69229D4C9900022048D /* WifiServiceCellSubView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D520D69129D4C9900022048D /* WifiServiceCellSubView.swift */; };
D5267411292E902700D4C79E /* Networking.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5267410292E902700D4C79E /* Networking.swift */; };
D5269C00291960A300C0CE4B /* WifiSelection.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5269BFF291960A300C0CE4B /* WifiSelection.swift */; };
D5269C02291997DE00C0CE4B /* WifiServiceCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5269C01291997DE00C0CE4B /* WifiServiceCellView.swift */; };
D5269C042919985400C0CE4B /* WifiServiceCellSubView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5269C032919985400C0CE4B /* WifiServiceCellSubView.swift */; };
D5269C08291AB75800C0CE4B /* WifiPairingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5269C07291AB75800C0CE4B /* WifiPairingView.swift */; };
D52A926D29071DF400973B6B /* SelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D52A926C29071DF400973B6B /* SelectionView.swift */; };
D52A926F29078E0A00973B6B /* WifiServiceSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D52A926E29078E0A00973B6B /* WifiServiceSelectionView.swift */; };
@ -68,7 +68,6 @@
D58D887B26CC02B60085604A /* OnboardingViewPure.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58D887A26CC02B60085604A /* OnboardingViewPure.swift */; };
D58E1C8828A2B10B00AB683E /* WifiView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58E1C8728A2B10B00AB683E /* WifiView.swift */; };
D58E1C8A28A2B15E00AB683E /* WifiViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58E1C8928A2B15E00AB683E /* WifiViewModel.swift */; };
D58E1C8D28A2B32C00AB683E /* Wifi_ifaddrs.m in Sources */ = {isa = PBXBuildFile; fileRef = D58E1C8C28A2B32C00AB683E /* Wifi_ifaddrs.m */; };
D58E1C8F28A30A5300AB683E /* WifiNetworkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58E1C8E28A30A5300AB683E /* WifiNetworkService.swift */; };
D595FC2E2812C23D00569D8C /* Image Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D595FC2D2812C23D00569D8C /* Image Extension.swift */; };
D59DFD8F268A4A4D001737F6 /* BTConnectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D59DFD8E268A4A4D001737F6 /* BTConnectionView.swift */; };
@ -84,6 +83,8 @@
D5BA1F7F28B66F280012FC62 /* WifiServiceManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5BA1F7E28B66F280012FC62 /* WifiServiceManager.swift */; };
D5BA1F8128B66F920012FC62 /* CircuitPythonService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5BA1F8028B66F920012FC62 /* CircuitPythonService.swift */; };
D5BA1F8328B68ED40012FC62 /* NetworkPeripheral.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5BA1F8228B68ED40012FC62 /* NetworkPeripheral.swift */; };
D5BBD12C2A1538C100961B68 /* BoardDataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5BBD12B2A1538C100961B68 /* BoardDataProvider.swift */; };
D5BBD12E2A1C6AB300961B68 /* BleBannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5BBD12D2A1C6AB300961B68 /* BleBannerView.swift */; };
D5C474AC27E174A5002DD160 /* WebView Content.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5C474AB27E174A5002DD160 /* WebView Content.swift */; };
D5C474C827E39FD7002DD160 /* ReadexPro-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D5C474C627E39FC8002DD160 /* ReadexPro-Medium.ttf */; };
D5C474C927E39FDA002DD160 /* ReadexPro-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D5C474C727E39FC8002DD160 /* ReadexPro-Regular.ttf */; };
@ -116,13 +117,14 @@
D50237E3280994F900F1EE8A /* PyLeap.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = PyLeap.entitlements; path = "../../../../Desktop/Copy Of PyLeap Entitlement/PyLeap.entitlements"; sourceTree = "<group>"; };
D505B99B2755323C00386E9F /* NetworkMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkMonitor.swift; sourceTree = "<group>"; };
D505B99D2756894300386E9F /* ViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModifier.swift; sourceTree = "<group>"; };
D50887612A2E35490002B798 /* PyLeap-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "PyLeap-Bridging-Header.h"; sourceTree = "<group>"; };
D50887622A2E35510002B798 /* Wifi_ifaddrs.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Wifi_ifaddrs.m; sourceTree = "<group>"; };
D517F68026C5771D002996E8 /* FillerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FillerView.swift; sourceTree = "<group>"; };
D5199A2E28DD16F100ACC34C /* BleContentTransfer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BleContentTransfer.swift; sourceTree = "<group>"; };
D51D1412293A53BD0028AEDD /* WifiCellViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WifiCellViewModel.swift; sourceTree = "<group>"; };
D520D68F29D4C9380022048D /* WifiServiceCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WifiServiceCellView.swift; sourceTree = "<group>"; };
D520D69129D4C9900022048D /* WifiServiceCellSubView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WifiServiceCellSubView.swift; sourceTree = "<group>"; };
D5267410292E902700D4C79E /* Networking.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Networking.swift; sourceTree = "<group>"; };
D5269BFF291960A300C0CE4B /* WifiSelection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = WifiSelection.swift; path = "PyLeap/Views/Unpaired View/WifiSelection.swift"; sourceTree = SOURCE_ROOT; };
D5269C01291997DE00C0CE4B /* WifiServiceCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = WifiServiceCellView.swift; path = ../../../../../../Desktop/WifiServiceCellView.swift; sourceTree = "<group>"; };
D5269C032919985400C0CE4B /* WifiServiceCellSubView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = WifiServiceCellSubView.swift; path = ../../../../../../Desktop/WifiServiceCellSubView.swift; sourceTree = "<group>"; };
D5269C07291AB75800C0CE4B /* WifiPairingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WifiPairingView.swift; sourceTree = "<group>"; };
D52A926C29071DF400973B6B /* SelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectionView.swift; sourceTree = "<group>"; };
D52A926E29078E0A00973B6B /* WifiServiceSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WifiServiceSelectionView.swift; sourceTree = "<group>"; };
@ -177,7 +179,6 @@
D58D887A26CC02B60085604A /* OnboardingViewPure.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingViewPure.swift; sourceTree = "<group>"; };
D58E1C8728A2B10B00AB683E /* WifiView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WifiView.swift; sourceTree = "<group>"; };
D58E1C8928A2B15E00AB683E /* WifiViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WifiViewModel.swift; sourceTree = "<group>"; };
D58E1C8C28A2B32C00AB683E /* Wifi_ifaddrs.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Wifi_ifaddrs.m; sourceTree = "<group>"; };
D58E1C8E28A30A5300AB683E /* WifiNetworkService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WifiNetworkService.swift; sourceTree = "<group>"; };
D595FC2D2812C23D00569D8C /* Image Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Image Extension.swift"; sourceTree = "<group>"; };
D59DFD8E268A4A4D001737F6 /* BTConnectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTConnectionView.swift; sourceTree = "<group>"; };
@ -194,6 +195,8 @@
D5BA1F7E28B66F280012FC62 /* WifiServiceManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WifiServiceManager.swift; sourceTree = "<group>"; };
D5BA1F8028B66F920012FC62 /* CircuitPythonService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircuitPythonService.swift; sourceTree = "<group>"; };
D5BA1F8228B68ED40012FC62 /* NetworkPeripheral.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkPeripheral.swift; sourceTree = "<group>"; };
D5BBD12B2A1538C100961B68 /* BoardDataProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoardDataProvider.swift; sourceTree = "<group>"; };
D5BBD12D2A1C6AB300961B68 /* BleBannerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BleBannerView.swift; sourceTree = "<group>"; };
D5C474AB27E174A5002DD160 /* WebView Content.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WebView Content.swift"; sourceTree = "<group>"; };
D5C474C227E39FAD002DD160 /* ReadexPro-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "ReadexPro-Bold.ttf"; sourceTree = "<group>"; };
D5C474C527E39FC8002DD160 /* ReadexPro-Light.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "ReadexPro-Light.ttf"; sourceTree = "<group>"; };
@ -218,7 +221,6 @@
D5DD39A628D11817000FAEB8 /* WifiFileTransfer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WifiFileTransfer.swift; sourceTree = "<group>"; };
D5DD39A828D11962000FAEB8 /* WifiTransferService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WifiTransferService.swift; sourceTree = "<group>"; };
D5DD39AA28D234C3000FAEB8 /* SettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewModel.swift; sourceTree = "<group>"; };
D5ECF4B828C8E3C600FBF93D /* PyLeap-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "PyLeap-Bridging-Header.h"; sourceTree = "<group>"; };
D5F53CEA2694B524007634C2 /* Blinka Animation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Blinka Animation.swift"; sourceTree = "<group>"; };
D5F53CEC2694B7A9007634C2 /* OnboardingBackgroundView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingBackgroundView.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -278,9 +280,11 @@
D59DFDB5268CD052001737F6 /* AppEnvironment.swift */,
D534F3FB280B59090053699C /* ExampleView.swift */,
D544A2502822D4730038D483 /* Spotlight Extension.swift */,
D5BBD12A2A15389400961B68 /* Helpers */,
D567E2B428B80DC10009F768 /* Outline.md */,
D567E2DD28C8D3E20009F768 /* SettingsView */,
D5C74DF027EB92E900730505 /* Model */,
D59DFDB2268CCEAC001737F6 /* Views */,
D59DFDB2268CCEAC001737F6 /* Features */,
D5C74DEE27EB91D600730505 /* Networking */,
D5C74DED27EB919200730505 /* Resource */,
D5C41D6726C5F509004C38E3 /* Download View */,
@ -376,23 +380,29 @@
path = BLESetttings;
sourceTree = "<group>";
};
D58E1C8628A2B0DE00AB683E /* Wifi View */ = {
D56FFE1C2A2A4ED500EF1E3B /* Views */ = {
isa = PBXGroup;
children = (
D567E2DD28C8D3E20009F768 /* SettingsView */,
D58E1C8728A2B10B00AB683E /* WifiView.swift */,
D567E2B728C137880009F768 /* WifiCell.swift */,
D51D1412293A53BD0028AEDD /* WifiCellViewModel.swift */,
D567E2B928C1382E0009F768 /* WifiSubViewCell.swift */,
);
path = Views;
sourceTree = "<group>";
};
D58E1C8628A2B0DE00AB683E /* Wifi Module */ = {
isa = PBXGroup;
children = (
D56FFE1C2A2A4ED500EF1E3B /* Views */,
D51D1412293A53BD0028AEDD /* WifiCellViewModel.swift */,
D58E1C8928A2B15E00AB683E /* WifiViewModel.swift */,
D5269C07291AB75800C0CE4B /* WifiPairingView.swift */,
D52A926E29078E0A00973B6B /* WifiServiceSelectionView.swift */,
D5269BFF291960A300C0CE4B /* WifiSelection.swift */,
D5269C01291997DE00C0CE4B /* WifiServiceCellView.swift */,
D5269C032919985400C0CE4B /* WifiServiceCellSubView.swift */,
D520D68F29D4C9380022048D /* WifiServiceCellView.swift */,
D5BA1F7E28B66F280012FC62 /* WifiServiceManager.swift */,
D5DD39A628D11817000FAEB8 /* WifiFileTransfer.swift */,
D5DD39A828D11962000FAEB8 /* WifiTransferService.swift */,
D520D69129D4C9900022048D /* WifiServiceCellSubView.swift */,
D5AA27F728CA785B001CCE25 /* CircuitPythonType.swift */,
D5482F4A28E75053000B0C8E /* LocalNetworkAuth.swift */,
D5AA27F928CA8D46001CCE25 /* WifiStatusHeaderBarView.swift */,
@ -402,17 +412,17 @@
D5BA1F8028B66F920012FC62 /* CircuitPythonService.swift */,
D58E1C8E28A30A5300AB683E /* WifiNetworkService.swift */,
D5BA1F8228B68ED40012FC62 /* NetworkPeripheral.swift */,
D50887622A2E35510002B798 /* Wifi_ifaddrs.m */,
D50887612A2E35490002B798 /* PyLeap-Bridging-Header.h */,
D5D7DF2A28B3E321008552D1 /* BasicCredentials.swift */,
D58E1C8C28A2B32C00AB683E /* Wifi_ifaddrs.m */,
D5ECF4B828C8E3C600FBF93D /* PyLeap-Bridging-Header.h */,
);
path = "Wifi View";
path = "Wifi Module";
sourceTree = "<group>";
};
D59DFDB2268CCEAC001737F6 /* Views */ = {
D59DFDB2268CCEAC001737F6 /* Features */ = {
isa = PBXGroup;
children = (
D58E1C8628A2B0DE00AB683E /* Wifi View */,
D58E1C8628A2B0DE00AB683E /* Wifi Module */,
D5507ACE26C668BC00512BAA /* UI Components */,
D59DFDB3268CCEB9001737F6 /* Onboarding Views */,
D5C474AA27E1746F002DD160 /* WebView */,
@ -424,7 +434,7 @@
D517F68026C5771D002996E8 /* FillerView.swift */,
D5D1F4AC27ECFD200040E2BF /* Startup View */,
);
path = Views;
path = Features;
sourceTree = "<group>";
};
D59DFDB3268CCEB9001737F6 /* Onboarding Views */ = {
@ -438,6 +448,14 @@
path = "Onboarding Views";
sourceTree = "<group>";
};
D5BBD12A2A15389400961B68 /* Helpers */ = {
isa = PBXGroup;
children = (
D5BBD12B2A1538C100961B68 /* BoardDataProvider.swift */,
);
path = Helpers;
sourceTree = "<group>";
};
D5C41D6726C5F509004C38E3 /* Download View */ = {
isa = PBXGroup;
children = (
@ -535,6 +553,7 @@
children = (
D59E31A9281B8DD300D24211 /* DownloadState.swift */,
D5507ACB26C668BC00512BAA /* BleModuleView.swift */,
D5BBD12D2A1C6AB300961B68 /* BleBannerView.swift */,
D5507ACC26C668BC00512BAA /* BleModuleViewModel.swift */,
D5199A2E28DD16F100ACC34C /* BleContentTransfer.swift */,
D5D5BB3828DD19F000E5D93F /* BleContentCommands.swift */,
@ -605,8 +624,9 @@
D52F7E682672F4C400911D43 /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = YES;
LastSwiftUpdateCheck = 1250;
LastUpgradeCheck = 1250;
LastUpgradeCheck = 1500;
TargetAttributes = {
D52F7E6F2672F4C400911D43 = {
CreatedOnToolsVersion = 12.5;
@ -659,6 +679,7 @@
D5D1F4B827ED2F4F0040E2BF /* FileManagerCheck.swift in Sources */,
D52BE82A26A0660200630900 /* KeyboardUtils.swift in Sources */,
D58182FB27F732E40091C43B /* SubCellViewModel.swift in Sources */,
D520D69229D4C9900022048D /* WifiServiceCellSubView.swift in Sources */,
D5D5BB3928DD19F000E5D93F /* BleContentCommands.swift in Sources */,
D544A1D2281B9BB70038D483 /* Buttons.swift in Sources */,
D5D1F4AE27ECFDA10040E2BF /* GifImage.swift in Sources */,
@ -678,9 +699,9 @@
D56F640C270242CA000E5975 /* UIColor+LightAndDark.swift in Sources */,
D5F53CED2694B7A9007634C2 /* OnboardingBackgroundView.swift in Sources */,
D5D1F4B227ECFF760040E2BF /* ProjectsModel.swift in Sources */,
D5269C02291997DE00C0CE4B /* WifiServiceCellView.swift in Sources */,
D5BA1F7A28B52A490012FC62 /* WifiListDetailView.swift in Sources */,
D52F7E742672F4C400911D43 /* PyLeapApp.swift in Sources */,
D5BBD12E2A1C6AB300961B68 /* BleBannerView.swift in Sources */,
D567E2B628B81B730009F768 /* Queue.swift in Sources */,
D534F3FC280B59090053699C /* ExampleView.swift in Sources */,
D57858ED28327E18008E8BE4 /* PairingTutorialView.swift in Sources */,
@ -702,7 +723,6 @@
D5DD39A728D11817000FAEB8 /* WifiFileTransfer.swift in Sources */,
D5AA27FA28CA8D46001CCE25 /* WifiStatusHeaderBarView.swift in Sources */,
D5F53CEB2694B524007634C2 /* Blinka Animation.swift in Sources */,
D5269C00291960A300C0CE4B /* WifiSelection.swift in Sources */,
D5597BF826A9E14B00DF17C0 /* AppDelegate.swift in Sources */,
D56F640E270242CA000E5975 /* String+DeletingPrefix.swift in Sources */,
D57858F328333CBC008E8BE4 /* TroubleshootView.swift in Sources */,
@ -739,18 +759,19 @@
D567E2B828C137880009F768 /* WifiCell.swift in Sources */,
D5D1F4B027ECFDE00040E2BF /* NavBarModifier.swift in Sources */,
D5597C0C26AF018800DF17C0 /* View+If.swift in Sources */,
D58E1C8D28A2B32C00AB683E /* Wifi_ifaddrs.m in Sources */,
D56B75D4294BAAB400D008E7 /* BLESettingsView.swift in Sources */,
D50887632A2E35510002B798 /* Wifi_ifaddrs.m in Sources */,
D52BE7EE269DF36E00630900 /* DownloadViewModel.swift in Sources */,
D59E31AA281B8DD300D24211 /* DownloadState.swift in Sources */,
D520D69029D4C9380022048D /* WifiServiceCellView.swift in Sources */,
D5BA1F7F28B66F280012FC62 /* WifiServiceManager.swift in Sources */,
D5C74DF327EB92FA00730505 /* View+Extensions.swift in Sources */,
D5AA27F828CA785B001CCE25 /* CircuitPythonType.swift in Sources */,
D5199A2F28DD16F100ACC34C /* BleContentTransfer.swift in Sources */,
D535E21628E1FA910096E548 /* ScrollRefreshableView.swift in Sources */,
D5640216271B54BF00AE1519 /* MainSelectionView.swift in Sources */,
D5269C042919985400C0CE4B /* WifiServiceCellSubView.swift in Sources */,
D505B99C2755323C00386E9F /* NetworkMonitor.swift in Sources */,
D5BBD12C2A1538C100961B68 /* BoardDataProvider.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -794,6 +815,7 @@
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
@ -808,7 +830,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
@ -855,6 +877,7 @@
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
@ -863,7 +886,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = iphoneos;
@ -881,22 +904,22 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = PyLeap/PyLeap.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 0;
CURRENT_PROJECT_VERSION = 2;
DEVELOPMENT_ASSET_PATHS = "\"PyLeap/Preview Content\"";
DEVELOPMENT_TEAM = 2X94RM7457;
ENABLE_PREVIEWS = YES;
INFOPLIST_FILE = PyLeap/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.5;
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.1.1;
MARKETING_VERSION = 2.2;
PRODUCT_BUNDLE_IDENTIFIER = com.adafruit.PyLeap;
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
SWIFT_OBJC_BRIDGING_HEADER = "PyLeap/Views/Wifi View/PyLeap-Bridging-Header.h";
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
@ -911,22 +934,22 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = PyLeap/PyLeap.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 0;
CURRENT_PROJECT_VERSION = 2;
DEVELOPMENT_ASSET_PATHS = "\"PyLeap/Preview Content\"";
DEVELOPMENT_TEAM = 2X94RM7457;
ENABLE_PREVIEWS = YES;
INFOPLIST_FILE = PyLeap/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.5;
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.1.1;
MARKETING_VERSION = 2.2;
PRODUCT_BUNDLE_IDENTIFIER = com.adafruit.PyLeap;
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
SWIFT_OBJC_BRIDGING_HEADER = "PyLeap/Views/Wifi View/PyLeap-Bridging-Header.h";
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1320"
LastUpgradeVersion = "1500"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View file

@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "Default device image.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View file

@ -0,0 +1,47 @@
//
// BleBannerView.swift
// PyLeap
//
// Created by Trevor Beaton on 5/22/23.
//
import SwiftUI
struct BleBannerView: View {
var deviceName: String
var disconnectAction: () -> Void
var body: some View {
VStack {
HStack {
Image("bluetoothLogo")
.resizable()
.scaledToFit()
.frame(width: 16, height: 16)
Text(deviceName)
.font(Font.custom("ReadexPro-Regular", size: 14))
Button(action: disconnectAction) {
Text("Disconnect")
.font(Font.custom("ReadexPro-Bold", size: 14))
.underline()
}
}
// .background(GeometryReader {
// Color.clear.preference(key: ViewHeightKey.self,
// value: $0.frame(in: .local).size.height)
// })
}
}
}
struct BleBannerView_Previews: PreviewProvider {
static var previews: some View {
BleBannerView(deviceName: "Test", disconnectAction: {
print("Dismiss Action")
})
}
}

View file

@ -13,8 +13,8 @@ class BleContentCommands: ObservableObject {
private weak var fileTransferClient: FileTransferClient?
@Published var transmissionProgress: TransmissionProgress?
@Published var isTransmiting = false
@Published var bootUpInfo = String()
@Published var counter = 0
enum ProjectViewError: LocalizedError {
@ -63,7 +63,6 @@ class BleContentCommands: ObservableObject {
@Published var lastTransmit: TransmissionLog? = TransmissionLog(type: .write(size: 334))
@Published var activeAlert: ActiveAlert?
// Data
private let bleManager = BleManager.shared
@ -103,9 +102,6 @@ class BleContentCommands: ObservableObject {
self.lastTransmit = TransmissionLog(type: .read(data: data))
let str = String(decoding: data, as: UTF8.self)
print("Read: \(str)")
self.bootUpInfo = str
sharedBootinfo = str
case .failure(let error):
self.lastTransmit = TransmissionLog(type: .error(message: error.localizedDescription))

View file

@ -8,7 +8,24 @@
import SwiftUI
import FileTransferClient
class BleContentTransfer: ObservableObject {
protocol BoardInfoDelegate: AnyObject {
func boardInfoDidUpdate(to newBoard: Board?)
}
enum ListCommandError: Error {
case belowMinimum
case isPrime
}
class BleContentTransfer: ObservableObject, BoardInfoDelegate {
@Published var currentBoard: Board? {
didSet {
}
}
static let shared = BleContentTransfer()
private weak var fileTransferClient: FileTransferClient?
@ -22,7 +39,6 @@ class BleContentTransfer: ObservableObject {
@Published var downloadState: DownloadState = .idle
@State var circuitPythonVersion = String()
@Published var isTransmiting = false
@ -32,18 +48,13 @@ class BleContentTransfer: ObservableObject {
@Published var downloaderror = false
@Published var bootUpInfo = ""
@Published var contentCommands = BleContentCommands()
// CLEAN UP
var projectDirectories: [URL] = []
var projectFiles: [URL] = []
var returnedArray = [[String]]()
var fileTransferArray : [URL] = []
var filesReadyForTransfer : [URL] = []
@Published var sendingBundle = false
@Published var didCompleteTranfer = false
@ -58,10 +69,12 @@ class BleContentTransfer: ObservableObject {
@Published var isConnectedToInternet = false
@Published var showAlert = false
var downloadPhases: String = ""
let directoryPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
func boardInfoDidUpdate(to newBoard: Board?) {
self.currentBoard = newBoard
// Add any other logic you need to handle the new board here.
}
enum ProjectViewError: LocalizedError {
case fileTransferUndefined
@ -77,25 +90,9 @@ class BleContentTransfer: ObservableObject {
}
init() {
getCPVersion()
registerNotification(enabled: true)
}
func getCPVersion() {
if sharedBootinfo.contains("CircuitPython 7") {
print("circuitPythonVersion = 7")
circuitPythonVersion = "7"
print(circuitPythonVersion)
}
if sharedBootinfo.contains("CircuitPython 8") {
print("circuitPythonVersion = 8")
circuitPythonVersion = "8"
print(circuitPythonVersion)
}
}
private weak var didCompleteZip: NSObjectProtocol?
private weak var didEncounterTransferError: NSObjectProtocol?
private weak var downloadErrorDidOccur: NSObjectProtocol?
@ -311,13 +308,6 @@ class BleContentTransfer: ObservableObject {
}
}
// filterCPVersion(incomingArray: regularFileUrls)
// filterCPFiles(filesArray: regularFileUrls)
// pathManipulation(arrayOfAny: filterCPVersion(incomingArray: projectDirectories), regularArray: filterCPVersion(incomingArray: projectFiles))
projectDirectories.removeFirst()
DispatchQueue.main.async {
@ -336,57 +326,19 @@ class BleContentTransfer: ObservableObject {
func filterCPVersion(incomingArray: [URL]) -> [URL] {
print("project name \(projectName)")
for i in incomingArray {
print("incoming Array \(i.absoluteString)")
let filteredList = incomingArray.filter {
let lastPathComponent = $0.lastPathComponent
return lastPathComponent != "CircuitPython 8.x"
&& lastPathComponent != "CircuitPython 7.x"
&& lastPathComponent != "CircuitPython_Templates"
}
let listWithoutCP8Folder = incomingArray.filter {
$0.lastPathComponent != ("CircuitPython 8.x")
}
let listWithoutCP7Folder = listWithoutCP8Folder.filter {
$0.lastPathComponent != ("CircuitPython 7.x")
}
//CircuitPython_Templates
let removedCPTemplates = listWithoutCP7Folder.filter {
$0.lastPathComponent != ("CircuitPython_Templates")
}
if sharedBootinfo.contains("CircuitPython 8") {
let listForCurrentCPVersion = removedCPTemplates.filter {
!$0.absoluteString.contains("CircuitPython%207.x")
}
for i in listForCurrentCPVersion {
print("listForCurrentCPVersion :- \(i.absoluteString)")
let listForCurrentCPVersion = filteredList.filter {
$0.absoluteString.contains("CircuitPython%20\(Board.shared.versionNumber).x")
}
return listForCurrentCPVersion
}
if sharedBootinfo.contains("CircuitPython 7") {
let listForCurrentCPVersion = listWithoutCP7Folder.filter {
!$0.absoluteString.contains("CircuitPython%208.x")
}
for i in listForCurrentCPVersion {
print("listForCurrentCPVersion :- \(i.absoluteString)")
}
return listForCurrentCPVersion
}
return listWithoutCP7Folder
}
@ -394,12 +346,8 @@ class BleContentTransfer: ObservableObject {
print("\(#function) @Line: \(#line)")
var recursiveArray = directoryArray
for i in recursiveArray {
print("recursiveArray \(i.absoluteString)")
}
if directoryArray.isEmpty {
print("Array is empty. makeDirectory is done - Ready for file transfer!")
newTransfer(listOf: regularFilesArray)
} else {
@ -490,10 +438,7 @@ class BleContentTransfer: ObservableObject {
}
enum ListCommandError: Error {
case belowMinimum
case isPrime
}
func checkIfFilesExistOnBoard(url: URL) {
@ -527,29 +472,16 @@ class BleContentTransfer: ObservableObject {
var tempPathComponents = url.pathComponents
print("Incoming URL for makeFileString: \(url.absoluteString)")
if sharedBootinfo.contains("CircuitPython 7") {
indexOfCP = tempPathComponents.firstIndex(of: "CircuitPython 7.x")!
tempPathComponents.removeSubrange(0...indexOfCP)
tempPathComponents.removeLast()
var joinedArrayPath = tempPathComponents.joined(separator: "/")
print("\(#function) @Line: \(#line)")
print("Outgoing makeFileString: \(joinedArrayPath) for CP 7")
return joinedArrayPath
}
if sharedBootinfo.contains("CircuitPython 8") {
indexOfCP = tempPathComponents.firstIndex(of: "CircuitPython 8.x")!
indexOfCP = tempPathComponents.firstIndex(of: "CircuitPython \(Board.shared.versionNumber).x")!
tempPathComponents.removeSubrange(0...indexOfCP)
tempPathComponents.removeLast()
var joinedArrayPath = tempPathComponents.joined(separator: "/")
print("\(#function) @Line: \(#line)")
print("Outgoing makeFileString: \(joinedArrayPath) for CP 8")
return joinedArrayPath
}
return ""
}
@ -561,256 +493,81 @@ class BleContentTransfer: ObservableObject {
print("Incoming URL: \(url.absoluteString) ")
if sharedBootinfo.contains("CircuitPython 7") {
print(tempPathComponents)
indexOfCP = tempPathComponents.firstIndex(of: "CircuitPython 7.x")!
indexOfCP = tempPathComponents.firstIndex(of: "CircuitPython \(Board.shared.versionNumber).x")!
tempPathComponents.removeSubrange(0...indexOfCP)
var joinedArrayPath = tempPathComponents.joined(separator: "/")
print("\(#function) @Line: \(#line)")
print("Outgoing String: \(joinedArrayPath) for CP 7")
print("Outgoing Directory String: \(joinedArrayPath) for CP \(Board.shared.versionNumber)")
return joinedArrayPath
}
if sharedBootinfo.contains("CircuitPython 8") {
indexOfCP = tempPathComponents.firstIndex(of: "CircuitPython 8.x")!
tempPathComponents.removeSubrange(0...indexOfCP)
var joinedArrayPath = tempPathComponents.joined(separator: "/")
print("\(#function) @Line: \(#line)")
print("Outgoing String: \(joinedArrayPath) for CP 8")
return joinedArrayPath
} else {
guard let projectName = projectName else {
return "Unknown"
}
indexOfCP = tempPathComponents.firstIndex(of: projectName)!
tempPathComponents.removeSubrange(0...indexOfCP)
var joinedArrayPath = tempPathComponents.joined(separator: "/")
print("\(#function) @Line: \(#line)")
print("Outgoing String: \(joinedArrayPath) for CP 7")
return joinedArrayPath
}
return ""
}
func newTransfer(listOf urls: [URL]) {
print("\(#function) @Line: \(#line)")
var copiedFiles = urls
print("Files left for transfer: \(urls.count)")
print("Attempting to transfer... \(urls.first?.lastPathComponent ?? "No file found")")
DispatchQueue.main.async {
self.counter += 1
}
if copiedFiles.isEmpty {
guard !urls.isEmpty else {
print("All Files Transferred! 👍")
self.completedTransfer()
completedTransfer()
DispatchQueue.main.asyncAfter(deadline: .now() + 2){
self.sendingBundle = false
self.completedTransfer()
self.numOfFiles = 0
self.counter = 0
self.contentList.removeAll()
self.resetTransferParameters()
}
return
}
} else {
print("\(#function) @Line: \(#line)")
print("Files left for transfer: \(urls.count)")
print("Attempting to transfer... \(urls.first?.lastPathComponent ?? "No file found")")
DispatchQueue.main.async { self.counter += 1 }
guard let data = try? Data(contentsOf: URL(string: copiedFiles.first!.absoluteString)!) else {
guard let url = urls.first,
let data = try? Data(contentsOf: url) else {
print("File not found")
return
}
let path = isDirectFile(name: url.lastPathComponent) ? url.lastPathComponent : makeDirectoryString(url: url)
if copiedFiles.first?.lastPathComponent == "code.py" || copiedFiles.first?.lastPathComponent == "README.txt" {
print("Input for writeFileCommand: \(copiedFiles.first?.absoluteString)")
self.contentCommands.writeFileCommand(path: copiedFiles.first!.lastPathComponent, data: data) { result in
contentCommands.writeFileCommand(path: path, data: data) { result in
switch result {
case .success(_):
case .success:
print("Success ✅")
copiedFiles.removeFirst()
self.newTransfer(listOf: copiedFiles)
self.newTransfer(listOf: Array(urls.dropFirst()))
case .failure(let error):
print("Transfer Failure \(error)")
self.handleTransferError(error)
}
}
}
private func handleTransferError(_ error: Error) {
DispatchQueue.main.async {
print("\(#function) @Line: \(#line)")
print(error)
DispatchQueue.main.async {
print("Transfer Failure \(error)")
self.downloadState = .failed
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
self.downloadState = .idle
}
NotificationCenter.default.post(name: .didEncounterTransferError, object: nil, userInfo: nil)
}
}
}
} else {
print("Checking... 🫥")
// print("makeDirectoryString transfer \(makeDirectoryString(url: copiedFiles.first!))")
let directoryPath = makeDirectoryString(url: copiedFiles.first!)
print("Input writeFileCommand: \(directoryPath)")
self.contentCommands.writeFileCommand(path: directoryPath, data: data) { result in
switch result {
case .success(_):
print("Success ✅")
copiedFiles.removeFirst()
self.newTransfer(listOf: copiedFiles)
case .failure(let error):
print("\(#function) @Line: \(#line)")
print(error)
DispatchQueue.main.async {
print("Transfer Failure \(error)")
self.downloadState = .failed
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
self.downloadState = .idle
}
NotificationCenter.default.post(name: .didEncounterTransferError, object: nil, userInfo: nil)
}
}
}
private func resetTransferParameters() {
sendingBundle = false
completedTransfer()
numOfFiles = 0
counter = 0
contentList.removeAll()
}
private func isDirectFile(name: String) -> Bool {
return name == "code.py" || name == "README.txt"
}
}
func filePathMod(listOf files: [URL]) {
var indexOfCP = 0
for i in files {
print("-\(i.absoluteString)-\n")
}
var tempArray = files
if files.isEmpty {
// Continue
print("\(#function) @Line: \(#line)")
print("Done!")
// print("RETURNED PATHS: \(returnedArray)\n")
for i in filesReadyForTransfer {
print("\(i)")
}
// self.transferFiles(files: fileArray)
// validateDirectory(directoryArray: returnedArray, fileArray: fileArray)
} else {
var tempPath = files[0].pathComponents
print("temp path \(tempPath)")
if tempPath.contains("CircuitPython 7.x") {
print("\(#function) @Line: \(#line)")
indexOfCP = tempPath.firstIndex(of: "CircuitPython 7.x")!
tempPath.removeSubrange(0...indexOfCP)
print("removeSubrange temp path - \(tempPath)")
tempArray.removeFirst()
var joinedArrayPath = tempPath.joined(separator: "/")
filesReadyForTransfer.append(URL(string: joinedArrayPath)!)
filePathMod(listOf: tempArray)
}
if tempPath.contains("CircuitPython 8.x") {
print("\(#function) @Line: \(#line)")
indexOfCP = tempPath.firstIndex(of: "CircuitPython 8.x")!
tempPath.removeSubrange(0...indexOfCP)
print("removeSubrange temp path - \(tempPath)")
tempArray.removeFirst()
var joinedArrayPath = tempPath.joined(separator: "/")
filesReadyForTransfer.append(URL(string: joinedArrayPath)!)
filePathMod(listOf: tempArray)
}
if tempPath.contains(projectName ?? "unknown") {
print("\(#function) @Line: \(#line)")
indexOfCP = tempPath.firstIndex(of: projectName!)!
tempPath.removeSubrange(0...indexOfCP+1)
print("removeSubrange temp path - \(tempPath)")
tempArray.removeFirst()
var joinedArrayPath = tempPath.joined(separator: "/")
filesReadyForTransfer.append(URL(string: joinedArrayPath)!)
filePathMod(listOf: tempArray)
}
}
}
func completedTransfer() {
DispatchQueue.main.async {
self.downloadState = .complete
self.didCompleteTranfer = true
@ -825,185 +582,6 @@ class BleContentTransfer: ObservableObject {
}
}
func transferFiles(files: [URL]) {
print(#function)
var copiedFiles = files
print("Number of files in filesArray \(files.count)")
print(files)
if files.isEmpty {
print("Array of contents empty - Check other directories")
self.completedTransfer()
DispatchQueue.main.asyncAfter(deadline: .now() + 2){
self.sendingBundle = false
self.completedTransfer()
self.numOfFiles = 0
self.contentList.removeAll()
}
} else {
guard let selectedUrl = files.first else {
print("No such file exist here")
return
}
guard let data = try? Data(contentsOf: URL(fileURLWithPath: selectedUrl.deletingPathExtension().lastPathComponent, relativeTo: selectedUrl).appendingPathExtension(selectedUrl.pathExtension)) else {
print("File not found")
return
}
if selectedUrl.deletingLastPathComponent().lastPathComponent == "CircuitPython 7.x"{
print("Selected Path: \(selectedUrl.path)")
var tempURL = selectedUrl.pathComponents
tempURL.removeFirst(12)
let joined = tempURL.joined(separator: "/")
var newModPath = tempURL
newModPath.removeLast()
print("Test file path: \(tempURL)")
print("File transfer modified path xx: \(joined)")
self.contentCommands.writeFileCommand(path: joined, data: data) { result in
switch result {
case .success(_):
copiedFiles.removeFirst()
self.transferFiles(files: copiedFiles)
case .failure(_):
DispatchQueue.main.async {
print("Transfer Failure")
print("\(joined)")
self.downloadState = .failed
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
self.downloadState = .idle
}
}
NotificationCenter.default.post(name: .didEncounterTransferError, object: nil, userInfo: nil)
}
}
}
else if selectedUrl.deletingLastPathComponent().lastPathComponent == "lib" {
var tempURL = selectedUrl.pathComponents
tempURL.removeFirst(12)
let joined = tempURL.joined(separator: "/")
print("File transfer modified path 11:\(joined)")
print("Updated Path:\(joined)")
contentCommands.writeFileCommand(path: joined, data: data) { result in
switch result {
case .success(_):
copiedFiles.removeFirst()
self.transferFiles(files: copiedFiles)
case .failure(_):
print("Transfer Failure - 2")
self.downloadState = .failed
NotificationCenter.default.post(name: .didEncounterTransferError, object: nil, userInfo: nil)
}
}
} else {
if selectedUrl.lastPathComponent == "README.txt" {
print("Got one")
copiedFiles.removeFirst()
self.transferFiles(files: copiedFiles)
} else {
var tempURL = selectedUrl.pathComponents
tempURL.removeFirst(12)
let joined = tempURL.joined(separator: "/")
print("File transfer modified path: \(joined)")
print("Updated Path:\(joined)")
contentCommands.writeFileCommand(path: joined, data: data) { result in
switch result {
case .success(_):
copiedFiles.removeFirst()
self.transferFiles(files: copiedFiles)
case .failure(let error):
print("Failed: \(error): \(result)")
NotificationCenter.default.post(name: .didEncounterTransferError, object: nil, userInfo: nil)
}
}
}
}
}
DispatchQueue.main.async {
self.sendingBundle = true
}
}
func readMyStatus() {
///model.readFile(filename: "boot_out.txt")
print(#function)
print("BOOT INFO: \(bootUpInfo)")
switch bootUpInfo.description {
case let str where str.contains("CircuitPython 7"):
print("CircuitPython 7")
circuitPythonVersion = "CircuitPython 7"
case let str where str.contains("CircuitPython 8"):
print("CircuitPython 8")
circuitPythonVersion = "CircuitPython 8"
default:
print("Unknown Device")
}
}
// MARK: System
struct TransmissionProgress {
@ -1047,7 +625,6 @@ class BleContentTransfer: ObservableObject {
@Published var lastTransmit: TransmissionLog? = TransmissionLog(type: .write(size: 334))
@Published var activeAlert: ActiveAlert?
// Data
private let bleManager = BleManager.shared
@ -1088,8 +665,7 @@ class BleContentTransfer: ObservableObject {
let str = String(decoding: data, as: UTF8.self)
print("\(#function) @Line: \(#line)")
print("Read: \(str)")
//self.readMyStatus()
self.bootUpInfo = str

View file

@ -12,28 +12,36 @@ class ExpandedBLECellState: ObservableObject {
@Published var currentCell = ""
}
class Board: Equatable {
static func == (lhs: Board, rhs: Board) -> Bool {
return lhs.name == rhs.name && lhs.versionNumber == rhs.versionNumber
}
static let shared = Board(name: "Unrecognized Board", versionNumber: "8")
var name: String
var versionNumber: String
private init(name: String, versionNumber: String) {
self.name = name
self.versionNumber = versionNumber
}
}
struct BleModuleView: View {
// Data
enum ActiveAlert: Identifiable {
case confirmUnpair(blePeripheral: BlePeripheral)
var id: Int {
switch self {
case .confirmUnpair: return 1
}
}
}
@State var boardInfoForView: Board?
@Environment(\.presentationMode) var presentationMode
@EnvironmentObject var expandedState : ExpandedBLECellState
@ObservedObject var connectionManager = FileTransferConnectionManager.shared
@State var unknownBoardName: String?
let selectedPeripheral = FileTransferConnectionManager.shared.selectedPeripheral
@StateObject var viewModel = BleModuleViewModel()
@StateObject var vm = BleModuleViewModel()
@EnvironmentObject var rootViewModel: RootViewModel
@ -44,7 +52,6 @@ struct BleModuleView: View {
@State var isExpanded = true
@State private var scrollViewID = UUID()
@State private var activeAlert: ActiveAlert?
@State private var boardBootInfo = ""
@State private var inConnectedInSelectionView = true
@ -118,12 +125,7 @@ struct BleModuleView: View {
.lineLimit(2)
} else {
}
if boardBootInfo == "clue_nrf52840_express" {
} else if boardBootInfo == "clue_nrf52840_express" {
Image("clue")
.resizable()
.aspectRatio(contentMode: .fit)
@ -138,6 +140,20 @@ struct BleModuleView: View {
.lineLimit(2)
.padding(.horizontal, 20)
} else {
Image("Placeholder Board Image")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 200, height: 200)
.padding(.horizontal, 60)
.fixedSize(horizontal: false, vertical: true)
Text(unknownBoardName ?? "")
.font(Font.custom("ReadexPro-Regular", size: 30))
.minimumScaleFactor(0.01)
.multilineTextAlignment(.center)
.lineLimit(2)
.padding(.horizontal, 20)
}
@ -173,86 +189,11 @@ struct BleModuleView: View {
VStack {
if boardBootInfo == "circuitplayground_bluefruit" {
HStack {
Image("bluetoothLogo")
.resizable()
.scaledToFit()
.frame(width: 16, height: 16)
Text("Circuit Playground Bluefruit.")
.font(Font.custom("ReadexPro-Regular", size: 14))
.minimumScaleFactor(0.1)
Button {
BleBannerView(deviceName: boardInfoForView?.name ?? "Unknown Device", disconnectAction: {
showConfirmationPrompt()
} label: {
Text("Disconnect")
.font(Font.custom("ReadexPro-Bold", size: 14))
.underline()
.minimumScaleFactor(0.1)
}
}
}
if boardBootInfo == "clue_nrf52840_express" {
VStack {
HStack {
Image("bluetoothLogo")
.resizable()
.scaledToFit()
.frame(width: 16, height: 16)
Text("Adafruit CLUE.")
.font(Font.custom("ReadexPro-Regular", size: 14))
Button {
showConfirmationPrompt()
} label: {
Text("Disconnect")
.font(Font.custom("ReadexPro-Bold", size: 14))
.underline()
//.minimumScaleFactor(0.1)
}
}
// Expandable
VStack {
Text("More Info")
}
.background(GeometryReader {
Color.clear.preference(key: ViewHeightKey.self,
value: $0.frame(in: .local).size.height)
})
}
.onPreferenceChange(ViewHeightKey.self) { subviewHeight = $0 }
.frame(height: isExpanded ? subviewHeight : 50, alignment: .top)
.clipped()
.frame(maxWidth: .infinity)
.transition(.move(edge: .bottom))
.onTapGesture {
withAnimation(.easeIn(duration: 0.5)) {
isExpanded.toggle()
}
}
}
}
.padding(.all, 0.0)
.frame(maxWidth: .infinity)
@ -270,14 +211,32 @@ struct BleModuleView: View {
MainSubHeaderView(device: "Adafruit CLUE")
}
if boardBootInfo == "circuitplayground_bluefruit" {
else if boardInfoForView?.name == "Circuitplayground Bluefruit" {
MainSubHeaderView(device: "Circuit Playground")
}
else {
MainSubHeaderView(device: unknownBoardName ?? "device")
}
let check = vm.pdemos.filter {
if boardInfoForView?.name == "Circuitplayground Bluefruit" {
let cpbProjects = $0.compatibility.contains("circuitplayground_bluefruit")
print("Returned \(cpbProjects) for circuitplayground_bluefruit")
return cpbProjects
}
else if boardInfoForView?.name == "Clue Nrf52840 Express" {
let clueProjects = $0.compatibility.contains("clue_nrf52840_express")
return clueProjects
}
else {
return true
}
let check = viewModel.pdemos.filter {
$0.compatibility.contains(boardBootInfo)
}
@ -311,6 +270,9 @@ struct BleModuleView: View {
.id(self.scrollViewID)
}
.environmentObject(expandedState)
.refreshable {
vm.fetchAndLoadProjectsFromStorage()
}
}
}
@ -318,49 +280,36 @@ struct BleModuleView: View {
.background(Color.white)
.onChange(of: viewModel.bootUpInfo, perform: { newValue in
viewModel.readMyStatus()
.onChange(of: vm.bootUpInfo, perform: { newValue in
vm.readMyStatus()
print("newValue \(newValue)")
boardBootInfo = newValue
})
.onChange(of: connectionManager.selectedClient) { selectedClient in
viewModel.setup(fileTransferClient: selectedClient)
vm.setup(fileTransferClient: selectedClient)
}
.onAppear(){
print("Opened BleModuleView")
// networkServiceModel.fetch()
.onChange(of: vm.connectedBoard, perform: { newValue in
dump(newValue)
boardInfoForView = newValue
unknownBoardName = newValue?.name
})
viewModel.setup(fileTransferClient:connectionManager.selectedClient)
.onAppear(){
vm.setup(fileTransferClient:connectionManager.selectedClient)
connectionManager.isSelectedPeripheralReconnecting = true
viewModel.readFile(filename: "boot_out.txt")
}
vm.readFile(filename: "boot_out.txt")
print("x\(boardBootInfo)")
}
struct Alerts: ViewModifier {
@Binding var activeAlert: ActiveAlert?
func body(content: Content) -> some View {
content
.alert(item: $activeAlert, content: { alert in
switch alert {
case .confirmUnpair(let blePeripheral):
return Alert(
title: Text("Confirm disconnect \(blePeripheral.name ?? "")"),
message: nil,
primaryButton: .destructive(Text("Disconnect")) {
//BleAutoReconnect.clearAutoconnectPeripheral()
BleManager.shared.disconnect(from: blePeripheral)
},
secondaryButton: .cancel(Text("Cancel")) {})
}
})
}
}
}

View file

@ -11,19 +11,46 @@ import FileTransferClient
class BleModuleViewModel: ObservableObject {
weak var delegate: BoardInfoDelegate?
@Published var boardInfoForView: Board? {
didSet {
print("Changed")
delegate?.boardInfoDidUpdate(to: boardInfoForView)
}
}
private weak var fileTransferClient: FileTransferClient?
@StateObject var contentTransfer = BleContentTransfer()
@State var contentTransfer = BleContentTransfer()
@Published var entries = [BlePeripheral.DirectoryEntry]()
@Published var isTransmiting = false
@Published var bootUpInfo = ""
let dataStore = DataStore()
@Published var pdemos : [ResultItem] = []
var boardDataProvider = BoardDataProvider()
var connectedBoard: Board?
let dataStore = DataStore()
@ObservedObject var networkModel = NetworkService()
@Published var pdemos : [PyProject] = []
func loadProjectsFromStorage() {
self.pdemos = self.dataStore.loadDefaultList()
}
func fetchAndLoadProjectsFromStorage() {
self.networkModel.fetch {
self.pdemos = self.dataStore.loadDefaultList()
}
}
init() {
pdemos = dataStore.loadDefaultList()
loadProjectsFromStorage()
self.delegate = contentTransfer
}
@ -71,7 +98,6 @@ class BleModuleViewModel: ObservableObject {
@Published var transmissionProgress: TransmissionProgress?
@Published var lastTransmit: TransmissionLog? = TransmissionLog(type: .write(size: 334))
@Published var activeAlert: ActiveAlert?
// Data
private let bleManager = BleManager.shared
@ -121,6 +147,28 @@ class BleModuleViewModel: ObservableObject {
}
func setupBoardInfoForDisplay(_ boardInfo: String?) -> Board {
// First, safely unwrap the optional 'boardInfo' string
guard let info = boardInfo else {
print("boardInfo is nil")
return Board.shared
}
let boardID = boardDataProvider.getBoardID(from: info) ?? "Unrecognized Board"
// Board default version is set to 8
let boardVersion = boardDataProvider.getCircuitPythonMajorVersion(from: info) ?? "8"
Board.shared.name = boardID
Board.shared.versionNumber = boardVersion
// Create a new Board object with the acquired name and version
let board = Board.shared
return board
}
// MARK: - Actions
func readFile(filename: String) {
@ -135,8 +183,10 @@ class BleModuleViewModel: ObservableObject {
let str = String(decoding: data, as: UTF8.self)
print("Read: \(str)")
self.bootUpInfo = str
sharedBootinfo = str
self.connectedBoard = self.setupBoardInfoForDisplay(str)
self.boardInfoForView = self.connectedBoard
case .failure(let error):
self.lastTransmit = TransmissionLog(type: .error(message: error.localizedDescription))
@ -344,18 +394,4 @@ class BleModuleViewModel: ObservableObject {
completion?(result)
}
}
}
public var sharedBootinfo = ""
enum ActiveAlert: Identifiable {
case error(error: Error)
var id: Int {
switch self {
case .error: return 1
}
}
}

View file

@ -10,13 +10,13 @@ import FileTransferClient
struct DemoSubview: View {
@Binding var bindingString: String
let result: ResultItem
let result: PyProject
@EnvironmentObject var rootViewModel: RootViewModel
@StateObject var viewModel = SubCellViewModel()
@StateObject var contentTransfer = BleContentTransfer()
//@StateObject var contentTransfer = BleContentTransfer()
@StateObject var contentTransfer = BleContentTransfer.shared
@ObservedObject var connectionManager = FileTransferConnectionManager.shared
@ -150,15 +150,6 @@ Try again later
if isConnected {
if result.compatibility.contains(bindingString) {
// Button {
// viewModel.deleteStoredFilesInFM()
// } label: {
// Text("Delete File Manager Contents")
// .bold()
// .padding(12)
// }
if contentTransfer.downloadState == .idle {
@ -210,7 +201,7 @@ Try again later
CompleteButton()
.padding(.top, 20)
}
}
} else {
@ -231,8 +222,6 @@ Try again later
print("On Appear")
contentTransfer.contentCommands.setup(fileTransferClient: connectionManager.selectedClient)
// viewModel.readFile(filename: "boot_out.txt")
}
.onChange(of: contentTransfer.transferError, perform: { newValue in
@ -247,14 +236,8 @@ Try again later
}
})
// .onChange(of: connectionManager.selectedClient) { selectedClient in
// viewModel.setup(fileTransferClient: selectedClient)
// }
.onAppear(perform: {
contentTransfer.readMyStatus()
viewModel.searchPathForProject(nameOf: result.projectName)
if viewModel.projectDownloaded {

View file

@ -11,7 +11,7 @@ struct DemoViewCell: View {
@EnvironmentObject var expandedState : ExpandedBLECellState
let result : ResultItem
let result : PyProject
@State var isExpanded: Bool = false {
didSet {
@ -36,7 +36,7 @@ struct DemoViewCell: View {
if isExpanded {
Group {
DemoSubview(bindingString: $deviceInfo, result: result, isConnected: $isConnected)
DemoSubview(result: result, isConnected: $isConnected)
}
}

View file

@ -54,8 +54,7 @@ struct RootView: View {
case .selection:
SelectionView()
case .wifiSelection:
WifiSelection()
case .wifiPairingTutorial:
WifiPairingView()

View file

@ -47,7 +47,7 @@ public class RootViewModel: ObservableObject {
}
func goToWiFiSelection() {
destination = .wifiSelection
destination = .wifiServiceSelection
}
func goToWifiView() {

View file

@ -16,8 +16,6 @@ struct WifiHeaderView: View {
VStack {
HStack (alignment: .center, spacing: 0) {
Image(systemName: "gearshape")

View file

@ -0,0 +1,143 @@
//
// MainSelectionView.swift
// PyLeap
//
// Created by Trevor Beaton on 10/16/21.
//
import SwiftUI
import FileTransferClient
enum AdafruitDevices {
case clue_nrf52840_express
case circuitplayground_bluefruit
case esp32s2
}
struct MainSelectionView: View {
@State private var showWebViewPopover: Bool = false
@State private var inConnectedInSelectionView = false
@State private var boardBootInfo = ""
@EnvironmentObject var expandedState : ExpandedBLECellState
@ObservedObject var vm = MainSelectionViewModel()
@State private var isConnected = false
@State private var test = ""
@State private var nilBinder = DownloadState.idle
@EnvironmentObject var rootViewModel: RootViewModel
@AppStorage("shouldShowOnboarding") var shouldShowOnboarding: Bool = true
var body: some View {
VStack(alignment: .center, spacing: 0) {
MainHeaderView()
HStack(alignment: .center, spacing: 8, content: {
Text("Not Connected to a Device.")
.font(Font.custom("ReadexPro-Regular", size: 16))
Button {
rootViewModel.goToSelection()
} label: {
Text("Connect Now")
.font(Font.custom("ReadexPro-Regular", size: 16))
.underline()
}
})
.padding(.all, 0.0)
.frame(maxWidth: .infinity)
.frame(maxHeight: 40)
.background(Color("pyleap_burg"))
.foregroundColor(.white)
ScrollView {
MainSubHeaderView(device: "Adafruit device")
if vm.pdemos.isEmpty {
HStack{
Spacer()
ProgressView()
.scaleEffect(2)
Spacer()
}
.padding(0)
}
ScrollViewReader { scroll in
ForEach(vm.pdemos) { demo in
if demo.bundleLink == expandedState.currentCell {
DemoViewCell(result: demo, isExpanded: true, isConnected: $inConnectedInSelectionView, deviceInfo: $boardBootInfo, onViewGeometryChanged: {
})
.onAppear(){
print("Cell Appeared")
withAnimation {
scroll.scrollTo(demo.id)
}
}
} else {
DemoViewCell(result: demo, isExpanded: false, isConnected: $inConnectedInSelectionView, deviceInfo: $boardBootInfo, onViewGeometryChanged: {
})
}
}
}
}
.refreshable {
vm.pdemos = []
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
vm.networkModel.fetch {
vm.loadProjectsFromStorage()
}
}
}
}
.onAppear() {
print("Opened MainSelectionView")
}
.fullScreenCover(isPresented: $shouldShowOnboarding, content: {
ExampleView(shouldShowOnboarding: $shouldShowOnboarding)
})
.preferredColorScheme(.light)
.background(Color.white)
.navigationBarColor(UIColor(named: "pyleap_gray"))
.navigationBarTitleDisplayMode(.inline)
}
}

View file

@ -57,32 +57,43 @@ class MainSelectionViewModel: ObservableObject {
let dataStore = DataStore()
@Published var pdemos : [ResultItem] = []
@Published var pdemos: [PyProject] = []
var networkMonitorCancellable: AnyCancellable?
init() {
let fileURL = documentsDirectory.appendingPathComponent("StandardPyLeapProjects.json")
startUp()
}
networkMonitorCancellable = networkMonitor.$isConnected.sink { isConnected in
if isConnected {
print("The device is currently connected to the internet.")
// Perform some action when the device is connected to the internet.
func loadProjectsFromStorage() {
self.pdemos = self.dataStore.loadDefaultList()
}
func fetchAndLoadProjectsFromStorage() {
self.networkModel.fetch {
self.pdemos = self.dataStore.loadDefaultList()
}
}
func startUp(){
networkMonitorCancellable = networkMonitor.$isConnected.sink { isConnected in
if isConnected {
// Perform some action when the device is connected to the internet.
self.fetchAndLoadProjectsFromStorage()
print("The device is currently connected to the internet.")
} else {
print("The device is not currently connected to the internet.")
// Perform some action when the device is not connected to the internet.
print("Loading cached remote data.")
self.pdemos = self.dataStore.loadDefaultList()
self.loadProjectsFromStorage()
}
}
}
}

View file

@ -54,7 +54,7 @@ struct SelectionView: View {
Button {
rootViewModel.goToWiFiSelection()
} label: {
Text("Wifi")
Text("WiFi")
.font(Font.custom("ReadexPro-Regular", size: 25))
.foregroundColor(Color.white)
.frame(width: 270, height: 50, alignment: .center)
@ -110,6 +110,9 @@ struct SelectionView: View {
}
.padding(.bottom, 60)
.onAppear() {
print("In Selection view")
}
}
}

View file

@ -19,7 +19,7 @@ struct WifiCell: View {
@EnvironmentObject var expandedState : ExpandedState
let result : ResultItem
let result : PyProject
@State var isExpanded: Bool = false {
didSet {

View file

@ -15,7 +15,7 @@ struct WifiSubViewCell: View {
@StateObject var wifiFileTransfer = WifiFileTransfer()
@StateObject var wifiTransferService = WifiTransferService()
let result : ResultItem
let result : PyProject
@Binding var bindingString: String
@ -85,7 +85,7 @@ Remove device from USB. Press "Reset" on the device.
func testOperation() {
func startOperationQueue() {
let operationQueue = OperationQueue()
let operation1 = BlockOperation {
@ -158,48 +158,17 @@ Remove device from USB. Press "Reset" on the device.
.font(Font.custom("ReadexPro-Bold", size: 18))
.padding(.top, 5)
ForEach(result.compatibility, id: \.self) { device in
HStack {
Image(systemName: "checkmark")
.resizable()
.frame(width: 25, height: 22, alignment: .center)
.foregroundColor(.green)
Text("ESP32-S2")
Text(formatDeviceName(device))
.font(Font.custom("ReadexPro-Regular", size: 18))
.foregroundColor(.black)
}
.padding(.top, 10)
ForEach(result.compatibility, id: \.self) { string in
if string == "circuitplayground_bluefruit" {
HStack {
Image(systemName: "checkmark")
.resizable()
.frame(width: 25, height: 22, alignment: .center)
.foregroundColor(.green)
Text("Circuit Playground Bluefruit")
.font(Font.custom("ReadexPro-Regular", size: 18))
.foregroundColor(.black)
}
.padding(.top, 10)
}
if string == "clue_nrf52840_express" {
HStack {
Image(systemName: "checkmark")
.resizable()
.frame(width: 25, height: 22, alignment: .center)
.foregroundColor(.green)
Text("Adafruit CLUE")
.font(Font.custom("ReadexPro-Regular", size: 18))
.foregroundColor(.black)
}
.padding(.top, 10)
}
}
})
.ignoresSafeArea(.all)
@ -219,18 +188,15 @@ Remove device from USB. Press "Reset" on the device.
if isConnected {
if result.compatibility.contains(bindingString) {
if wifiFileTransfer.testIndex.downloadState == .idle {
Button {
// NotificationCenter.default.post(name: .didCompleteZip, object: nil, userInfo: projectResponse)
testOperation()
startOperationQueue()
} label: {
RunItButton()
@ -274,18 +240,6 @@ Remove device from USB. Press "Reset" on the device.
}
} else {
Button {
rootViewModel.goTobluetoothPairing()
} label: {
ConnectButton()
.padding(.top, 20)
}
}
}
Spacer()
.frame(height: 30)
.ignoresSafeArea(.all)

View file

@ -11,7 +11,7 @@ import Combine
struct WifiView: View {
@StateObject var viewModel = WifiViewModel()
@StateObject var vm = WifiViewModel()
private let kPrefix = Bundle.main.bundleIdentifier!
// User Defaults
@ -38,12 +38,12 @@ struct WifiView: View {
@State private var showPopover: Bool = false
func toggleViewModelIP() {
viewModel.isInvalidIP.toggle()
vm.isInvalidIP.toggle()
}
func scanNetworkWifi() {
viewModel.wifiServiceManager.findService()
vm.wifiServiceManager.findService()
}
func printArray(array: [Any]) {
@ -59,9 +59,9 @@ struct WifiView: View {
} else {
hostName = userDefaults.object(forKey: kPrefix+".storeResolvedAddress.hostName") as! String
viewModel.ipAddressStored = true
vm.ipAddressStored = true
print("storeResolvedAddress - is stored")
viewModel.connectionStatus = .connected
vm.connectionStatus = .connected
}
}
@ -71,28 +71,40 @@ struct WifiView: View {
hintText: "IP Address...",
primaryTitle: "Done",
secondaryTitle: "Cancel") { text in
viewModel.checkServices(ip: text)
vm.checkServices(ip: text)
} secondaryAction: {
print("Cancel")
}
}
func showConfirmationPrompt() {
comfirmationAlertMessage(title: "Are you sure you want to disconnect?", exitTitle: "Cancel", primaryTitle: "Disconnect") {
rootViewModel.goToSelection()
} cancel: {
}
}
func showAlertMessage() {
alertMessage(title: "IP address Not Found", exitTitle: "Ok") {
showValidationPrompt()
}
}
@State var boardInfoForView = Board.shared
var body: some View {
VStack(spacing: 0) {
WifiHeaderView()
Group{
switch viewModel.connectionStatus {
switch vm.connectionStatus {
case .connected:
WifiStatusConnectedView(hostName: $hostName)
WifiStatusConnectedView(hostName: $hostName, disconnectAction: {
showConfirmationPrompt()
})
case .noConnection:
WifiStatusNoConnectionView()
case .connecting:
@ -100,16 +112,14 @@ struct WifiView: View {
}
}
ScrollView(.vertical, showsIndicators: false) {
ScrollViewReader { scroll in
SubHeaderView()
let check = viewModel.pdemos.filter {
$0.compatibility.contains(boardBootInfo)
let check = vm.pdemos.filter {
$0.wifiCompatible
}
ForEach(check) { demo in
@ -117,48 +127,43 @@ struct WifiView: View {
if demo.bundleLink == test.currentCell {
WifiCell(result: demo,isExpanded: trueTog, isConnected: $inConnectedInWifiView, bootOne: $boardBootInfo, stateBinder: $downloadState, onViewGeometryChanged: {
})
.onAppear(){
withAnimation {
scroll.scrollTo(demo.id)
}
}
} else {
WifiCell(result: demo, isExpanded: falseTog, isConnected: $inConnectedInWifiView, bootOne: $boardBootInfo, stateBinder: $downloadState, onViewGeometryChanged: {
withAnimation {
// scroll.scrollTo(demo.id)
}
})
}
}
}
.id(self.scrollViewID)
}
.foregroundColor(.black)
.environmentObject(test)
.refreshable {
vm.fetchAndLoadProjectsFromStorage()
}
}
.onChange(of: viewModel.connectionStatus, perform: { newValue in
.onChange(of: vm.connectionStatus, perform: { newValue in
if newValue == .connected {
hostName = userDefaults.object(forKey: kPrefix+".storeResolvedAddress.hostName") as! String
}
})
.onChange(of: viewModel.wifiServiceManager.resolvedServices, perform: { newValue in
.onChange(of: vm.wifiServiceManager.resolvedServices, perform: { newValue in
print("Credential Check!")
print(newValue)
@ -173,7 +178,7 @@ struct WifiView: View {
})
.onChange(of: viewModel.isInvalidIP, perform: { newValue in
.onChange(of: vm.isInvalidIP, perform: { newValue in
print("viewModel.isInvalidIP .onChange")
if newValue {
showAlertMessage()
@ -185,8 +190,8 @@ struct WifiView: View {
.onAppear(){
checkForStoredIPAddress()
viewModel.printStoredInfo()
viewModel.read()
vm.printStoredInfo()
vm.read()
}
}

View file

@ -18,51 +18,6 @@ struct TestIndex {
class WifiFileTransfer: ObservableObject {
// func fetchDocuments<T: Sequence>(in sequence: T) where T.Element == Int {
// var documentNumbers = sequence.map { String($0) }
//
// let timer = Timer.scheduledTimer(withTimeInterval: 2, repeats: true) { [weak self] timer in
// guard
// let self = self,
// let documentNumber = documentNumbers.first
// else {
// timer.invalidate()
// return
// }
//
// self.fetchDocument(byNumber: documentNumber)
// documentNumbers.removeLast()
// }
// timer.fire() // if you don't want to wait 2 seconds for the first one to fire, go ahead and fire it manually
// }
func fetchDocumentsq<T: Sequence>(in sequence: T) where T.Element == URL {
print(sequence)
guard let value = sequence.first(where: { _ in true }) else {
print("Complete - fetchDocumentsq")
return
}
// let docNumber = String(value)
// fetchDocument(byNumber: docNumber)
DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] in
// self?.fetchDocuments(in: sequence.dropFirst())
print(value)
self?.fetchDocumentsq(in: sequence.dropFirst())
}
}
struct TestIndex {
var count = 0
var numberOfFiles = 0
@ -74,13 +29,10 @@ class WifiFileTransfer: ObservableObject {
downloadState = .idle
}
}
var testIndex = TestIndex()
func copy(with zone: NSZone? = nil) -> Any {
let copy = WifiFileTransfer()
copy.counter = counter
@ -125,6 +77,7 @@ class WifiFileTransfer: ObservableObject {
func printArray(array: [Any]) {
print("From print array:")
for i in array {
print("\(i)")
}
@ -339,34 +292,20 @@ class WifiFileTransfer: ObservableObject {
func filterOutCPDirectories(urls: [URL]) -> [URL] {
// Removes - CircuitPython 8.x directory at the lastPathComponent
let removingCP8FromArray = urls.filter {
$0.lastPathComponent != ("CircuitPython 8.x")
let filteredList = urls.filter {
let lastPathComponent = $0.lastPathComponent
return lastPathComponent != "CircuitPython 8.x"
&& lastPathComponent != "CircuitPython 7.x"
&& lastPathComponent != "CircuitPython_Templates"
}
// Removes - CircuitPython 7.x directory at the lastPathComponent
let removingCP7FromArray = removingCP8FromArray.filter {
$0.lastPathComponent != ("CircuitPython 7.x")
let listForCurrentCPVersion = filteredList.filter {
$0.absoluteString.contains("CircuitPython%20\(Board.shared.versionNumber).x")
}
if WifiCPVersion.versionNumber == 8 {
let listForCurrentCPVersion = removingCP7FromArray.filter {
!$0.absoluteString.contains("CircuitPython%207.x")
printArray(array: listForCurrentCPVersion)
}
return listForCurrentCPVersion
}
if WifiCPVersion.versionNumber == 7 {
let listForCurrentCPVersion = removingCP7FromArray.filter {
!$0.absoluteString.contains("CircuitPython%208.x")
}
return listForCurrentCPVersion
}
return removingCP7FromArray
}
@ -419,8 +358,6 @@ class WifiFileTransfer: ObservableObject {
DispatchQueue.main.async {
self.numOfFiles = tempArray.count
self.makeFile(files: tempArray)
// self.testIndex.numberOfFiles = self.numOfFiles
}
@ -618,7 +555,6 @@ class WifiFileTransfer: ObservableObject {
printArray(array: files)
var copiedArray = files
// self.fetchDocumentsq(in: files)
DispatchQueue.main.async {
self.counter += 1

View file

@ -0,0 +1,99 @@
//
// WifiServiceCellSubView.swift
// PyLeap
//
// Created by Trevor Beaton on 3/29/23.
//
import SwiftUI
import Foundation
struct WifiServiceCellSubView: View {
let resolvedService: ResolvedService
@EnvironmentObject var rootViewModel: RootViewModel
let userDefaults = UserDefaults.standard
private let kPrefix = Bundle.main.bundleIdentifier!
func storeResolvedAddress(service: ResolvedService) {
print("Storing resolved address")
userDefaults.set(service.ipAddress, forKey: kPrefix+".storeResolvedAddress.ipAddress" )
userDefaults.set(service.hostName, forKey: kPrefix+".storeResolvedAddress.hostName" )
userDefaults.set(service.device, forKey: kPrefix+".storeResolvedAddress.device" )
print("Stored UserDefaults")
print(userDefaults.object(forKey: kPrefix+".storeResolvedAddress.ipAddress"))
print(userDefaults.object(forKey: kPrefix+".storeResolvedAddress.hostName"))
print(userDefaults.object(forKey: kPrefix+".storeResolvedAddress.device"))
}
func showConfirmationPrompt(service: ResolvedService, hostName: String) {
comfirmationAlertMessage(title: "Would you like to connect to \(hostName)?", exitTitle: "Cancel", primaryTitle: "Connect") {
storeResolvedAddress(service: service)
rootViewModel.goToWifiView()
} cancel: {
}
}
var body: some View {
VStack {
HStack {
VStack {
Text("Device ID: \(resolvedService.hostName)")
.font(Font.custom("ReadexPro-Regular", size: 18))
.multilineTextAlignment(.leading)
.minimumScaleFactor(0.1)
Text("Device IP: \(resolvedService.ipAddress)")
.font(Font.custom("ReadexPro-Regular", size: 18))
.multilineTextAlignment(.leading)
.minimumScaleFactor(0.1)
}
Spacer()
}
.padding(.horizontal, 30)
.padding(.vertical, 30)
HStack (
alignment: .center,
spacing: 0
) {
Spacer()
Button {
showConfirmationPrompt(service: resolvedService, hostName: resolvedService.hostName)
} label: {
Text("Connect")
.font(Font.custom("ReadexPro-Regular", size: 25))
.foregroundColor(Color.white)
.frame(width: 270, height: 50, alignment: .center)
.background(Color("pyleap_pink"))
.clipShape(Capsule())
.padding(.bottom, 30)
}
Spacer()
}
}
}
}
//struct WifiServiceCellSubView_Previews: PreviewProvider {
// static var previews: some View {
// WifiServiceCellSubView(resolvedService: .con)
// }
//}

View file

@ -0,0 +1,81 @@
//
// WifiServiceCellView.swift
// PyLeap
//
// Created by Trevor Beaton on 11/7/22.
//
import SwiftUI
struct WifiServiceCellView: View {
let resolvedService: ResolvedService
@State private var isExpanded: Bool = false {
didSet {
onViewGeometryChanged()
}
}
let onViewGeometryChanged: ()->Void
var body: some View {
content
.frame(maxWidth: .infinity)
}
private var content: some View {
VStack(alignment: .leading, spacing: 8) {
header
if isExpanded {
Group {
WifiServiceCellSubView(resolvedService: resolvedService)
}
}
}
}
func removeAdafruitString(text: String) -> String {
if text.contains("Adafruit") {
let parsed = text.replacingOccurrences(of: "Adafruit", with: "")
return parsed
} else {
return text
}
}
private var header: some View {
HStack {
Text(removeAdafruitString(text:resolvedService.device))
.font(Font.custom("ReadexPro-Regular", size: 24))
.minimumScaleFactor(0.1)
.lineLimit(1)
// .padding(.horizontal, 30)
.foregroundColor(.white)
Spacer()
Image(systemName: "chevron.down")
.resizable()
.frame(width: 30, height: 15, alignment: .center)
.foregroundColor(.white)
.padding(.trailing, 30)
}
//.padding(.vertical, 5)
.padding(.leading)
.frame(maxWidth: .infinity)
.frame(height: 50)
.background(Color("alt-gray"))
.onTapGesture { isExpanded.toggle() }
}
}

View file

@ -3,7 +3,7 @@
// PyLeap
//
// Created by Trevor Beaton on 10/24/22.
//
// Testing
import SwiftUI

View file

@ -10,18 +10,15 @@ import Foundation
struct WifiStatusConnectedView: View {
let userDefaults = UserDefaults.standard
private let kPrefix = Bundle.main.bundleIdentifier!
@EnvironmentObject var rootViewModel: RootViewModel
@Binding var hostName: String
func showConfirmationPrompt() {
comfirmationAlertMessage(title: "Are you sure you want to disconnect?", exitTitle: "Cancel", primaryTitle: "Disconnect") {
rootViewModel.goToSelection()
} cancel: {
var disconnectAction: () -> Void
}
func removeAdafruitString(from boardName: String) -> String {
let removeString = "Adafruit"
let updatedText = boardName.replacingOccurrences(of: removeString, with: "")
return updatedText
}
var body: some View {
@ -33,16 +30,13 @@ struct WifiStatusConnectedView: View {
.frame(width: 20, height: 20)
.padding(5)
Text("Connected to \(hostName). ")
Text("Connected to \(hostName) ")
.font(Font.custom("ReadexPro-Regular", size: 14))
Button {
showConfirmationPrompt()
} label: {
Button(action: disconnectAction) {
Text("Disconnect")
.font(Font.custom("ReadexPro-Bold", size: 14))
.underline()
.minimumScaleFactor(0.1)
}
})

View file

@ -71,31 +71,6 @@ class WifiSubViewCellModel: ObservableObject {
// if success.contains("GET, OPTIONS, PUT, DELETE, MOVE") {
//
// print("USB not in use.")
// DispatchQueue.main.async {
// self.usbInUse = false
// }
//
// // DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
// // if wifiFileTransfer.projectDownloaded {
// //
// // wifiFileTransfer.testFileExistance(for: result.projectName, bundleLink: result.bundleLink)
// //
// // } else {
// // downloadModel.trueDownload(useProject: result.bundleLink, projectName: result.projectName)
// // }
// // }
//
// } else {
// DispatchQueue.main.async {
// self.usbInUse = true
// }
// print("USB in use - files cannot be tranferred or moved while USB is in use. Show Error")
// }
})
}

View file

@ -173,7 +173,7 @@ class WifiTransferService: ObservableObject {
}
let base64LoginString = loginData.base64EncodedString()
// var request = URLRequest(url: URL(string: "http://cpy-9cbe10.local/fs/")!,timeoutInterval: Double.infinity)
var request = URLRequest(url: URL(string: "http://\(hostName).local/fs/")!,timeoutInterval: Double.infinity)
request.addValue("application/json", forHTTPHeaderField: "Accept")
@ -226,7 +226,6 @@ class WifiTransferService: ObservableObject {
let loginData = loginString.data(using: String.Encoding.utf8)
let base64LoginString = loginData!.base64EncodedString()
print("Host Name: \(hostName)")

View file

@ -27,6 +27,7 @@ class WifiViewModel: ObservableObject {
@Published var ipInputValidation = false
//Dependencies
var networkMonitor = NetworkMonitor()
var networkAuth = LocalNetworkAuthorization()
public var wifiNetworkService = WifiNetworkService()
@ -34,7 +35,7 @@ class WifiViewModel: ObservableObject {
@Published var wifiTransferService = WifiTransferService()
@Published var wifiServiceManager = WifiServiceManager()
@ObservedObject var networkModel = NetworkService()
var circuitPythonVersion = Int()
@Published var webDirectoryInfo = [WebDirectoryModel]()
@ -45,9 +46,19 @@ class WifiViewModel: ObservableObject {
let dataStore = DataStore()
@Published var pdemos : [ResultItem] = []
@Published var pdemos : [PyProject] = []
func loadProjectsFromStorage() {
self.pdemos = self.dataStore.loadDefaultList()
}
func fetchAndLoadProjectsFromStorage() {
self.networkModel.fetch {
self.pdemos = self.dataStore.loadDefaultList()
}
}
// File Manager Data
let directoryPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
@ -59,11 +70,10 @@ class WifiViewModel: ObservableObject {
var ipAddressStored = false
init() {
pdemos = dataStore.loadDefaultList()
loadProjectsFromStorage()
checkIP()
registerNotifications(enabled: true)
wifiServiceManager.findService()
//read()
}
/// Makes a network call to populate our project list
@ -71,29 +81,39 @@ class WifiViewModel: ObservableObject {
// networkModel.fetch()
}
@Published var pyleapProjects = [ResultItem]()
@Published var pyleapProjects = [PyProject]()
var boardDataProvider = BoardDataProvider()
// This function reads the boards the boot_out.txt file to then set the current board's name and version number for file transfer.
func setBoardToDefault() {
Board.shared.name = "Unrecognized Board"
Board.shared.versionNumber = "8"
}
func read() {
setBoardToDefault()
// This method can't be used until the device has permission to communicate.
print("READING CP Vers.")
wifiTransferService.getRequest(read: "boot_out.txt") { result in
if result.contains("CircuitPython 7") {
WifiCPVersion.versionNumber = 7
print("WifiCPVersion.versionNumber set to: \(WifiCPVersion.versionNumber)")
}
let boardID = self.boardDataProvider.getBoardID(from: result) ?? "Unrecognized Board"
// Board default version is set to 8
let boardVersion = self.boardDataProvider.getCircuitPythonMajorVersion(from: result) ?? "8"
if result.contains("CircuitPython 8") {
WifiCPVersion.versionNumber = 8
print("WifiCPVersion.versionNumber set to: \(WifiCPVersion.versionNumber)")
}
Board.shared.name = boardID
Board.shared.versionNumber = boardVersion
dump(Board.shared)
}
}
private weak var invalidIPObserver: NSObjectProtocol?
private weak var testObserver: NSObjectProtocol?
@ -146,11 +166,6 @@ class WifiViewModel: ObservableObject {
connectionStatus = .connected
}
// @Published var connectionStatus: ConnectionStatus = AppEnvironment.isRunningTests ? .connected : .noConnection
func printStoredInfo() {
print("======Stored UserDefaults======")

View file

@ -0,0 +1,53 @@
//
// BoardDataProvider.swift
// PyLeap
//
// Created by Trevor Beaton on 5/17/23.
//
import Foundation
final class BoardDataProvider {
func getBoardID(from prompt: String) -> String? {
print("getBoardID function: \(prompt)")
let boardIDPrefix = "Board ID:"
let uidSuffix = "UID"
if let startRange = prompt.range(of: boardIDPrefix) {
let start = startRange.upperBound
var end = prompt.endIndex
if let endRange = prompt.range(of: uidSuffix) {
end = endRange.lowerBound
}
let range = start..<end
let boardID = prompt[range].trimmingCharacters(in: .whitespacesAndNewlines)
let boardIDWithSpaces = boardID.replacingOccurrences(of: "_", with: " ")
let capitalizedBoardID = boardIDWithSpaces.capitalized
print("Outgoing: \(capitalizedBoardID)")
return capitalizedBoardID
}
return nil
}
func getCircuitPythonMajorVersion(from prompt: String) -> String? {
print("From getCircuitPythonMajorVersion function: \(prompt)")
let regexPattern = "CircuitPython (\\d+)"
if let regex = try? NSRegularExpression(pattern: regexPattern) {
let range = NSRange(location: 0, length: prompt.utf16.count)
if let match = regex.firstMatch(in: prompt, options: [], range: range) {
if let range = Range(match.range(at: 1), in: prompt) {
return String(prompt[range])
}
}
}
return nil
}
}

View file

@ -11,7 +11,7 @@ import Foundation
/// This is a DataStore class that is used for saving and loading data to/from the file system using the FileManager class.
*/
public class DataStore {
public class DataStore: ObservableObject {
let fileManager = FileManager.default
@ -27,8 +27,8 @@ public class DataStore {
/// This method writes it to a file named "StandardPyLeapProjects.json" in the documents directory.
*/
func save(content: [ResultItem], completion: @escaping () -> Void) {
func save(content: [PyProject], completion: @escaping () -> Void) {
print(#function)
let encoder = JSONEncoder()
if let encodedProjectData = try? encoder.encode(content) {
@ -47,7 +47,7 @@ public class DataStore {
/// This method reads the "StandardPyLeapProjects.json" file in the documents directory and decodes it as an array of ResultItem objects, and then appends the customProjects array to it and saves it back to the file.
*/
func save(customProjects: [ResultItem], completion: @escaping () -> Void) {
func save(customProjects: [PyProject], completion: @escaping () -> Void) {
var temp = customProjects
@ -55,7 +55,7 @@ public class DataStore {
let savedData = try? Data(contentsOf: fileURL)
if let savedData = savedData,
let savedProjects = try? JSONDecoder().decode([ResultItem].self, from: savedData) {
let savedProjects = try? JSONDecoder().decode([PyProject].self, from: savedData) {
NotificationCenter.default.post(name: .didCollectCustomProject, object: nil, userInfo: nil)
temp.append(contentsOf: savedProjects)
@ -76,24 +76,24 @@ public class DataStore {
let savedData = try? Data(contentsOf: fileURL)
if let savedData = savedData,
let savedProjects = try? JSONDecoder().decode([ResultItem].self, from: savedData) {
let savedProjects = try? JSONDecoder().decode([PyProject].self, from: savedData) {
loadCustomProjectList(contents: savedProjects)
}
}
/**
/// : This method reads the "StandardPyLeapProjects.json" file in the documents directory, decodes it as an array of ResultItem objects, and returns it.
/// This method reads the "StandardPyLeapProjects.json" file in the documents directory, decodes it as an array of ResultItem objects, and returns it.
*/
func loadDefaultList() -> [ResultItem] {
func loadDefaultList() -> [PyProject] {
var result = [ResultItem]()
var result = [PyProject]()
let fileURL = documentsDirectory.appendingPathComponent("StandardPyLeapProjects.json")
let savedData = try? Data(contentsOf: fileURL)
if let savedData = savedData,
let savedProjects = try? JSONDecoder().decode([ResultItem].self, from: savedData) {
let savedProjects = try? JSONDecoder().decode([PyProject].self, from: savedData) {
result = savedProjects
}
return result
@ -107,14 +107,14 @@ public class DataStore {
/// This method reads the "CustomProjects.json" file in the documents directory, decodes it as an array of ResultItem objects, appends it to the input array, and then calls
*/
func loadCustomProjectList(contents: [ResultItem]) {
func loadCustomProjectList(contents: [PyProject]) {
var temp = contents
let fileURL = documentsDirectory.appendingPathComponent("CustomProjects.json")
let savedData = try? Data(contentsOf: fileURL)
if let savedData = savedData,
let savedProjects = try? JSONDecoder().decode([ResultItem].self, from: savedData) {
let savedProjects = try? JSONDecoder().decode([PyProject].self, from: savedData) {
temp.append(contentsOf: savedProjects)
removeDuplicates(projectList: temp)
@ -129,9 +129,9 @@ public class DataStore {
/// This method uses the reduce(into:_:) method to iterate over the array, and it builds a new array that only contains unique ResultItem objects based on their bundleLink property. It then calls the save(content:completion:) method to save the new array to "StandardPyLeapProjects.json" file.
*/
func removeDuplicates(projectList: [ResultItem]) {
func removeDuplicates(projectList: [PyProject]) {
let combinedLists = projectList.reduce(into: [ResultItem]()) { (result, projectList) in
let combinedLists = projectList.reduce(into: [PyProject]()) { (result, projectList) in
if !result.contains(where: { $0.bundleLink == projectList.bundleLink }) {
result.append(projectList)
@ -146,7 +146,7 @@ public class DataStore {
let savedData = try? Data(contentsOf: fileURL)
if let savedData = savedData,
let savedProjects = try? JSONDecoder().decode([ResultItem].self, from: savedData) {
let savedProjects = try? JSONDecoder().decode([PyProject].self, from: savedData) {
for project in savedProjects {
print("CustomProjects name: \(project.projectName)")

View file

@ -8,11 +8,24 @@
import Foundation
struct RootResults: Decodable {
let projects: [ResultItem]
struct RootResults: Codable {
let formatVersion: Int
let fileVersion: Int
let projects: [PyProject]
}
struct ResultItem: Codable, Identifiable, Equatable {
struct PyProject: Codable, Identifiable, Equatable {
var id: UUID = UUID()
let projectName: String
let projectImage: String
let description: String
let bundleLink: String
let learnGuideLink: String
let compatibility: [String]
let bluetoothCompatible: Bool
let wifiCompatible: Bool
enum CodingKeys: CodingKey {
case projectName
case projectImage
@ -20,15 +33,21 @@ struct ResultItem: Codable, Identifiable, Equatable {
case bundleLink
case learnGuideLink
case compatibility
case bluetoothCompatible
case wifiCompatible
}
var id = UUID()
let projectName: String
let projectImage: String
let description: String
let bundleLink: String
let learnGuideLink: String
let compatibility: [String]
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.projectName = try container.decode(String.self, forKey: .projectName)
self.projectImage = try container.decode(String.self, forKey: .projectImage)
self.description = try container.decode(String.self, forKey: .description)
self.bundleLink = try container.decode(String.self, forKey: .bundleLink)
self.learnGuideLink = try container.decode(String.self, forKey: .learnGuideLink)
self.compatibility = try container.decode([String].self, forKey: .compatibility)
self.bluetoothCompatible = try container.decode(Bool.self, forKey: .bluetoothCompatible)
self.wifiCompatible = try container.decode(Bool.self, forKey: .wifiCompatible)
}
}

View file

@ -14,6 +14,7 @@ import Foundation
import SwiftUI
class NetworkService: ObservableObject {
let dataStore = DataStore()
let thirdPartyBackgroundQueue = DispatchQueue(label: "com.PyLeap.thirdPartyBackgroundQueue", qos: .background, attributes: .concurrent)
@ -34,39 +35,40 @@ class NetworkService: ObservableObject {
func fetch(completion: @escaping() -> Void) {
print("Attempting Network Request")
let request = URLRequest(url: URL(string: AdafruitInfo.baseURL)!, cachePolicy: URLRequest.CachePolicy.returnCacheDataElseLoad, timeoutInterval: 60.0)
let request = URLRequest(url: URL(string: AdafruitInfo.baseURL)!)
let task = session.dataTask(with: request) { data, response, error in
if let error = error {
print("error: \(error)")
}
if let data = data {
print("Updating UIList with new data...")
if let projectData = try? JSONDecoder().decode(RootResults.self, from: data) {
DispatchQueue.main.async {
self.dataStore.save(content: projectData.projects, completion: self.dataStore.loadDefaultProjectList)
completion()
}
} else {
print("No data found")
}
} else {
print("Updating UIList with Cached data...")
DispatchQueue.main.async {
self.dataStore.loadDefaultProjectList()
completion()
}
return
}
if let data = data {
print("Updating storage with new data.")
do {
let projectData = try JSONDecoder().decode(RootResults.self, from: data)
dump(projectData.projects)
DispatchQueue.main.async {
self.dataStore.save(content: projectData.projects, completion: self.dataStore.loadDefaultProjectList)
completion()
}
} catch {
print("Decoding error: \(error)")
}
}
}
task.resume()
}
func fetchThirdPartyProject(urlString: String?) {
thirdPartyBackgroundQueue.async {

View file

@ -175,6 +175,9 @@ Find more information on adding your own project here:
}
}
extension View {
func comfirmationAlertMessage(title: String, exitTitle: String, primaryTitle: String,disconnect: @escaping() -> (),cancel: @escaping() -> ()){

View file

@ -31,5 +31,10 @@ extension View {
}
}
func formatDeviceName(_ name: String) -> String {
let replaced = name.replacingOccurrences(of: "_", with: " ").replacingOccurrences(of: "-", with: " ")
let formatted = replaced.capitalized
return formatted
}
}

BIN
PyLeap/Views/.DS_Store vendored

Binary file not shown.

View file

@ -1,246 +0,0 @@
//
// MainSelectionView.swift
// PyLeap
//
// Created by Trevor Beaton on 10/16/21.
//
import SwiftUI
import FileTransferClient
enum AdafruitDevices {
case clue_nrf52840_express
case circuitplayground_bluefruit
case esp32s2
}
struct MainSelectionView: View {
@State private var showWebViewPopover: Bool = false
@State private var inConnectedInSelectionView = true
@State private var boardBootInfo = ""
@EnvironmentObject var expandedState : ExpandedBLECellState
@ObservedObject var viewModel = MainSelectionViewModel()
@State private var isConnected = false
@State private var test = ""
@State private var nilBinder = DownloadState.idle
@EnvironmentObject var rootViewModel: RootViewModel
@AppStorage("shouldShowOnboarding") var shouldShowOnboarding: Bool = true
var body: some View {
VStack(alignment: .center, spacing: 0) {
MainHeaderView()
HStack(alignment: .center, spacing: 8, content: {
Text("Not Connected to a Device.")
.font(Font.custom("ReadexPro-Regular", size: 16))
Button {
rootViewModel.goToSelection()
} label: {
Text("Connect Now")
.font(Font.custom("ReadexPro-Regular", size: 16))
.underline()
}
})
.padding(.all, 0.0)
.frame(maxWidth: .infinity)
.frame(maxHeight: 40)
.background(Color("pyleap_burg"))
.foregroundColor(.white)
ScrollView {
MainSubHeaderView(device: "Adafruit device")
if viewModel.pdemos.isEmpty {
HStack{
Spacer()
ProgressView()
.scaleEffect(2)
Spacer()
}
.padding(0)
}
ScrollViewReader { scroll in
ForEach(viewModel.pdemos) { demo in
if demo.bundleLink == expandedState.currentCell {
DemoViewCell(result: demo, isExpanded: true, isConnected: $inConnectedInSelectionView, deviceInfo: $boardBootInfo, onViewGeometryChanged: {
})
.onAppear(){
print("Cell Appeared")
withAnimation {
scroll.scrollTo(demo.id)
}
}
} else {
DemoViewCell(result: demo, isExpanded: false, isConnected: $inConnectedInSelectionView, deviceInfo: $boardBootInfo, onViewGeometryChanged: {
})
}
}
}
}
}
.onDisappear() {
}
/// **Pull down to Refresh feature**
// ScrollRefreshableView(title: "Refresh", tintColor: .purple) {
// HStack{
// Spacer()
// MainSubHeaderView()
// Spacer()
// }
// .padding(0)
//
// if networkModel.pdemos.isEmpty {
// HStack{
// Spacer()
// ProgressView()
// .scaleEffect(2)
// Spacer()
// }
// .padding(0)
//
// }
//
// }// } onRefresh: {
// self.networkModel.fetch()
// }
//
// }
.onChange(of: viewModel.pdemos, perform: { newValue in
print("Update")
})
.onAppear() {
print("Opened MainSelectionView")
}
.fullScreenCover(isPresented: $shouldShowOnboarding, content: {
ExampleView(shouldShowOnboarding: $shouldShowOnboarding)
})
.preferredColorScheme(.light)
.background(Color.white)
.navigationBarColor(UIColor(named: "pyleap_gray"))
.navigationBarTitleDisplayMode(.inline)
}
}
//struct MainSelectionView_Previews: PreviewProvider {
// static var previews: some View {
// MainSelectionView()
// }
//}
//
//struct MainSelectionView: View {
//
// @State private var showWebViewPopover: Bool = false
// @ObservedObject var networkModel = NetworkService()
// @ObservedObject var viewModel = MainSelectionViewModel()
// @State private var test = ""
// @State private var nilBinder = DownloadState.idle
// @EnvironmentObject var rootViewModel: RootViewModel
// @AppStorage("shouldShowOnboarding") var shouldShowOnboarding: Bool = true
//
// var body: some View {
// VStack(alignment: .center, spacing: 0) {
// MainHeaderView()
// connectionMessageView()
// ScrollView {
// MainSubHeaderView(device: "Adafruit device")
// if networkModel.pdemos.isEmpty {
// loadingIndicatorView()
// }
// ScrollViewReader { scroll in
// ForEach(networkModel.pdemos) { demo in
// demoViewCell(demo: demo, scroll: scroll)
// }
// }
// }
// }
// }
//
// private func connectionMessageView() -> some View {
// HStack(alignment: .center, spacing: 8) {
// Text("Not Connected to a Device.")
// .font(Font.custom("ReadexPro-Regular", size: 16))
// Button {
// rootViewModel.goToSelection()
// } label: {
// Text("Connect Now")
// .font(Font.custom("ReadexPro-Regular", size: 16))
// .underline()
// }
// }
// .padding(.all, 0.0)
// .frame(maxWidth: .infinity)
// .frame(maxHeight: 40)
// .background(Color("pyleap_burg"))
// .foregroundColor(.white)
// }
//
// private func loadingIndicatorView() -> some View {
// HStack{
// Spacer()
// ProgressView()
// .scaleEffect(2)
// Spacer()
// }
// .padding(0)
// }
//
// private func demoViewCell(demo: Demo, scroll: ScrollViewProxy) -> some View {
// if demo.bundleLink == expandedState.currentCell {
// DemoViewCell(result: demo, isExpanded: true, isConnected: $inConnectedInSelectionView, deviceInfo: $boardBootInfo, onViewGeometryChanged: {
// })
// .onAppear(){
// print("Cell Appeared")
// withAnimation {
// scroll.scrollTo(demo.id)
// }
// }
// } else {
// DemoViewCell(result: demo, isExpanded: false, isConnected: $inConnectedInSelectionView, deviceInfo: $boardBootInfo, onViewGeometryChanged: {
// })
// }
// }
//}