diff --git a/Gym Routine Tracker.xcodeproj/project.pbxproj b/Gym Routine Tracker Watch.xcodeproj/project.pbxproj similarity index 54% rename from Gym Routine Tracker.xcodeproj/project.pbxproj rename to Gym Routine Tracker Watch.xcodeproj/project.pbxproj index d1aa395..eb0882a 100644 --- a/Gym Routine Tracker.xcodeproj/project.pbxproj +++ b/Gym Routine Tracker Watch.xcodeproj/project.pbxproj @@ -7,43 +7,18 @@ objects = { /* Begin PBXBuildFile section */ - 4000597D293D22F900661E4F /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4000597C293D22F900661E4F /* AboutView.swift */; }; - 4000597F293D274F00661E4F /* AppIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4000597E293D274F00661E4F /* AppIcon.swift */; }; + 40A1A8C9294550620042966C /* GroutUI in Frameworks */ = {isa = PBXBuildFile; productRef = 40A1A8C8294550620042966C /* GroutUI */; }; 40A70CAB293BA22D008688F8 /* LICENSE in Resources */ = {isa = PBXBuildFile; fileRef = 40A70CA9293BA22D008688F8 /* LICENSE */; }; 40A70CAC293BA22D008688F8 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 40A70CAA293BA22D008688F8 /* README.md */; }; 40BD218F293FDD0500B17BEA /* GroutLib in Frameworks */ = {isa = PBXBuildFile; productRef = 40BD218E293FDD0500B17BEA /* GroutLib */; }; 40CE1404293B0DBB00462D62 /* Gym Routine Tracker Watch App.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = 40CE1403293B0DBB00462D62 /* Gym Routine Tracker Watch App.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; - 40CE1409293B0DBB00462D62 /* Gym_Routine_Tracker_App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CE1408293B0DBB00462D62 /* Gym_Routine_Tracker_App.swift */; }; + 40CE1409293B0DBB00462D62 /* WatchApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CE1408293B0DBB00462D62 /* WatchApp.swift */; }; 40CE140D293B0DBC00462D62 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 40CE140C293B0DBC00462D62 /* Assets.xcassets */; }; 40CE1458293B100800462D62 /* GroutLib in Frameworks */ = {isa = PBXBuildFile; productRef = 40CE1457293B100800462D62 /* GroutLib */; }; 40CE145D293B113600462D62 /* Compactor in Frameworks */ = {isa = PBXBuildFile; productRef = 40CE145C293B113600462D62 /* Compactor */; }; - 40CE14AC293B153700462D62 /* RoutineList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CE148C293B153700462D62 /* RoutineList.swift */; }; - 40CE14AD293B153700462D62 /* RoutineCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CE148D293B153700462D62 /* RoutineCell.swift */; }; - 40CE14AE293B153700462D62 /* SettingsForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CE148E293B153700462D62 /* SettingsForm.swift */; }; - 40CE14AF293B153700462D62 /* RoutineSinceText.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CE148F293B153700462D62 /* RoutineSinceText.swift */; }; 40CE14B0293B153700462D62 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CE1490293B153700462D62 /* ContentView.swift */; }; - 40CE14B1293B153700462D62 /* ExerciseList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CE1492293B153700462D62 /* ExerciseList.swift */; }; - 40CE14B2293B153700462D62 /* RoutineDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CE1493293B153700462D62 /* RoutineDetail.swift */; }; - 40CE14B3293B153700462D62 /* RoutineRunView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CE1495293B153700462D62 /* RoutineRunView.swift */; }; - 40CE14B4293B153700462D62 /* RoutineControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CE1496293B153700462D62 /* RoutineControl.swift */; }; - 40CE14B5293B153700462D62 /* ExerciseRunView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CE1497293B153700462D62 /* ExerciseRunView.swift */; }; - 40CE14B6293B153700462D62 /* ExerciseRunMiddleRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CE1498293B153700462D62 /* ExerciseRunMiddleRow.swift */; }; - 40CE14B7293B153700462D62 /* ExerciseFirstTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CE149A293B153700462D62 /* ExerciseFirstTab.swift */; }; - 40CE14B8293B153700462D62 /* ExerciseIntensityTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CE149B293B153700462D62 /* ExerciseIntensityTab.swift */; }; - 40CE14B9293B153700462D62 /* ExerciseVolumeTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CE149C293B153700462D62 /* ExerciseVolumeTab.swift */; }; - 40CE14BA293B153700462D62 /* ExerciseDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CE149D293B153700462D62 /* ExerciseDetail.swift */; }; - 40CE14BB293B153700462D62 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CE149F293B153700462D62 /* Constants.swift */; }; - 40CE14BC293B153700462D62 /* PresetsPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CE14A0293B153700462D62 /* PresetsPicker.swift */; }; - 40CE14BD293B153700462D62 /* StepperUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CE14A1293B153700462D62 /* StepperUtils.swift */; }; - 40CE14BE293B153700462D62 /* Bundle-extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CE14A2293B153700462D62 /* Bundle-extension.swift */; }; - 40CE14BF293B153700462D62 /* ElapsedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CE14A3293B153700462D62 /* ElapsedView.swift */; }; - 40CE14C0293B153700462D62 /* ActionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CE14A4293B153700462D62 /* ActionButton.swift */; }; - 40CE14C1293B153700462D62 /* TextFieldWithPresets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CE14A5293B153700462D62 /* TextFieldWithPresets.swift */; }; - 40CE14C2293B153700462D62 /* FakeSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CE14A6293B153700462D62 /* FakeSection.swift */; }; - 40CE14C3293B153700462D62 /* AddButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CE14A7293B153700462D62 /* AddButton.swift */; }; - 40CE14C4293B153700462D62 /* TitleText.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CE14A8293B153700462D62 /* TitleText.swift */; }; - 40CE14C5293B153700462D62 /* ImageStepper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CE14A9293B153700462D62 /* ImageStepper.swift */; }; 40CE14C6293B153700462D62 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 40CE14AB293B153700462D62 /* Preview Assets.xcassets */; }; + 40EBE1C929634FEA004B9189 /* GroutUI in Frameworks */ = {isa = PBXBuildFile; productRef = 40EBE1C829634FEA004B9189 /* GroutUI */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -71,42 +46,15 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 4000597C293D22F900661E4F /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = ""; }; - 4000597E293D274F00661E4F /* AppIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppIcon.swift; sourceTree = ""; }; 40A70CA9293BA22D008688F8 /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; 40A70CAA293BA22D008688F8 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; - 40CE13FD293B0DBB00462D62 /* Gym Routine Tracker.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Gym Routine Tracker.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 40CE13FD293B0DBB00462D62 /* Gym Routine Tracker Watch.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Gym Routine Tracker Watch.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 40CE1403293B0DBB00462D62 /* Gym Routine Tracker Watch App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Gym Routine Tracker Watch App.app"; sourceTree = BUILT_PRODUCTS_DIR; }; - 40CE1408293B0DBB00462D62 /* Gym_Routine_Tracker_App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Gym_Routine_Tracker_App.swift; sourceTree = ""; }; + 40CE1408293B0DBB00462D62 /* WatchApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchApp.swift; sourceTree = ""; }; 40CE140C293B0DBC00462D62 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 40CE1459293B105800462D62 /* Gym Routine Tracker Watch App.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "Gym Routine Tracker Watch App.entitlements"; sourceTree = ""; }; - 40CE145A293B109400462D62 /* Gym-Routine-Tracker-Watch-App-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "Gym-Routine-Tracker-Watch-App-Info.plist"; sourceTree = ""; }; - 40CE148C293B153700462D62 /* RoutineList.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoutineList.swift; sourceTree = ""; }; - 40CE148D293B153700462D62 /* RoutineCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoutineCell.swift; sourceTree = ""; }; - 40CE148E293B153700462D62 /* SettingsForm.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsForm.swift; sourceTree = ""; }; - 40CE148F293B153700462D62 /* RoutineSinceText.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoutineSinceText.swift; sourceTree = ""; }; + 40CE1459293B105800462D62 /* App.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = App.entitlements; sourceTree = ""; }; + 40CE145A293B109400462D62 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 40CE1490293B153700462D62 /* ContentView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; - 40CE1492293B153700462D62 /* ExerciseList.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExerciseList.swift; sourceTree = ""; }; - 40CE1493293B153700462D62 /* RoutineDetail.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoutineDetail.swift; sourceTree = ""; }; - 40CE1495293B153700462D62 /* RoutineRunView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoutineRunView.swift; sourceTree = ""; }; - 40CE1496293B153700462D62 /* RoutineControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoutineControl.swift; sourceTree = ""; }; - 40CE1497293B153700462D62 /* ExerciseRunView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExerciseRunView.swift; sourceTree = ""; }; - 40CE1498293B153700462D62 /* ExerciseRunMiddleRow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExerciseRunMiddleRow.swift; sourceTree = ""; }; - 40CE149A293B153700462D62 /* ExerciseFirstTab.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExerciseFirstTab.swift; sourceTree = ""; }; - 40CE149B293B153700462D62 /* ExerciseIntensityTab.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExerciseIntensityTab.swift; sourceTree = ""; }; - 40CE149C293B153700462D62 /* ExerciseVolumeTab.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExerciseVolumeTab.swift; sourceTree = ""; }; - 40CE149D293B153700462D62 /* ExerciseDetail.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExerciseDetail.swift; sourceTree = ""; }; - 40CE149F293B153700462D62 /* Constants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; - 40CE14A0293B153700462D62 /* PresetsPicker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PresetsPicker.swift; sourceTree = ""; }; - 40CE14A1293B153700462D62 /* StepperUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StepperUtils.swift; sourceTree = ""; }; - 40CE14A2293B153700462D62 /* Bundle-extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Bundle-extension.swift"; sourceTree = ""; }; - 40CE14A3293B153700462D62 /* ElapsedView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ElapsedView.swift; sourceTree = ""; }; - 40CE14A4293B153700462D62 /* ActionButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionButton.swift; sourceTree = ""; }; - 40CE14A5293B153700462D62 /* TextFieldWithPresets.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextFieldWithPresets.swift; sourceTree = ""; }; - 40CE14A6293B153700462D62 /* FakeSection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FakeSection.swift; sourceTree = ""; }; - 40CE14A7293B153700462D62 /* AddButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddButton.swift; sourceTree = ""; }; - 40CE14A8293B153700462D62 /* TitleText.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TitleText.swift; sourceTree = ""; }; - 40CE14A9293B153700462D62 /* ImageStepper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageStepper.swift; sourceTree = ""; }; 40CE14AB293B153700462D62 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -117,7 +65,9 @@ files = ( 40CE1458293B100800462D62 /* GroutLib in Frameworks */, 40BD218F293FDD0500B17BEA /* GroutLib in Frameworks */, + 40A1A8C9294550620042966C /* GroutUI in Frameworks */, 40CE145D293B113600462D62 /* Compactor in Frameworks */, + 40EBE1C929634FEA004B9189 /* GroutUI in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -129,8 +79,6 @@ children = ( 40A70CA9293BA22D008688F8 /* LICENSE */, 40A70CAA293BA22D008688F8 /* README.md */, - 40CE145A293B109400462D62 /* Gym-Routine-Tracker-Watch-App-Info.plist */, - 40CE1459293B105800462D62 /* Gym Routine Tracker Watch App.entitlements */, 40CE1407293B0DBB00462D62 /* Sources */, 40CE13FE293B0DBB00462D62 /* Products */, 40CE1456293B100800462D62 /* Frameworks */, @@ -140,7 +88,7 @@ 40CE13FE293B0DBB00462D62 /* Products */ = { isa = PBXGroup; children = ( - 40CE13FD293B0DBB00462D62 /* Gym Routine Tracker.app */, + 40CE13FD293B0DBB00462D62 /* Gym Routine Tracker Watch.app */, 40CE1403293B0DBB00462D62 /* Gym Routine Tracker Watch App.app */, ); name = Products; @@ -149,14 +97,12 @@ 40CE1407293B0DBB00462D62 /* Sources */ = { isa = PBXGroup; children = ( - 40CE1408293B0DBB00462D62 /* Gym_Routine_Tracker_App.swift */, - 40CE148B293B153700462D62 /* Main View */, - 40CE1499293B153700462D62 /* Exercise Detail */, - 40CE1491293B153700462D62 /* Routine Detail */, - 40CE1494293B153700462D62 /* Run Views */, - 40CE149E293B153700462D62 /* Helper */, - 40CE14AA293B153700462D62 /* Preview Content */, + 40CE1408293B0DBB00462D62 /* WatchApp.swift */, + 40CE1490293B153700462D62 /* ContentView.swift */, 40CE140C293B0DBC00462D62 /* Assets.xcassets */, + 40CE145A293B109400462D62 /* Info.plist */, + 40CE1459293B105800462D62 /* App.entitlements */, + 40CE14AA293B153700462D62 /* Preview Content */, ); path = Sources; sourceTree = ""; @@ -168,69 +114,6 @@ name = Frameworks; sourceTree = ""; }; - 40CE148B293B153700462D62 /* Main View */ = { - isa = PBXGroup; - children = ( - 40CE1490293B153700462D62 /* ContentView.swift */, - 40CE148C293B153700462D62 /* RoutineList.swift */, - 40CE148D293B153700462D62 /* RoutineCell.swift */, - 40CE148E293B153700462D62 /* SettingsForm.swift */, - 40CE148F293B153700462D62 /* RoutineSinceText.swift */, - 4000597C293D22F900661E4F /* AboutView.swift */, - ); - path = "Main View"; - sourceTree = ""; - }; - 40CE1491293B153700462D62 /* Routine Detail */ = { - isa = PBXGroup; - children = ( - 40CE1492293B153700462D62 /* ExerciseList.swift */, - 40CE1493293B153700462D62 /* RoutineDetail.swift */, - ); - path = "Routine Detail"; - sourceTree = ""; - }; - 40CE1494293B153700462D62 /* Run Views */ = { - isa = PBXGroup; - children = ( - 40CE1495293B153700462D62 /* RoutineRunView.swift */, - 40CE1496293B153700462D62 /* RoutineControl.swift */, - 40CE1497293B153700462D62 /* ExerciseRunView.swift */, - 40CE1498293B153700462D62 /* ExerciseRunMiddleRow.swift */, - ); - path = "Run Views"; - sourceTree = ""; - }; - 40CE1499293B153700462D62 /* Exercise Detail */ = { - isa = PBXGroup; - children = ( - 40CE149A293B153700462D62 /* ExerciseFirstTab.swift */, - 40CE149B293B153700462D62 /* ExerciseIntensityTab.swift */, - 40CE149C293B153700462D62 /* ExerciseVolumeTab.swift */, - 40CE149D293B153700462D62 /* ExerciseDetail.swift */, - ); - path = "Exercise Detail"; - sourceTree = ""; - }; - 40CE149E293B153700462D62 /* Helper */ = { - isa = PBXGroup; - children = ( - 40CE14A4293B153700462D62 /* ActionButton.swift */, - 40CE14A7293B153700462D62 /* AddButton.swift */, - 4000597E293D274F00661E4F /* AppIcon.swift */, - 40CE14A2293B153700462D62 /* Bundle-extension.swift */, - 40CE149F293B153700462D62 /* Constants.swift */, - 40CE14A3293B153700462D62 /* ElapsedView.swift */, - 40CE14A6293B153700462D62 /* FakeSection.swift */, - 40CE14A9293B153700462D62 /* ImageStepper.swift */, - 40CE14A0293B153700462D62 /* PresetsPicker.swift */, - 40CE14A1293B153700462D62 /* StepperUtils.swift */, - 40CE14A5293B153700462D62 /* TextFieldWithPresets.swift */, - 40CE14A8293B153700462D62 /* TitleText.swift */, - ); - path = Helper; - sourceTree = ""; - }; 40CE14AA293B153700462D62 /* Preview Content */ = { isa = PBXGroup; children = ( @@ -243,9 +126,9 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ - 40CE13FC293B0DBB00462D62 /* Gym Routine Tracker */ = { + 40CE13FC293B0DBB00462D62 /* Gym Routine Tracker Watch */ = { isa = PBXNativeTarget; - buildConfigurationList = 40CE1417293B0DBC00462D62 /* Build configuration list for PBXNativeTarget "Gym Routine Tracker" */; + buildConfigurationList = 40CE1417293B0DBC00462D62 /* Build configuration list for PBXNativeTarget "Gym Routine Tracker Watch" */; buildPhases = ( 40CE13FB293B0DBB00462D62 /* Resources */, 40CE1416293B0DBC00462D62 /* Embed Watch Content */, @@ -255,9 +138,9 @@ dependencies = ( 40CE1406293B0DBB00462D62 /* PBXTargetDependency */, ); - name = "Gym Routine Tracker"; + name = "Gym Routine Tracker Watch"; productName = "Gym Routine Tracker"; - productReference = 40CE13FD293B0DBB00462D62 /* Gym Routine Tracker.app */; + productReference = 40CE13FD293B0DBB00462D62 /* Gym Routine Tracker Watch.app */; productType = "com.apple.product-type.application.watchapp2-container"; }; 40CE1402293B0DBB00462D62 /* Gym Routine Tracker Watch App */ = { @@ -277,6 +160,8 @@ 40CE1457293B100800462D62 /* GroutLib */, 40CE145C293B113600462D62 /* Compactor */, 40BD218E293FDD0500B17BEA /* GroutLib */, + 40A1A8C8294550620042966C /* GroutUI */, + 40EBE1C829634FEA004B9189 /* GroutUI */, ); productName = "Gym Routine Tracker Watch App"; productReference = 40CE1403293B0DBB00462D62 /* Gym Routine Tracker Watch App.app */; @@ -300,7 +185,7 @@ }; }; }; - buildConfigurationList = 40CE13FA293B0DBB00462D62 /* Build configuration list for PBXProject "Gym Routine Tracker" */; + buildConfigurationList = 40CE13FA293B0DBB00462D62 /* Build configuration list for PBXProject "Gym Routine Tracker Watch" */; compatibilityVersion = "Xcode 14.0"; developmentRegion = en; hasScannedForEncodings = 0; @@ -312,12 +197,13 @@ packageReferences = ( 40CE145B293B113600462D62 /* XCRemoteSwiftPackageReference "SwiftCompactor" */, 40BD218D293FDD0500B17BEA /* XCRemoteSwiftPackageReference "GroutLib" */, + 40EBE1C729634FEA004B9189 /* XCRemoteSwiftPackageReference "GroutUI" */, ); productRefGroup = 40CE13FE293B0DBB00462D62 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( - 40CE13FC293B0DBB00462D62 /* Gym Routine Tracker */, + 40CE13FC293B0DBB00462D62 /* Gym Routine Tracker Watch */, 40CE1402293B0DBB00462D62 /* Gym Routine Tracker Watch App */, ); }; @@ -349,35 +235,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 40CE14AC293B153700462D62 /* RoutineList.swift in Sources */, - 40CE14BF293B153700462D62 /* ElapsedView.swift in Sources */, - 40CE14BB293B153700462D62 /* Constants.swift in Sources */, - 4000597D293D22F900661E4F /* AboutView.swift in Sources */, - 40CE14C3293B153700462D62 /* AddButton.swift in Sources */, - 4000597F293D274F00661E4F /* AppIcon.swift in Sources */, - 40CE14C0293B153700462D62 /* ActionButton.swift in Sources */, - 40CE14B8293B153700462D62 /* ExerciseIntensityTab.swift in Sources */, - 40CE1409293B0DBB00462D62 /* Gym_Routine_Tracker_App.swift in Sources */, - 40CE14BC293B153700462D62 /* PresetsPicker.swift in Sources */, + 40CE1409293B0DBB00462D62 /* WatchApp.swift in Sources */, 40CE14B0293B153700462D62 /* ContentView.swift in Sources */, - 40CE14C2293B153700462D62 /* FakeSection.swift in Sources */, - 40CE14B1293B153700462D62 /* ExerciseList.swift in Sources */, - 40CE14B5293B153700462D62 /* ExerciseRunView.swift in Sources */, - 40CE14B6293B153700462D62 /* ExerciseRunMiddleRow.swift in Sources */, - 40CE14BE293B153700462D62 /* Bundle-extension.swift in Sources */, - 40CE14C1293B153700462D62 /* TextFieldWithPresets.swift in Sources */, - 40CE14B7293B153700462D62 /* ExerciseFirstTab.swift in Sources */, - 40CE14BD293B153700462D62 /* StepperUtils.swift in Sources */, - 40CE14AF293B153700462D62 /* RoutineSinceText.swift in Sources */, - 40CE14AE293B153700462D62 /* SettingsForm.swift in Sources */, - 40CE14BA293B153700462D62 /* ExerciseDetail.swift in Sources */, - 40CE14B4293B153700462D62 /* RoutineControl.swift in Sources */, - 40CE14AD293B153700462D62 /* RoutineCell.swift in Sources */, - 40CE14B3293B153700462D62 /* RoutineRunView.swift in Sources */, - 40CE14B9293B153700462D62 /* ExerciseVolumeTab.swift in Sources */, - 40CE14C5293B153700462D62 /* ImageStepper.swift in Sources */, - 40CE14B2293B153700462D62 /* RoutineDetail.swift in Sources */, - 40CE14C4293B153700462D62 /* TitleText.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -442,6 +301,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_FILE = "Sources/Gym-Routine-Tracker-Watch-App-Info.plist"; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -495,6 +355,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_FILE = "Sources/Gym-Routine-Tracker-Watch-App-Info.plist"; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = org.openalloc.grout.watch; @@ -509,14 +370,14 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO; - CODE_SIGN_ENTITLEMENTS = "Gym Routine Tracker Watch App.entitlements"; + CODE_SIGN_ENTITLEMENTS = Sources/App.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 5; DEVELOPMENT_ASSET_PATHS = "\"Sources/Preview Content\""; DEVELOPMENT_TEAM = J735QC5U38; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = "Gym-Routine-Tracker-Watch-App-Info.plist"; + INFOPLIST_FILE = Sources/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "Gym Routine Tracker"; INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; INFOPLIST_KEY_WKWatchOnly = YES; @@ -524,7 +385,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.3; + MARKETING_VERSION = 1.4; PRODUCT_BUNDLE_IDENTIFIER = org.openalloc.grout.watch; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = watchos; @@ -542,14 +403,14 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO; - CODE_SIGN_ENTITLEMENTS = "Gym Routine Tracker Watch App.entitlements"; + CODE_SIGN_ENTITLEMENTS = Sources/App.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 5; DEVELOPMENT_ASSET_PATHS = "\"Sources/Preview Content\""; DEVELOPMENT_TEAM = J735QC5U38; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = "Gym-Routine-Tracker-Watch-App-Info.plist"; + INFOPLIST_FILE = Sources/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "Gym Routine Tracker"; INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; INFOPLIST_KEY_WKWatchOnly = YES; @@ -557,7 +418,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.3; + MARKETING_VERSION = 1.4; PRODUCT_BUNDLE_IDENTIFIER = org.openalloc.grout.watch; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = watchos; @@ -577,8 +438,9 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = J735QC5U38; + INFOPLIST_FILE = Sources/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "Gym Routine Tracker"; - MARKETING_VERSION = 1.3; + MARKETING_VERSION = 1.4; PRODUCT_BUNDLE_IDENTIFIER = org.openalloc.grout; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; @@ -593,8 +455,9 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = J735QC5U38; + INFOPLIST_FILE = Sources/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "Gym Routine Tracker"; - MARKETING_VERSION = 1.3; + MARKETING_VERSION = 1.4; PRODUCT_BUNDLE_IDENTIFIER = org.openalloc.grout; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; @@ -606,7 +469,7 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - 40CE13FA293B0DBB00462D62 /* Build configuration list for PBXProject "Gym Routine Tracker" */ = { + 40CE13FA293B0DBB00462D62 /* Build configuration list for PBXProject "Gym Routine Tracker Watch" */ = { isa = XCConfigurationList; buildConfigurations = ( 40CE1411293B0DBC00462D62 /* Debug */, @@ -624,7 +487,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 40CE1417293B0DBC00462D62 /* Build configuration list for PBXNativeTarget "Gym Routine Tracker" */ = { + 40CE1417293B0DBC00462D62 /* Build configuration list for PBXNativeTarget "Gym Routine Tracker Watch" */ = { isa = XCConfigurationList; buildConfigurations = ( 40CE1418293B0DBC00462D62 /* Debug */, @@ -641,7 +504,7 @@ repositoryURL = "https://github.com/gym-routine-tracker/GroutLib.git"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 1.0.0; + minimumVersion = 1.1.0; }; }; 40CE145B293B113600462D62 /* XCRemoteSwiftPackageReference "SwiftCompactor" */ = { @@ -652,9 +515,21 @@ minimumVersion = 1.0.0; }; }; + 40EBE1C729634FEA004B9189 /* XCRemoteSwiftPackageReference "GroutUI" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/gym-routine-tracker/GroutUI.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.1.0; + }; + }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + 40A1A8C8294550620042966C /* GroutUI */ = { + isa = XCSwiftPackageProductDependency; + productName = GroutUI; + }; 40BD218E293FDD0500B17BEA /* GroutLib */ = { isa = XCSwiftPackageProductDependency; package = 40BD218D293FDD0500B17BEA /* XCRemoteSwiftPackageReference "GroutLib" */; @@ -669,6 +544,11 @@ package = 40CE145B293B113600462D62 /* XCRemoteSwiftPackageReference "SwiftCompactor" */; productName = Compactor; }; + 40EBE1C829634FEA004B9189 /* GroutUI */ = { + isa = XCSwiftPackageProductDependency; + package = 40EBE1C729634FEA004B9189 /* XCRemoteSwiftPackageReference "GroutUI" */; + productName = GroutUI; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 40CE13F7293B0DBB00462D62 /* Project object */; diff --git a/Gym Routine Tracker.xcodeproj/xcshareddata/xcschemes/Gym Routine Tracker Watch App.xcscheme b/Gym Routine Tracker Watch.xcodeproj/xcshareddata/xcschemes/Gym Routine Tracker Watch App.xcscheme similarity index 91% rename from Gym Routine Tracker.xcodeproj/xcshareddata/xcschemes/Gym Routine Tracker Watch App.xcscheme rename to Gym Routine Tracker Watch.xcodeproj/xcshareddata/xcschemes/Gym Routine Tracker Watch App.xcscheme index a631321..0cba62b 100644 --- a/Gym Routine Tracker.xcodeproj/xcshareddata/xcschemes/Gym Routine Tracker Watch App.xcscheme +++ b/Gym Routine Tracker Watch.xcodeproj/xcshareddata/xcschemes/Gym Routine Tracker Watch App.xcscheme @@ -17,7 +17,7 @@ BlueprintIdentifier = "40CE1402293B0DBB00462D62" BuildableName = "Gym Routine Tracker Watch App.app" BlueprintName = "Gym Routine Tracker Watch App" - ReferencedContainer = "container:Gym Routine Tracker.xcodeproj"> + ReferencedContainer = "container:Gym Routine Tracker Watch.xcodeproj"> + BuildableName = "Gym Routine Tracker Watch.app" + BlueprintName = "Gym Routine Tracker Watch" + ReferencedContainer = "container:Gym Routine Tracker Watch.xcodeproj"> @@ -61,7 +61,7 @@ BlueprintIdentifier = "40CE1402293B0DBB00462D62" BuildableName = "Gym Routine Tracker Watch App.app" BlueprintName = "Gym Routine Tracker Watch App" - ReferencedContainer = "container:Gym Routine Tracker.xcodeproj"> + ReferencedContainer = "container:Gym Routine Tracker Watch.xcodeproj"> @@ -69,6 +69,10 @@ argument = "-com.apple.CoreData.Logging.stderr 0" isEnabled = "YES"> + + @@ -88,7 +92,7 @@ BlueprintIdentifier = "40CE1402293B0DBB00462D62" BuildableName = "Gym Routine Tracker Watch App.app" BlueprintName = "Gym Routine Tracker Watch App" - ReferencedContainer = "container:Gym Routine Tracker.xcodeproj"> + ReferencedContainer = "container:Gym Routine Tracker Watch.xcodeproj"> diff --git a/README.md b/README.md index 0336813..8ab8c7b 100644 --- a/README.md +++ b/README.md @@ -46,8 +46,9 @@ To any Apple product managers who like this app, please consider Sherlocking it! ## See Also -* [GRT Website](https://gym-routine-tracker.github.io) - Website for GRT +* [GRT Website](https://gym-routine-tracker.github.io) - Website for GRT (both watchOS and iOS implementations) * [GRT on the App Store](https://apps.apple.com/us/app/gym-routine-tracker/id6444747204) - App Store link for free download of GRT +* [GroutUI](https://github.com/gym-routine-tracker/GroutUI) - shared UI layer for GRT (watchOS and iOS) * [GroutLib](https://github.com/gym-routine-tracker/GroutLib) - shared business logic and data layer for GRT Apps by the same author: @@ -57,7 +58,7 @@ Apps by the same author: ## License -Copyright 2022 OpenAlloc LLC +Copyright 2022, 2023 OpenAlloc LLC All application code is licensed under the [Mozilla Public License 2](https://www.mozilla.org/en-US/MPL/2.0/), except where noted in individual modules. diff --git a/Gym Routine Tracker Watch App.entitlements b/Sources/App.entitlements similarity index 100% rename from Gym Routine Tracker Watch App.entitlements rename to Sources/App.entitlements diff --git a/Sources/Main View/ContentView.swift b/Sources/ContentView.swift similarity index 78% rename from Sources/Main View/ContentView.swift rename to Sources/ContentView.swift index 20b8e88..2f77d05 100644 --- a/Sources/Main View/ContentView.swift +++ b/Sources/ContentView.swift @@ -1,7 +1,7 @@ // // ContentView.swift // -// Copyright 2022 OpenAlloc LLC +// Copyright 2022, 2023 OpenAlloc LLC // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -12,15 +12,15 @@ import CoreData import SwiftUI import GroutLib +import GroutUI struct ContentView: View { - @State var router: NavigationPath = .init() + @SceneStorage("main-routines-nav") private var routinesNavData: Data? var body: some View { - NavigationStack(path: $router) { - RoutineList(router: $router) + NavStack(name: "main", navData: $routinesNavData) { + RoutineList() } - .interactiveDismissDisabled() // NOTE: needed to prevent home button from dismissing sheet } } diff --git a/Sources/Exercise Detail/ExerciseDetail.swift b/Sources/Exercise Detail/ExerciseDetail.swift deleted file mode 100644 index 8911b6b..0000000 --- a/Sources/Exercise Detail/ExerciseDetail.swift +++ /dev/null @@ -1,54 +0,0 @@ -// -// ExerciseDetail.swift -// -// Copyright 2022 OpenAlloc LLC -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// - -import SwiftUI - -import GroutLib - -struct ExerciseDetail: View { - var exercise: Exercise - - @State private var tabSelected = 1 - - var body: some View { - TabView(selection: $tabSelected) { - ExerciseFirstTab(exercise: exercise) - .tag(1) - ExerciseVolumeTab(exercise: exercise) - .tag(2) - ExerciseIntensityTab(exercise: exercise) - .tag(3) - } - .tabViewStyle(.page) - .symbolRenderingMode(.hierarchical) - .navigationTitle { - Text("Exercise") - .foregroundColor(exerciseColor) - } - .onDisappear { - PersistenceManager.shared.save() - } - } -} - -struct ExerciseDetail_Previews: PreviewProvider { - static var previews: some View { - let ctx = PersistenceManager.preview.container.viewContext - let routine = Routine.create(ctx, userOrder: 0) - routine.name = "Back & Bicep" - let exercise = Exercise.create(ctx, userOrder: 0) - exercise.name = "Lat Pulldown" - exercise.routine = routine - return NavigationStack { - ExerciseDetail(exercise: exercise) - } - .environment(\.managedObjectContext, ctx) - } -} diff --git a/Sources/Exercise Detail/ExerciseFirstTab.swift b/Sources/Exercise Detail/ExerciseFirstTab.swift deleted file mode 100644 index 0c10815..0000000 --- a/Sources/Exercise Detail/ExerciseFirstTab.swift +++ /dev/null @@ -1,55 +0,0 @@ -// -// ExerciseFirstTab.swift -// -// Copyright 2022 OpenAlloc LLC -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// - -import SwiftUI - -import GroutLib - -struct ExerciseFirstTab: View { - // MARK: - Parameters - - @ObservedObject var exercise: Exercise - - // MARK: - Views - - var body: some View { - Form { - Section("Name") { - TextFieldWithPresets($exercise.wrappedName, - prompt: "Enter exercise name", - color: exerciseColor, - presets: exercisePresets) - } - - Section("Primary Setting") { - Stepper(value: $exercise.primarySetting, in: settingRange, step: 1) { - stepperValueDisplay(value: exercise.primarySetting) - } - .tint(exerciseColor) - } - - Section("Secondary Setting") { - Stepper(value: $exercise.secondarySetting, in: settingRange, step: 1) { - stepperValueDisplay(value: exercise.secondarySetting) - } - .tint(exerciseColor) - } - } - } -} - -struct ExerciseTab1_Previews: PreviewProvider { - static var previews: some View { - let ctx = PersistenceManager.preview.container.viewContext - let exercise = Exercise.create(ctx, userOrder: 0) - exercise.name = "Lat Pulldown" - return ExerciseFirstTab(exercise: exercise) - } -} diff --git a/Sources/Exercise Detail/ExerciseIntensityTab.swift b/Sources/Exercise Detail/ExerciseIntensityTab.swift deleted file mode 100644 index bbec8e0..0000000 --- a/Sources/Exercise Detail/ExerciseIntensityTab.swift +++ /dev/null @@ -1,82 +0,0 @@ -// -// ExerciseIntensityTab.swift -// -// Copyright 2022 OpenAlloc LLC -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// - -import SwiftUI - -import GroutLib - -struct ExerciseIntensityTab: View { - // MARK: - Parameters - - @ObservedObject private var exercise: Exercise - - internal init(exercise: Exercise) { - self.exercise = exercise - _units = State(initialValue: exercise.units) - } - - // MARK: - Locals - - @State private var units: Int16 - - var body: some View { - Form { - Section("Intensity") { - Stepper(value: $exercise.lastIntensity, in: 0 ... intensityMaxValue, step: exercise.intensityStep) { - stepperValueDisplay(value: exercise.lastIntensity, units: Units.from(exercise.units)) - } - .tint(exerciseColor) - } - - Section("Intensity Step") { - Stepper(value: $exercise.intensityStep, in: 0.1 ... 25, step: 0.1) { - stepperValueDisplay(value: exercise.intensityStep, units: Units.from(exercise.units)) - } - .tint(exerciseColor) - } - - Section("Intensity Units") { - Picker(selection: $units) { - ForEach(Units.allCases, id: \.self) { unit in - Text(unit.formattedDescription) - .font(.title3) - .tag(unit.rawValue) - } - } label: { - EmptyView() - } - .pickerStyle(.wheel) - .onChange(of: units) { - exercise.units = $0 - } - } - - Section { - Toggle(isOn: $exercise.invertedIntensity) { - Text("Inverted") - } - .tint(exerciseColor) - } header: { - Text("Advance Direction") - } footer: { - Text("Example: if inverted with step of 5, advance from 25 to 20") - } - } - } -} - -struct ExerciseTab3_Previews: PreviewProvider { - static var previews: some View { - let ctx = PersistenceManager.preview.container.viewContext - let exercise = Exercise.create(ctx, userOrder: 0) - exercise.name = "Lat Pulldown" - return ExerciseIntensityTab(exercise: exercise) - } -} diff --git a/Sources/Exercise Detail/ExerciseVolumeTab.swift b/Sources/Exercise Detail/ExerciseVolumeTab.swift deleted file mode 100644 index a445902..0000000 --- a/Sources/Exercise Detail/ExerciseVolumeTab.swift +++ /dev/null @@ -1,48 +0,0 @@ -// -// ExerciseVolumeTab.swift -// -// Copyright 2022 OpenAlloc LLC -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// - -import SwiftUI - -import GroutLib - -struct ExerciseVolumeTab: View { - // MARK: - Parameters - - @ObservedObject var exercise: Exercise - - // MARK: - Views - - var body: some View { - Form { - Section("Set Count") { - Stepper(value: $exercise.sets, in: 0 ... 10, step: 1) { - stepperValueDisplay(value: exercise.sets) - } - .tint(exerciseColor) - } - - Section("Repetition Count") { - Stepper(value: $exercise.repetitions, in: 0 ... 100, step: 1) { - stepperValueDisplay(value: exercise.repetitions) - } - .tint(exerciseColor) - } - } - } -} - -struct ExerciseTab2_Previews: PreviewProvider { - static var previews: some View { - let ctx = PersistenceManager.preview.container.viewContext - let exercise = Exercise.create(ctx, userOrder: 0) - exercise.name = "Lat Pulldown" - return ExerciseVolumeTab(exercise: exercise) - } -} diff --git a/Sources/Helper/ActionButton.swift b/Sources/Helper/ActionButton.swift deleted file mode 100644 index 2caab39..0000000 --- a/Sources/Helper/ActionButton.swift +++ /dev/null @@ -1,64 +0,0 @@ -// -// ActionButton.swift -// -// Copyright 2022 OpenAlloc LLC -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// - -import SwiftUI - -struct ActionButton: View { - var action: () -> Void - var imageSystemName: String // "arrow.backward" - var buttonText: String? // "Previous" - var tint: Color - var onLongPress: (() -> Void)? - - var body: some View { - VStack { - Group { - if onLongPress != nil { - Button(action: {}, label: label) - .simultaneousGesture( - LongPressGesture() - .onEnded { _ in - onLongPress?() - } - ) - .highPriorityGesture( - TapGesture() - .onEnded { _ in - action() - } - ) - } else { - Button(action: action, label: label) - } - } - .symbolRenderingMode(.hierarchical) - .foregroundStyle(.tint) - .tint(tint) - .font(.title) - .fontWeight(.bold) - - if let _text = buttonText { - Text(_text) - .font(.caption2) - .lineLimit(1) - } - } - } - - private func label() -> some View { - Image(systemName: imageSystemName) - } -} - -struct ActionButton_Previews: PreviewProvider { - static var previews: some View { - ActionButton(action: {}, imageSystemName: "arrow.backward", buttonText: "Previous", tint: .green) - } -} diff --git a/Sources/Helper/AddButton.swift b/Sources/Helper/AddButton.swift deleted file mode 100644 index 45f3aea..0000000 --- a/Sources/Helper/AddButton.swift +++ /dev/null @@ -1,31 +0,0 @@ -// -// AddButton.swift -// -// Copyright 2022 OpenAlloc LLC -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// - -import SwiftUI - -struct AddButton: View { - var addAction: () -> Void - var title: String - - var body: some View { - Button(action: addAction) { - Label("Add \(title)", systemImage: "plus.circle.fill") - .symbolRenderingMode(.hierarchical) - } - .foregroundStyle(.tint) - } -} - -struct AddButton_Previews: PreviewProvider { - static var previews: some View { - AddButton(addAction: {}, title: "Routine") - .tint(.accentColor) - } -} diff --git a/Sources/Helper/AppIcon.swift b/Sources/Helper/AppIcon.swift deleted file mode 100644 index 7f549aa..0000000 --- a/Sources/Helper/AppIcon.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// AppIcon.swift -// Gym Routine Tracker Watch App -// -// Created by Reed Esau on 12/4/22. -// - -import SwiftUI - -struct AppIcon: View { - var body: some View { - if let img = UIImage(named: "grt_icon") { - Image(uiImage: img) - .resizable() - } else { - // in case the AppIcon has been stripped from the bundle - Image(systemName: "info.circle") - } - } -} - -struct AppIcon_Previews: PreviewProvider { - static var previews: some View { - AppIcon() - } -} diff --git a/Sources/Helper/Bundle-extension.swift b/Sources/Helper/Bundle-extension.swift deleted file mode 100644 index d95f19d..0000000 --- a/Sources/Helper/Bundle-extension.swift +++ /dev/null @@ -1,29 +0,0 @@ -// -// Bundle-extension.swift -// -// Copyright 2022 OpenAlloc LLC -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// - -import SwiftUI - -extension Bundle { - var appName: String? { - infoDictionary?["CFBundleName"] as? String - } - - var displayName: String? { - infoDictionary?["CFBundleDisplayName"] as? String - } - - var releaseVersionNumber: String? { - infoDictionary?["CFBundleShortVersionString"] as? String - } - - var buildNumber: String? { - infoDictionary?["CFBundleVersion"] as? String - } -} diff --git a/Sources/Helper/Constants.swift b/Sources/Helper/Constants.swift deleted file mode 100644 index 98e8737..0000000 --- a/Sources/Helper/Constants.swift +++ /dev/null @@ -1,51 +0,0 @@ -// -// Helper.swift -// -// Copyright 2022 OpenAlloc LLC -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// - -import SwiftUI - -import GroutLib - -let websiteDomain = "gym-routine-tracker.github.io" -let websiteTitle = "Gym Routine Tracker" -let copyright = "Copyright 2022 OpenAlloc LLC" - -let routineColor: Color = .accentColor -let routineListItemTint: Color = .accentColor.opacity(0.2) - -let exerciseColor: Color = .yellow -let exerciseListItemTint: Color = .yellow.opacity(0.2) - -let stopColor: Color = .pink - -let exerciseDoneColor: Color = .green -let exerciseUndoColor: Color = .green -let exerciseAdvanceColor: Color = .mint -let exerciseNextColor: Color = .blue - -let exerciseGearColor: Color = .gray -let exerciseSetsColor: Color = .teal - -let titleColor: Color = .primary.opacity(0.8) -let lastColor: Color = .primary.opacity(0.6) -let disabledColor: Color = .secondary.opacity(0.4) -let completedColor: Color = .secondary.opacity(0.5) - -let titleWeight: Font.Weight = .medium -let numberWeight: Font.Weight = .light - -let numberFont: Font = .title2 - -let settingRange: ClosedRange = 0 ... 50 -let intensityMaxValue: Float = 500 - -// How frequently to update time strings in RoutineCell -let routineSinceUpdateSeconds: TimeInterval = 60 - -let alwaysAdvanceOnLongPressKey = "alwaysAdvanceOnLongPress" diff --git a/Sources/Helper/ElapsedView.swift b/Sources/Helper/ElapsedView.swift deleted file mode 100644 index d44fa20..0000000 --- a/Sources/Helper/ElapsedView.swift +++ /dev/null @@ -1,77 +0,0 @@ -// -// StatusView.swift -// -// Copyright 2022 OpenAlloc LLC -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// - -import SwiftUI - -import Compactor - -import GroutLib - -struct ElapsedView: View { - var startedAt: Date - - // MARK: - Locals - - private let tc: TimeCompactor = .init(ifZero: "", style: .short, roundSmallToWhole: false) - - @State private var now = Date() - private let timer = Timer.publish(every: 1, - on: .current, - in: .common).autoconnect() - - // MARK: - Views - - var body: some View { - VStack { - ZStack { - RoundedRectangle(cornerRadius: 10) - .fill(routineColor.opacity(0.2)) - TitleText(remainingStr) - .fontDesign(.monospaced) - .padding(.horizontal) - .foregroundStyle(routineColor) - } - Text("Elapsed") - .font(.caption2) - .lineLimit(1) - } - .onReceive(timer) { _ in - self.now = Date.now - } - .onAppear { - self.now = Date.now - } - } - - // MARK: - Properties - - private var remainingStr: String { - let secondsPerHour: TimeInterval = 3600 - let et = elapsedTime - if et >= secondsPerHour { - return tc.string(from: et as NSNumber) ?? "" - } - let t = Int(max(0, min(et, TimeInterval(Int.max)))) - let minutes = t / 60 % 60 - let seconds = t % 60 - return String(format: "%02i:%02i", minutes, seconds) - } - - private var elapsedTime: TimeInterval { - now.timeIntervalSince(startedAt) - } -} - -struct StatusView_Previews: PreviewProvider { - static var previews: some View { - ElapsedView(startedAt: Date.now.addingTimeInterval(-3590)) - .frame(height: 80) - } -} diff --git a/Sources/Helper/FakeSection.swift b/Sources/Helper/FakeSection.swift deleted file mode 100644 index 3747f4a..0000000 --- a/Sources/Helper/FakeSection.swift +++ /dev/null @@ -1,39 +0,0 @@ -// -// FakeSection.swift -// -// Copyright 2022 OpenAlloc LLC -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// - -import SwiftUI - -struct FakeSection: View { - private var title: String - private var content: () -> Content - - internal init(_ title: String, content: @escaping () -> Content) { - self.title = title - self.content = content - } - - var body: some View { - VStack(alignment: .leading) { - Text(title.uppercased()) - .font(.footnote) - .foregroundColor(.primary.opacity(0.6)) - .padding(.leading, 10) - content() - } - } -} - -struct FakeSection_Previews: PreviewProvider { - static var previews: some View { - FakeSection("Foobar Baz Blah") { - Text("The Content") - } - } -} diff --git a/Sources/Helper/ImageStepper.swift b/Sources/Helper/ImageStepper.swift deleted file mode 100644 index 7079a7e..0000000 --- a/Sources/Helper/ImageStepper.swift +++ /dev/null @@ -1,49 +0,0 @@ -// -// ImageStepper.swift -// -// Copyright 2022 OpenAlloc LLC -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// - -import SwiftUI - -import GroutLib - -struct ImageStepper: View { - private var initialName: String? - private var imageNames: [String] - private var onSelect: (String) -> Void - - internal init(initialName: String? = nil, imageNames: [String], onSelect: @escaping (String) -> Void) { - self.initialName = initialName - self.imageNames = imageNames - self.onSelect = onSelect - - let index = imageNames.firstIndex(where: { $0 == initialName }) ?? 0 - _index = State(initialValue: index) - } - - // MARK: - Locals - - @State private var index: Int = 0 - - var body: some View { - Stepper(value: $index, in: 0 ... imageNames.count - 1) { - Image(systemName: imageNames[index]) - .imageScale(.small) - }.onChange(of: index) { newValue in - onSelect(imageNames[newValue]) - } - .symbolRenderingMode(.hierarchical) - .frame(height: 65) - } -} - -struct SystemImageStepper_Previews: PreviewProvider { - static var previews: some View { - ImageStepper(initialName: "flame", imageNames: systemImageNames, onSelect: { _ in }) - } -} diff --git a/Sources/Helper/PresetsPicker.swift b/Sources/Helper/PresetsPicker.swift deleted file mode 100644 index 4b4035d..0000000 --- a/Sources/Helper/PresetsPicker.swift +++ /dev/null @@ -1,72 +0,0 @@ -// -// PresetsPicker.swift -// -// Copyright 2022 OpenAlloc LLC -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// - -import Collections -import SwiftUI - -struct PresetsPicker: View { - typealias PresetsDictionary = OrderedDictionary - - var presets: PresetsDictionary - @Binding var showPresets: Bool - var onSelect: (String, String) -> Void - - var body: some View { - List { - ForEach(presets.keys, id: \.self) { key in - Section(header: Text(key)) { - ForEach(presets[key]!, id: \.self) { name in // .sorted(by: <) - Button { - onSelect(key, name) - showPresets = false - } label: { - Text(name) - } - } - } - } - } - .toolbar { - ToolbarItem(placement: .cancellationAction) { - Button("Cancel") { self.showPresets = false } - } - } - } -} - -struct PresetsList_Previews: PreviewProvider { - struct TestHolder: View { - let presets: OrderedDictionary = [ - "Machine/Free Weights": [ - "Abdominal", - "Arm Curl", - "Arm Ext", - ], - "Bodyweight": [ - "Crunch", - "Jumping-jack", - "Jump", - ], - ] - - @State var showPresets = false - var body: some View { - NavigationStack { - PresetsPicker(presets: presets, showPresets: $showPresets) { - logger.debug("#\(#function): Selected \($0) \($1)") - } - } - } - } - - static var previews: some View { - TestHolder() - } -} diff --git a/Sources/Helper/StepperUtils.swift b/Sources/Helper/StepperUtils.swift deleted file mode 100644 index a636f03..0000000 --- a/Sources/Helper/StepperUtils.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// StepperUtils.swift -// -// Copyright 2022 OpenAlloc LLC -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// - -import SwiftUI - -import GroutLib - -func stepperValueDisplay(value: Int16) -> some View { - Text("\(value)") - .font(numberFont) - .fontWeight(numberWeight) -} - -func stepperValueDisplay(value: Float, units: Units = .none) -> some View { - let spec: String = units != .none ? "%0.1f \(units.abbreviation)" : "%0.1f" - return Text("\(value, specifier: spec)") - .font(numberFont) - .fontWeight(numberWeight) -} diff --git a/Sources/Helper/TextFieldWithPresets.swift b/Sources/Helper/TextFieldWithPresets.swift deleted file mode 100644 index ffde2e2..0000000 --- a/Sources/Helper/TextFieldWithPresets.swift +++ /dev/null @@ -1,79 +0,0 @@ -// -// TextFieldWithPresets.swift -// -// Copyright 2022 OpenAlloc LLC -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// - -import Collections -import SwiftUI - -struct TextFieldWithPresets: View { - // MARK: - Parameters - - @Binding private var name: String - private var prompt: String - private var color: Color - private var presets: PresetsPicker.PresetsDictionary - - internal init(_ name: Binding, prompt: String, color: Color, presets: PresetsPicker.PresetsDictionary) { - _name = name - self.prompt = prompt - self.color = color - self.presets = presets - } - - // MARK: - Locals - - @State private var showPresetNames = false - - // MARK: - Views - - var body: some View { - HStack { - TextField(text: $name, prompt: Text(prompt)) { EmptyView() } - Button(action: { showPresetNames = true }) { - Image(systemName: "line.3.horizontal.decrease") - .imageScale(.large) - .symbolRenderingMode(.hierarchical) - .foregroundStyle(color) - } - .buttonStyle(.borderless) - } - .font(.title3) - .sheet(isPresented: $showPresetNames) { - NavigationStack { - PresetsPicker(presets: presets, showPresets: $showPresetNames) { _, presetName in - name = presetName - } - } - .interactiveDismissDisabled() // NOTE: needed to prevent home button from dismissing sheet - } - } -} - -struct NameTextField_Previews: PreviewProvider { - struct TestHolder: View { - let presets: OrderedDictionary = [ - "Machine/Free Weights": [ - "Abdominal", - "Arm Curl", - ], - "Bodyweight": [ - "Crunch", - "Jumping-jack", - ], - ] - @State var name: String = "Back & Bicep" - var body: some View { - TextFieldWithPresets($name, prompt: "Enter name", color: .teal, presets: presets) - } - } - - static var previews: some View { - TestHolder() - } -} diff --git a/Sources/Helper/TitleText.swift b/Sources/Helper/TitleText.swift deleted file mode 100644 index 2a63d44..0000000 --- a/Sources/Helper/TitleText.swift +++ /dev/null @@ -1,33 +0,0 @@ -// -// TitleText.swift -// -// Copyright 2022 OpenAlloc LLC -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// - -import SwiftUI - -struct TitleText: View { - private var text: String - - internal init(_ text: String) { - self.text = text - } - - var body: some View { - Text(text) - .font(.title) - .fontWeight(titleWeight) - .lineLimit(1) - .minimumScaleFactor(0.5) - } -} - -struct TitleView_Previews: PreviewProvider { - static var previews: some View { - TitleText("This is a test") - } -} diff --git a/Gym-Routine-Tracker-Watch-App-Info.plist b/Sources/Info.plist similarity index 73% rename from Gym-Routine-Tracker-Watch-App-Info.plist rename to Sources/Info.plist index ca9a074..665ea7b 100644 --- a/Gym-Routine-Tracker-Watch-App-Info.plist +++ b/Sources/Info.plist @@ -2,6 +2,10 @@ + NSUserActivityTypes + + org.openalloc.grout.run-routine + UIBackgroundModes remote-notification diff --git a/Sources/Main View/AboutView.swift b/Sources/Main View/AboutView.swift deleted file mode 100644 index 7331207..0000000 --- a/Sources/Main View/AboutView.swift +++ /dev/null @@ -1,47 +0,0 @@ -// -// AboutView.swift -// -// Copyright 2022 OpenAlloc LLC -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// - -import SwiftUI - -struct AboutView: View { - var body: some View { - VStack(alignment: .center) { - AppIcon() - .frame(width: 40, height: 40) - .padding(.bottom, 5) - - Text(Bundle.main.displayName ?? "unknown app") - .font(.title3) - Text("Version: \(Bundle.main.releaseVersionNumber ?? "unknown") (build \(Bundle.main.buildNumber ?? "?"))") - .foregroundColor(.secondary) - .padding(.bottom) - - Text("WEBSITE") - .font(.footnote) - .foregroundColor(.secondary) - Text(websiteDomain) - .font(.footnote) - .foregroundStyle(routineColor) - .padding(.bottom) - - Text(copyright) - .font(.caption2) - .foregroundStyle(.secondary) - } - .multilineTextAlignment(.center) - .minimumScaleFactor(0.5) - } -} - -struct AboutView_Previews: PreviewProvider { - static var previews: some View { - AboutView() - } -} diff --git a/Sources/Main View/RoutineCell.swift b/Sources/Main View/RoutineCell.swift deleted file mode 100644 index 21210d6..0000000 --- a/Sources/Main View/RoutineCell.swift +++ /dev/null @@ -1,104 +0,0 @@ -// -// RoutineCell.swift -// -// Copyright 2022 OpenAlloc LLC -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// - -import CoreData -import SwiftUI - -import GroutLib - -public struct RoutineCell: View { - @Environment(\.managedObjectContext) private var viewContext - - var routine: Routine - @Binding var now: Date - var onStart: (Routine.ID) -> Void - - // MARK: - Views - - public var body: some View { - VStack(alignment: .leading, spacing: 3) { - HStack { - VStack(alignment: .leading) { - HStack { - Image(systemName: routine.imageName ?? "dumbbell.fill") - Spacer() - } - } - .padding(.vertical, 12) - .contentShape(Rectangle()) - .onTapGesture(perform: startAction) -// .border(.teal) - - Spacer(minLength: 20) - - NavigationLink(value: routine) { - Image(systemName: "ellipsis.rectangle.fill") - // .imageScale(.large) - .padding(.leading, 20) - .padding(.vertical, 10) -// .border(.teal) - } - } - .foregroundColor(routineColor) - .font(.title2) - .symbolRenderingMode(.hierarchical) - - VStack(alignment: .leading) { - TitleText(routine.name ?? "unknown") - .foregroundColor(titleColor) - - RoutineSinceText(routine: routine, now: $now) - .font(.body) - .italic() - .foregroundColor(lastColor) - .lineLimit(1) - } - .contentShape(Rectangle()) - .onTapGesture(perform: startAction) -// .border(.teal) - } - .onAppear { - // refresh immediately on routine completion (timer only updates 'now' on on the minute) - now = Date.now - } - } - - // MARK: - Actions - - private func startAction() { - onStart(routine.id) - } -} - -struct RoutineCell_Previews: PreviewProvider { - struct TestHolder: View { - var routine: Routine - @State var selectedTab: URL = RoutineRunView.controlTab - @State var now: Date = .now - var body: some View { - List { - RoutineCell(routine: routine, now: $now, onStart: { _ in }) - } - } - } - - static var previews: some View { - let ctx = PersistenceManager.preview.container.viewContext - let routine = Routine.create(ctx, userOrder: 0) - routine.name = "Back & Bicep" - routine.lastDuration = 3545 - routine.lastStartedAt = Date.now.addingTimeInterval(-364 * 86400) - return NavigationStack { - TestHolder(routine: routine) - .environment(\.managedObjectContext, ctx) - } - .environment(\.managedObjectContext, ctx) - } -} diff --git a/Sources/Main View/RoutineList.swift b/Sources/Main View/RoutineList.swift deleted file mode 100644 index b3a25f1..0000000 --- a/Sources/Main View/RoutineList.swift +++ /dev/null @@ -1,205 +0,0 @@ -// -// RoutineList.swift -// -// Copyright 2022 OpenAlloc LLC -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// - -import SwiftUI - -import GroutLib - -struct RoutineList: View { - @Environment(\.managedObjectContext) private var viewContext - - // MARK: - Parameters - - @Binding var router: NavigationPath - - // MARK: - Locals - - @FetchRequest( - sortDescriptors: [NSSortDescriptor(keyPath: \Routine.userOrder, ascending: true)], - animation: .default - ) - private var routines: FetchedResults - - @State private var selectedRoutine: Routine? - @State private var selectedTab: URL = RoutineRunView.controlTab - @State private var startedAt: Date = .distantFuture - - // timer used to refresh "2d ago, for 16.5m" on each Routine Cell - @State private var now = Date() - private let timer = Timer.publish(every: routineSinceUpdateSeconds, - on: .current, - in: .common).autoconnect() - - // MARK: - Views - - var body: some View { - List { - ForEach(routines, id: \.self) { routine in - RoutineCell(routine: routine, - now: $now, - onStart: startAction) - } - .onMove(perform: moveAction) - .onDelete(perform: deleteAction) - .listItemTint(routineListItemTint) - - Group { - addButton - settingsButton - aboutButton - } - .font(.title3) - .tint(routineColor) - .foregroundStyle(.tint) - } - .navigationDestination(for: Routine.self) { - RoutineDetail(router: $router, routine: $0) - } - .navigationDestination(for: Exercise.self) { - ExerciseDetail(exercise: $0) - } - .navigationTitle("Routines") - .sheet(item: $selectedRoutine) { routine in - NavigationStack { - RoutineRunView(routine: routine, - selectedTab: $selectedTab, - startedAt: $startedAt, - onStop: stopAction) - .environment(\.managedObjectContext, viewContext) - } - .interactiveDismissDisabled() // NOTE: needed to prevent home button from dismissing sheet - } - .onReceive(timer) { _ in - self.now = Date.now - } - } - - private var addButton: some View { - Button(action: addAction) { - Label("Add Routine", systemImage: "plus.circle.fill") - .symbolRenderingMode(.hierarchical) - } - } - - private var settingsButton: some View { - NavigationLink(destination: { - SettingsForm() - }) { - Label("Settings", systemImage: "gear.circle") - .symbolRenderingMode(.hierarchical) - } - } - - private var aboutButton: some View { - NavigationLink(destination: { AboutView() }) { - Label(title: { Text("About") }, icon: { - AppIcon() - .frame(width: 24, height: 24) - }) - } - } - - // MARK: - Properties - - private var maxOrder: Int16 { - routines.last?.userOrder ?? 0 - } - - // MARK: - Actions - - private func addAction() { - withAnimation { - let nu = Routine.create(viewContext, userOrder: maxOrder + 1) - nu.name = "New Routine" - PersistenceManager.shared.save(forced: true) - router.append(nu) - } - } - - private func deleteAction(offsets: IndexSet) { - offsets.map { routines[$0] }.forEach(viewContext.delete) - PersistenceManager.shared.save() - } - - private func moveAction(from source: IndexSet, to destination: Int) { - Routine.move(routines, from: source, to: destination) - PersistenceManager.shared.save() - } - - private func startAction(_ routineID: Routine.ID) { - guard let routine = routines.first(where: { $0.id == routineID }) else { - logger.debug("#\(#function): couldn't find routine; not starting") - return - } - - logger.notice("#\(#function): Start Routine \(routine.wrappedName)") - - do { - // NOTE: storing startedAt locally (not in routine.lastStartedAt) - // to ignore mistaken starts. - startedAt = try routine.start(viewContext) - PersistenceManager.shared.save() - - if let ex = try routine.getNextIncomplete(viewContext) { - selectedTab = ex.objectID.uriRepresentation() - } - - selectedRoutine = routine // displays sheet - - } catch { - let nserror = error as NSError - logger.error("#\(#function): Start failure \(nserror.localizedDescription)") - } - } - - private func stopAction(_ routine: Routine) { - logger.notice("#\(#function): Stop Routine \(routine.wrappedName)") - if routine.stop(startedAt: startedAt) { - PersistenceManager.shared.save() - } else { - logger.debug("#\(#function): not recorded, probably because no exercises completed") - } - startedAt = Date.distantFuture - selectedRoutine = nil // closes sheet - } -} - -// TODO: four copies of each routine showing up; should be one! -struct RoutineList_Previews: PreviewProvider { - struct TestHolder: View { - @State private var router = NavigationPath() - var body: some View { - NavigationStack(path: $router) { - RoutineList(router: $router) - } - } - } - - static var previews: some View { - let ctx = PersistenceManager.preview.container.viewContext - - let rA = Routine.create(ctx, userOrder: 0) - rA.name = "Circuit" - let e1 = Exercise.create(ctx, userOrder: 0) - e1.name = "Lat Pulldown" - e1.routine = rA - let e2 = Exercise.create(ctx, userOrder: 1) - e2.name = "Arm Curl" - e2.routine = rA - - let rB = Routine.create(ctx, userOrder: 1) - rB.name = "Chest & Shoulders" - let e3 = Exercise.create(ctx, userOrder: 0) - e3.name = "Chest Press" - e3.routine = rB - return TestHolder() - .environment(\.managedObjectContext, ctx) - } -} diff --git a/Sources/Main View/RoutineSinceText.swift b/Sources/Main View/RoutineSinceText.swift deleted file mode 100644 index 44ca561..0000000 --- a/Sources/Main View/RoutineSinceText.swift +++ /dev/null @@ -1,83 +0,0 @@ -// -// RoutineSinceText.swift -// -// Copyright 2022 OpenAlloc LLC -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// - -import Combine -import SwiftUI - -import Compactor - -import GroutLib - -struct RoutineSinceText: View { - var routine: Routine - @Binding var now: Date - - // MARK: - Locals - - private let tcDur: TimeCompactor = .init(ifZero: "", style: .short, roundSmallToWhole: false) - private let tcSince: TimeCompactor = .init(ifZero: nil, style: .short, roundSmallToWhole: true) - - // MARK: - Views - - var body: some View { - VStack { - if let _lastStr = lastStr { - Text(_lastStr) - } else { - EmptyView() - } - } - } - - // MARK: - Properties - - private var lastStr: String? { - guard let _sinceStr = sinceStr, - let _durationStr = durationStr - else { return nil } - return "\(_sinceStr) ago, for \(_durationStr)" - } - - // time interval since the last workout ended, formatted compactly - private var sinceStr: String? { - guard let lastStartedAt = routine.lastStartedAt, - routine.lastDuration > 0 - else { return nil } - let since = max(0, now.timeIntervalSince(lastStartedAt) - routine.lastDuration) - return tcSince.string(from: since as NSNumber) - } - - private var durationStr: String? { - tcDur.string(from: routine.lastDuration as NSNumber) - } -} - -struct RoutineSinceText_Previews: PreviewProvider { - struct TestHolder: View { - var routine: Routine - @State var now: Date = .now - var body: some View { - RoutineSinceText(routine: routine, now: $now) - } - } - - static var previews: some View { - let ctx = PersistenceManager.preview.container.viewContext - let routine = Routine.create(ctx, userOrder: 0) - routine.name = "Back & Bicep" - routine.lastDuration = 1000 - routine.lastStartedAt = Date.now.addingTimeInterval(-2 * 86400) - return NavigationStack { - TestHolder(routine: routine) - .environment(\.managedObjectContext, ctx) - } - .environment(\.managedObjectContext, ctx) - } -} diff --git a/Sources/Main View/SettingsForm.swift b/Sources/Main View/SettingsForm.swift deleted file mode 100644 index 86765e4..0000000 --- a/Sources/Main View/SettingsForm.swift +++ /dev/null @@ -1,41 +0,0 @@ -// -// SettingsForm.swift -// -// Copyright 2022 OpenAlloc LLC -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// - -import SwiftUI - -struct SettingsForm: View { - @AppStorage(alwaysAdvanceOnLongPressKey) var alwaysAdvanceOnLongPress: Bool = false - - var body: some View { - Form { - Section("Exercises") { - Toggle("On long press, always advance intensity", isOn: $alwaysAdvanceOnLongPress) - } - -// NavigationLink(destination: { AboutView() }) { -// HStack { -// Text("About Gym Routine Tracker") -// // AppIcon() -// Spacer(minLength: 5) -// Image(systemName: "chevron.forward") -// } -// } - } - .navigationTitle("Settings") - } -} - -struct SettingsForm_Previews: PreviewProvider { - static var previews: some View { - NavigationStack { - SettingsForm() - } - } -} diff --git a/Sources/Routine Detail/ExerciseList.swift b/Sources/Routine Detail/ExerciseList.swift deleted file mode 100644 index fe994f4..0000000 --- a/Sources/Routine Detail/ExerciseList.swift +++ /dev/null @@ -1,119 +0,0 @@ -// -// ExerciseList.swift -// -// Copyright 2022 OpenAlloc LLC -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// - -import SwiftUI - -import GroutLib - -struct ExerciseList: View { - @Environment(\.managedObjectContext) private var viewContext - - // MARK: - Parameters - - @Binding private var router: NavigationPath - private var routine: Routine - - internal init(router: Binding, - routine: Routine) - { - _router = router - self.routine = routine - - let sort = [NSSortDescriptor(keyPath: \Exercise.userOrder, ascending: true)] - let pred = NSPredicate(format: "routine = %@", routine) - _exercises = FetchRequest(sortDescriptors: sort, predicate: pred) - } - - // MARK: - Locals - - @FetchRequest private var exercises: FetchedResults - - // MARK: - Views - - var body: some View { - List { - ForEach(exercises, id: \.self) { exercise in - NavigationLink(value: exercise) { - Text("\(exercise.name ?? "unknown")") // \(exercise.userOrder) - } - .buttonStyle(.borderless) - } - .onMove(perform: moveAction) - .onDelete(perform: deleteAction) - .listItemTint(exerciseListItemTint) - - addButton - } - .font(.title3) - } - - private var addButton: some View { - Button(action: addAction) { - Label("Add Exercise", systemImage: "plus.circle.fill") - .symbolRenderingMode(.hierarchical) - } - .tint(exerciseColor) - .foregroundStyle(.tint) - } - - // MARK: - Properties - - private var maxOrder: Int16 { - exercises.last?.userOrder ?? 0 - } - - // MARK: - Actions - - private func addAction() { - withAnimation { - let nu = Exercise.create(viewContext, userOrder: maxOrder + 1) - nu.name = "New Exercise" - nu.routine = routine - PersistenceManager.shared.save(forced: true) - router.append(nu) - } - } - - private func deleteAction(offsets: IndexSet) { - offsets.map { exercises[$0] }.forEach(viewContext.delete) - PersistenceManager.shared.save() - } - - private func moveAction(from source: IndexSet, to destination: Int) { - Exercise.move(exercises, from: source, to: destination) - PersistenceManager.shared.save() - } -} - -struct ExerciseList_Previews: PreviewProvider { - struct TestHolder: View { - var routine: Routine - @State var router: NavigationPath = .init() - var body: some View { - NavigationStack(path: $router) { - ExerciseList(router: $router, routine: routine) - } - } - } - - static var previews: some View { - let ctx = PersistenceManager.preview.container.viewContext - let routine = Routine.create(ctx, userOrder: 0) - routine.name = "Back & Bicep" - let e1 = Exercise.create(ctx, userOrder: 0) - e1.name = "Lat Pulldown" - e1.routine = routine - let e2 = Exercise.create(ctx, userOrder: 0) - e2.name = "Arm Curl" - e2.routine = routine - return TestHolder(routine: routine) - .environment(\.managedObjectContext, ctx) - } -} diff --git a/Sources/Routine Detail/RoutineDetail.swift b/Sources/Routine Detail/RoutineDetail.swift deleted file mode 100644 index 04741ea..0000000 --- a/Sources/Routine Detail/RoutineDetail.swift +++ /dev/null @@ -1,95 +0,0 @@ -// -// RoutineDetail.swift -// -// Copyright 2022 OpenAlloc LLC -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// - -import SwiftUI - -import GroutLib - -struct RoutineDetail: View { - @Environment(\.managedObjectContext) private var viewContext - - // MARK: - Parameters - - @Binding var router: NavigationPath - - @ObservedObject var routine: Routine - - // MARK: - Views - - var body: some View { - TabView { - Form { - Section("Name") { - TextFieldWithPresets($routine.wrappedName, - prompt: "Enter routine name", - color: routineColor, - presets: routinePresets) - } - - Section("Image") { - ImageStepper(initialName: routine.imageName, imageNames: systemImageNames) { - routine.imageName = $0 - } - } - } - - FakeSection("Exercises") { - ExerciseList(router: $router, routine: routine) - } - } - .tabViewStyle(.page) - .navigationTitle { - Text("Routine") - .foregroundColor(routineColor) - } - .onDisappear { - logger.debug("Routine Detail, onDisappear") - PersistenceManager.shared.save() - } - } - - // MARK: - Properties - - private var exerciseCount: Int { - routine.exercises?.count ?? 0 - } - - private var exerciseButtonDisplay: String { - "\(exerciseCount) Item\(exerciseCount == 1 ? "" : "s")" - } -} - -struct RoutineDetail_Previews: PreviewProvider { - struct TestHolder: View { - var routine: Routine - @State var router: NavigationPath = .init() - var body: some View { - NavigationStack(path: $router) { - RoutineDetail(router: $router, routine: routine) - } - } - } - - static var previews: some View { - let ctx = PersistenceManager.preview.container.viewContext - let routine = Routine.create(ctx, userOrder: 0) - routine.name = "Back & Bicep" - let e1 = Exercise.create(ctx, userOrder: 0) - e1.name = "Lat Pulldown" - e1.routine = routine - let e2 = Exercise.create(ctx, userOrder: 1) - e2.name = "Arm Curl" - e2.routine = routine - return NavigationStack { - TestHolder(routine: routine) - } - .environment(\.managedObjectContext, ctx) - } -} diff --git a/Sources/Run Views/ExerciseRunMiddleRow.swift b/Sources/Run Views/ExerciseRunMiddleRow.swift deleted file mode 100644 index 8fb4baf..0000000 --- a/Sources/Run Views/ExerciseRunMiddleRow.swift +++ /dev/null @@ -1,66 +0,0 @@ -// -// MiddleRow.swift -// -// Copyright 2022 OpenAlloc LLC -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// - -import SwiftUI - -enum ExerciseMiddleRowMode { - case intensity - case gear - case sets -} - -struct ExerciseRunMiddleRow: View { - var imageName: String - var imageColor: Color - var onDetail: () -> Void - var onTap: () -> Void - var content: () -> Content - - var body: some View { - HStack { - Button(action: onTap) { - Image(systemName: imageName) - .symbolRenderingMode(.hierarchical) - .foregroundStyle(imageColor) - } - .padding(.vertical) - .buttonStyle(.borderless) - .font(.title2) - // .border(.teal) - - Spacer() - - Button(action: onTap) { - content() - .frame(maxWidth: .infinity) - } - .buttonStyle(.borderless) - // .border(.teal) - - Spacer() - - Button(action: onDetail) { - Image(systemName: "ellipsis.circle.fill") - .symbolRenderingMode(.hierarchical) - .foregroundStyle(.yellow) - } - .padding(.vertical) - .buttonStyle(.borderless) - .font(.title2) - // .border(.teal) - } - } -} - -// struct MiddleRow_Previews: PreviewProvider { -// static var previews: some View { -// MiddleRow() -// } -// } diff --git a/Sources/Run Views/ExerciseRunView.swift b/Sources/Run Views/ExerciseRunView.swift deleted file mode 100644 index 821f3f7..0000000 --- a/Sources/Run Views/ExerciseRunView.swift +++ /dev/null @@ -1,295 +0,0 @@ -// -// ExerciseRunView.swift -// -// Copyright 2022 OpenAlloc LLC -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// - -import SwiftUI - -import GroutLib - -struct ExerciseRunView: View { - @Environment(\.managedObjectContext) private var viewContext - - @AppStorage(alwaysAdvanceOnLongPressKey) var alwaysAdvanceOnLongPress: Bool = false - - // MARK: - Parameters - - @ObservedObject var exercise: Exercise - @Binding var middleMode: ExerciseMiddleRowMode - var nextAction: (Int16?) -> Void - var hasNextIncomplete: () -> Bool - @Binding var selectedExercise: Exercise? - - // MARK: - Locals - - @State private var showAdvanceAlert = false - - // MARK: - Views - - var body: some View { - // rows sized to visually-appealling proportions - GeometryReader { geo in - VStack(alignment: .center) { - top - .frame(height: geo.size.height * 3 / 13) - - middle - .frame(height: geo.size.height * 5 / 13) - - bottom - .frame(height: geo.size.height * 5 / 13) - } - } - .alert("Long Press", - isPresented: $showAdvanceAlert, - actions: { - VStack { - Button("Yes, advance") { markDone(withAdvance: true) } - Button("No") { markDone(withAdvance: false) } - Button("Always advance") { - alwaysAdvanceOnLongPress = true - markDone(withAdvance: true) - } - } - }, - message: { - Text(alertTitle) - }) - } - - private var top: some View { - TitleText(exercise.wrappedName) - .foregroundColor(isDone ? completedColor : exerciseColor) - } - - private var middle: some View { - VStack { - switch middleMode { - case .intensity: - midIntensity - case .gear: - midGear - case .sets: - midSets - } - } - } - - private var bottom: some View { - HStack { - ActionButton(action: isDone ? undoAction : doneAction, - imageSystemName: isDone ? "arrow.uturn.backward" : "checkmark", - buttonText: isDone ? "Undo" : "Done", - tint: isDone ? exerciseUndoColor : exerciseDoneColor, - onLongPress: isDone ? nil : doneLongPressAction) - - ActionButton(action: { nextAction(exercise.userOrder) }, - imageSystemName: "arrow.forward", - buttonText: "Next", - tint: nextColor) - .disabled(!hasNext) - } - } - - private var midIntensity: some View { - Stepper(value: $exercise.lastIntensity, - in: 0.0 ... intensityMaxValue, - step: exercise.intensityStep) { - intensityUnitsText - .minimumScaleFactor(0.5) - .lineLimit(1) - } - .fontWeight(numberWeight) - .symbolRenderingMode(.hierarchical) - .disabled(isDone) - .foregroundColor(isDone ? completedColor : .primary) - .contentShape(Rectangle()) - .onTapGesture { - middleMode = .gear - } - } - - private var intensityUnitsText: some View { - let units = Units.from(exercise.units) - return HStack(alignment: .center) { - Text(formatIntensity(exercise.lastIntensity)) - if units != .none { - Text(units.abbreviation) - .font(.title3) - } - } - } - - private var midGear: some View { - ExerciseRunMiddleRow(imageName: "gearshape.fill", - imageColor: exerciseGearColor, - onDetail: { selectedExercise = exercise }, - onTap: { middleMode = .sets }) { - HStack { - if exercise.primarySetting == 0, exercise.secondarySetting == 0 { - Text("None") - .font(.title2) - .foregroundStyle(exerciseGearColor) - } else { - Group { - if exercise.primarySetting > 0 { - numberImage(exercise.primarySetting, isCircle: true) - } - if exercise.secondarySetting > 0 { - numberImage(exercise.secondarySetting, isCircle: false) - } - } - // .symbolRenderingMode(.hierarchical) - .imageScale(.large) - .font(.title) - } - } - } - } - - private var midSets: some View { - ExerciseRunMiddleRow(imageName: "dumbbell.fill", - imageColor: exerciseSetsColor, - onDetail: { selectedExercise = exercise }, - onTap: { middleMode = .intensity }) { - TitleText("\(exercise.sets)/\(exercise.repetitions)") - .tint(.primary) - .fontDesign(.monospaced) - } - } - - private func numberImage(_ value: Int16, isCircle: Bool) -> some View { - // Image(systemName: numberImageName(Int(exercise.primarySetting), shape: .circle, filled: false)) - let prefix = systemImagePrefix(Int(value)) - let shape = isCircle ? "circle" : "square" - let full = "\(prefix).\(shape).fill" - - return ZStack { - Image(systemName: "\(shape).fill") - .foregroundColor(.white) - Image(systemName: full) - .foregroundColor(.black.opacity(0.8)) - } - .compositingGroup() - } - - // MARK: - Properties - - private var isStepFractional: Bool { - exercise.intensityStep.truncatingRemainder(dividingBy: 1) >= 0.1 - } - - private var canAdvance: Bool { - !isDone && !atMax - } - - private var advanceColor: Color { - canAdvance ? exerciseAdvanceColor : disabledColor - } - - private var hasNext: Bool { - hasNextIncomplete() - } - - private var nextColor: Color { - hasNextIncomplete() ? exerciseNextColor : disabledColor - } - - private var isDone: Bool { - exercise.lastCompletedAt != nil - } - - private var atMax: Bool { - intensityMaxValue <= exercise.lastIntensity - } - - private var advancedIntensity: Float { - if exercise.invertedIntensity { - // advance downwards - return max(0, exercise.lastIntensity - exercise.intensityStep) - } else { - // advance upwards - return min(intensityMaxValue, exercise.lastIntensity + exercise.intensityStep) - } - } - - private var alertTitle: String { - "Advance intensity from \(formatIntensity(exercise.lastIntensity)) to \(formatIntensity(advancedIntensity))?" - } - - // MARK: - Actions - - private func doneAction() { - logger.debug("#\(#function):") - markDone(withAdvance: false) - } - - private func undoAction() { - logger.debug("#\(#function):") - exercise.lastCompletedAt = nil - } - - private func doneLongPressAction() { - logger.debug("#\(#function):") - if alwaysAdvanceOnLongPress { - markDone(withAdvance: true) - } else { - showAdvanceAlert = true - } - } - - // MARK: - Helpers - - private func formatIntensity(_ intensity: Float) -> String { - let specifier = isStepFractional ? "%0.1f" : "%0.0f" - return String(format: specifier, intensity) - } - - private func markDone(withAdvance: Bool) { - exercise.lastCompletedAt = Date.now - - if withAdvance { - exercise.lastIntensity = advancedIntensity - } - - DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { - nextAction(exercise.userOrder) - } - } -} - -struct ExerciseRunView_Previews: PreviewProvider { - struct TestHolder: View { - var exercise: Exercise - @State var middleMode: ExerciseMiddleRowMode = .intensity - @State var selectedExercise: Exercise? - var body: some View { - ExerciseRunView(exercise: exercise, - middleMode: $middleMode, - nextAction: { _ in }, - hasNextIncomplete: { true }, - selectedExercise: $selectedExercise) - } - } - - static var previews: some View { - let ctx = PersistenceManager.preview.container.viewContext - let routine = Routine.create(ctx, userOrder: 0) - routine.name = "Back & Bicep" - let e1 = Exercise.create(ctx, userOrder: 0) - e1.name = "Lat Pulldown" - e1.routine = routine - e1.primarySetting = 4 - e1.secondarySetting = 6 - // e1.units = Units.kilograms.rawValue - e1.intensityStep = 7.1 - return NavigationStack { - TestHolder(exercise: e1) - } - } -} diff --git a/Sources/Run Views/RoutineControl.swift b/Sources/Run Views/RoutineControl.swift deleted file mode 100644 index 50cde91..0000000 --- a/Sources/Run Views/RoutineControl.swift +++ /dev/null @@ -1,135 +0,0 @@ -// -// RoutineControl.swift -// -// Copyright 2022 OpenAlloc LLC -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// - -import SwiftUI - -import GroutLib - -struct RoutineControl: View { - @Environment(\.managedObjectContext) private var viewContext - - // MARK: - Parameters - - var routine: Routine - @Binding var selectedTab: URL - let onStop: () -> Void - let nextAction: (Int16?) -> Void - var maxOrder: Int16 - var remainingCount: () -> Int - @Binding var middleMode: ExerciseMiddleRowMode - var startedAt: Date - - // MARK: - Views - - var body: some View { - // rows sized to visually-appealling proportions - GeometryReader { geo in - VStack(alignment: .center, spacing: 10) { - top - .frame(height: geo.size.height * 3 / 11) - middle - .frame(height: geo.size.height * 4 / 11) - .padding(.bottom, 3) - bottom(geo) - .frame(height: geo.size.height * 4 / 11) - } - } - } - - private var top: some View { - TitleText(routine.wrappedName) - .foregroundColor(titleColor) - } - - private var middle: some View { - HStack(alignment: .bottom) { - ActionButton(action: stopAction, - imageSystemName: "xmark", - buttonText: "Stop", - tint: stopColor) - - ElapsedView(startedAt: startedAt) - } - } - - private func bottom(_: GeometryProxy) -> some View { - HStack(alignment: .top) { - ActionButton(action: addAction, - imageSystemName: "plus", // plus.circle.fill - buttonText: "Add", - tint: exerciseColor) - ActionButton(action: { nextAction(nil) }, - imageSystemName: "arrow.forward", - buttonText: "Next", - tint: nextActionColor) - .disabled(!hasRemaining) - } - .padding(.bottom, 0) - } - - // MARK: - Properties - - private var nextActionColor: Color { - hasRemaining ? exerciseNextColor : disabledColor - } - - private var hasRemaining: Bool { - remainingCount() > 0 - } - - // MARK: - Actions - - private func stopAction() { - onStop() - } - - private func addAction() { - withAnimation { - let nu = Exercise.create(viewContext, userOrder: maxOrder + 1) - routine.addToExercises(nu) - PersistenceManager.shared.save(forced: true) - DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) { - selectedTab = nu.objectID.uriRepresentation() - middleMode = .gear - } - } - } -} - -struct RoutineControl_Previews: PreviewProvider { - struct TestHolder: View { - var routine: Routine - @State var selectedTab: URL = .init(string: "blah")! - @State var middleMode: ExerciseMiddleRowMode = .gear - var startedAt = Date.now.addingTimeInterval(-1200) - var body: some View { - RoutineControl(routine: routine, - selectedTab: $selectedTab, - onStop: {}, - nextAction: { _ in }, - maxOrder: 0, - remainingCount: { 3 }, - middleMode: $middleMode, - startedAt: startedAt) - } - } - - static var previews: some View { - let ctx = PersistenceManager.preview.container.viewContext - let routine = Routine.create(ctx, userOrder: 0) - routine.name = "Back & Bicep" - let e1 = Exercise.create(ctx, userOrder: 0) - e1.name = "Lat Pulldown" - e1.routine = routine - return NavigationStack { - TestHolder(routine: routine) - } - } -} diff --git a/Sources/Run Views/RoutineRunView.swift b/Sources/Run Views/RoutineRunView.swift deleted file mode 100644 index 32617f8..0000000 --- a/Sources/Run Views/RoutineRunView.swift +++ /dev/null @@ -1,186 +0,0 @@ -// -// RoutineRunView.swift -// -// Copyright 2022 OpenAlloc LLC -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// - -import SwiftUI - -import GroutLib - -struct RoutineRunView: View { - @Environment(\.managedObjectContext) private var viewContext - - // MARK: - Parameters - - private var routine: Routine - @Binding private var selectedTab: URL - @Binding private var startedAt: Date - private let onStop: (Routine) -> Void - - internal init(routine: Routine, - selectedTab: Binding, - startedAt: Binding, - onStop: @escaping (Routine) -> Void) - { - self.routine = routine - self.onStop = onStop - - _selectedTab = selectedTab - _startedAt = startedAt - - _exercises = FetchRequest(sortDescriptors: Routine.exerciseSort, - predicate: routine.exercisePredicate) - _incomplete = FetchRequest(sortDescriptors: Routine.exerciseSort, - predicate: routine.incompletePredicate) - } - - // MARK: - Locals - - static let controlTab = URL(string: "uri://control-panel")! - - @FetchRequest private var exercises: FetchedResults - @FetchRequest private var incomplete: FetchedResults - @State private var middleMode: ExerciseMiddleRowMode = .intensity - - // support for exercise detail - @State private var detailRouter: NavigationPath = .init() - @State private var selectedExercise: Exercise? - - // MARK: - Views - - var body: some View { - TabView(selection: $selectedTab) { - RoutineControl(routine: routine, - selectedTab: $selectedTab, - onStop: stopAction, - nextAction: nextAction, - maxOrder: maxOrder, - remainingCount: { remainingCount }, - middleMode: $middleMode, - startedAt: startedAt) - .tag(RoutineRunView.controlTab) - - ForEach(exercises, id: \.self) { exercise in - ExerciseRunView(exercise: exercise, - middleMode: $middleMode, - nextAction: nextAction, - hasNextIncomplete: { hasNextIncomplete }, - selectedExercise: $selectedExercise) - .tag(exercise.objectID.uriRepresentation()) - } - } - .tabViewStyle(.page) - .animation(.easeInOut(duration: 0.25), value: selectedTab) - .toolbar { - ToolbarItem(placement: .cancellationAction) { - toolbarItem - } - } - .navigationDestination(for: Exercise.self) { - ExerciseDetail(exercise: $0) - } - - // supporting exercise detail - .sheet(item: $selectedExercise) { exercise in - NavigationStack(path: $detailRouter) { - ExerciseDetail(exercise: exercise) - .toolbar { - ToolbarItem(placement: .cancellationAction) { - Button { self.selectedExercise = nil } label: { - Image(systemName: "chevron.backward") - } - } - } - .environment(\.managedObjectContext, viewContext) - } - .interactiveDismissDisabled() // NOTE: needed to prevent home button from dismissing sheet - } - } - - private var toolbarItem: some View { - Button(action: { selectedTab = RoutineRunView.controlTab }) { - Image(systemName: "control") - .foregroundColor(isOnControlPanel ? disabledColor : .primary) - } - .disabled(isOnControlPanel) - } - - // MARK: - Properties - - private var maxOrder: Int16 { - exercises.last?.userOrder ?? 0 - } - - private var isOnControlPanel: Bool { - selectedTab == RoutineRunView.controlTab - } - - private var remainingCount: Int { - incomplete.count - } - - private var hasRemaining: Bool { - remainingCount > 0 - } - - private var hasNextIncomplete: Bool { - remainingCount > 1 - } - - private var completedCount: Int { - exercises.count - remainingCount - } - - // MARK: - Actions - - private func stopAction() { - onStop(routine) // parent view will take down the sheet & save context - } - - // if next incomplete exercise exists, switch to its tab - private func nextAction(from userOrder: Int16?) { - if let nextIncomplete = try? routine.getNextIncomplete(viewContext, from: userOrder) { - selectedTab = nextIncomplete.objectID.uriRepresentation() - } else { - selectedTab = RoutineRunView.controlTab - } - } -} - -struct RoutineRunView_Previews: PreviewProvider { - struct TestHolder: View { - // @State var router: NavigationPath = .init() - var routine: Routine - @State var selectedTab: URL = RoutineRunView.controlTab - @State var startedAt: Date = Date.now.addingTimeInterval(-1000) - var body: some View { - NavigationStack { - RoutineRunView(routine: routine, selectedTab: $selectedTab, startedAt: $startedAt, onStop: { _ in }) - } - } - } - - static var previews: some View { - let ctx = PersistenceManager.preview.container.viewContext - let routine = Routine.create(ctx, userOrder: 0) - routine.name = "Back & Bicep" - let e1 = Exercise.create(ctx, userOrder: 0) - e1.name = "Lat Pulldown" - e1.routine = routine - e1.primarySetting = 4 - e1.secondarySetting = 6 - // e1.units = Units.kilograms.rawValue - e1.intensityStep = 7.1 - let e2 = Exercise.create(ctx, userOrder: 1) - e2.name = "Arm Curl" - e2.routine = routine - return - TestHolder(routine: routine) - .environment(\.managedObjectContext, ctx) - } -} diff --git a/Sources/Gym_Routine_Tracker_App.swift b/Sources/WatchApp.swift similarity index 79% rename from Sources/Gym_Routine_Tracker_App.swift rename to Sources/WatchApp.swift index 477d0b3..5c43a98 100644 --- a/Sources/Gym_Routine_Tracker_App.swift +++ b/Sources/WatchApp.swift @@ -1,7 +1,7 @@ // -// Gym_Routine_Tracker_App.swift +// WatchApp.swift // -// Copyright 2022 OpenAlloc LLC +// Copyright 2022, 2023 OpenAlloc LLC // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -13,11 +13,6 @@ import SwiftUI import GroutLib -let logger = Logger( - subsystem: Bundle.main.bundleIdentifier!, - category: String(describing: Gym_Routine_Tracker_Watch_App.self) -) - @main struct Gym_Routine_Tracker_Watch_App: App { let persistenceManager = PersistenceManager.shared