ソースを参照

Add NS profile import result view and functionality WIP

Deniz Cengiz 1 年間 前
コミット
c7982cf489

+ 12 - 0
FreeAPS.xcodeproj/project.pbxproj

@@ -459,6 +459,7 @@
 		DD6B7CB22C7B6F0800B75029 /* Rounding.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6B7CB12C7B6F0800B75029 /* Rounding.swift */; };
 		DD6B7CB42C7B71F700B75029 /* ForecastDisplayType.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6B7CB32C7B71F700B75029 /* ForecastDisplayType.swift */; };
 		DD6B7CB62C7B748B00B75029 /* TotalInsulinDisplayType.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6B7CB52C7B748B00B75029 /* TotalInsulinDisplayType.swift */; };
+		DD6B7CB92C7BAC6900B75029 /* NightscoutImportResultView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6B7CB82C7BAC6900B75029 /* NightscoutImportResultView.swift */; };
 		DD88C8E22C50420800F2D558 /* DefinitionRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD88C8E12C50420800F2D558 /* DefinitionRow.swift */; };
 		DDD163122C4C689900CD525A /* OverrideStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDD163112C4C689900CD525A /* OverrideStateModel.swift */; };
 		DDD163142C4C68D300CD525A /* OverrideProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDD163132C4C68D300CD525A /* OverrideProvider.swift */; };
@@ -1102,6 +1103,7 @@
 		DD6B7CB12C7B6F0800B75029 /* Rounding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Rounding.swift; sourceTree = "<group>"; };
 		DD6B7CB32C7B71F700B75029 /* ForecastDisplayType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastDisplayType.swift; sourceTree = "<group>"; };
 		DD6B7CB52C7B748B00B75029 /* TotalInsulinDisplayType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TotalInsulinDisplayType.swift; sourceTree = "<group>"; };
+		DD6B7CB82C7BAC6900B75029 /* NightscoutImportResultView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightscoutImportResultView.swift; sourceTree = "<group>"; };
 		DD88C8E12C50420800F2D558 /* DefinitionRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefinitionRow.swift; sourceTree = "<group>"; };
 		DDD163112C4C689900CD525A /* OverrideStateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverrideStateModel.swift; sourceTree = "<group>"; };
 		DDD163132C4C68D300CD525A /* OverrideProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverrideProvider.swift; sourceTree = "<group>"; };
@@ -2135,6 +2137,7 @@
 		4F4AE4D901E8BA872B207D7F /* View */ = {
 			isa = PBXGroup;
 			children = (
+				DD6B7CB72C7BAC1B00B75029 /* ProfileImport */,
 				8782B44544F38F2B2D82C38E /* NightscoutConfigRootView.swift */,
 				5A2325512BFCBF55003518CA /* NightscoutUploadView.swift */,
 				5A2325532BFCBF65003518CA /* NightscoutFetchView.swift */,
@@ -2649,6 +2652,14 @@
 			path = "Classes+Properties";
 			sourceTree = "<group>";
 		};
+		DD6B7CB72C7BAC1B00B75029 /* ProfileImport */ = {
+			isa = PBXGroup;
+			children = (
+				DD6B7CB82C7BAC6900B75029 /* NightscoutImportResultView.swift */,
+			);
+			path = ProfileImport;
+			sourceTree = "<group>";
+		};
 		DDD163032C4C67B400CD525A /* OverrideConfig */ = {
 			isa = PBXGroup;
 			children = (
@@ -3138,6 +3149,7 @@
 				3894873A2614928B004DF424 /* DispatchTimer.swift in Sources */,
 				3895E4C625B9E00D00214B37 /* Preferences.swift in Sources */,
 				CE94598429E9E3E60047C9C6 /* WatchConfigStateModel.swift in Sources */,
+				DD6B7CB92C7BAC6900B75029 /* NightscoutImportResultView.swift in Sources */,
 				38DF1786276A73D400B3528F /* TagCloudView.swift in Sources */,
 				38B4F3CD25E5031100E76A18 /* Broadcaster.swift in Sources */,
 				383420D925FFEB3F002D46C1 /* Popup.swift in Sources */,

+ 33 - 25
FreeAPS/Sources/Modules/NightscoutConfig/NightscoutConfigStateModel.swift

@@ -35,6 +35,10 @@ extension NightscoutConfig {
         @Published var allowAnnouncements: Bool = false
         @Published var isConnectedToNS: Bool = false
 
+        @Published var isProfileImportPresented: Bool = false
+        @Published var importErrors: [String] = []
+        @Published var importStatus: ImportStatus = .finished
+
         override func subscribe() {
             url = keychain.getValue(String.self, forKey: Config.urlKey) ?? ""
             secret = keychain.getValue(String.self, forKey: Config.secretKey) ?? ""
@@ -117,8 +121,11 @@ extension NightscoutConfig {
         }
 
         func importSettings() async {
+            importStatus = .running
+
             do {
                 guard let fetchedProfile = await nightscoutManager.importSettings() else {
+                    importStatus = .failed
                     throw NSError(
                         domain: "ImportError",
                         code: 1,
@@ -140,6 +147,8 @@ extension NightscoutConfig {
                 }
 
                 if carbratios.contains(where: { $0.ratio <= 0 }) {
+                    importStatus = .failed
+
                     throw NSError(
                         domain: "ImportError",
                         code: 2,
@@ -160,6 +169,8 @@ extension NightscoutConfig {
                 }
 
                 if pumpName != "Omnipod DASH", basals.contains(where: { $0.rate <= 0 }) {
+                    importStatus = .failed
+
                     throw NSError(
                         domain: "ImportError",
                         code: 3,
@@ -168,6 +179,8 @@ extension NightscoutConfig {
                 }
 
                 if pumpName == "Omnipod DASH", basals.reduce(0, { $0 + $1.rate }) <= 0 {
+                    importStatus = .failed
+
                     throw NSError(
                         domain: "ImportError",
                         code: 4,
@@ -188,6 +201,8 @@ extension NightscoutConfig {
                 }
 
                 if sensitivities.contains(where: { $0.sensitivity <= 0 }) {
+                    importStatus = .failed
+
                     throw NSError(
                         domain: "ImportError",
                         code: 5,
@@ -234,9 +249,10 @@ extension NightscoutConfig {
                                 dia: fetchedProfile.dia
                             )
                         case .failure:
-                            self.saveError(
+                            self.importErrors.append(
                                 "Settings were imported but the Basals couldn't be saved to pump (communication error)."
                             )
+                            self.importStatus = .failed
                         }
                     }
                 } else {
@@ -247,21 +263,9 @@ extension NightscoutConfig {
                         targetsProfile: targetsProfile,
                         dia: fetchedProfile.dia
                     )
-                    saveError("Settings were imported but the Basals couldn't be saved to pump (No pump).")
-                }
-
-                // Save DIA if different
-                let dia = fetchedProfile.dia
-                if dia != self.dia, dia >= 0 {
-                    let file = PumpSettings(insulinActionCurve: dia, maxBolus: maxBolus, maxBasal: maxBasal)
-                    storage.save(file, as: OpenAPS.Settings.settings)
-                    debug(.nightscout, "DIA setting updated to \(dia) after a NS import.")
                 }
-
-                debug(.service, "Settings imported successfully.")
-
             } catch {
-                saveError(error.localizedDescription)
+                importErrors.append(error.localizedDescription)
                 debug(.service, "Settings import failed with error: \(error.localizedDescription)")
             }
         }
@@ -284,6 +288,13 @@ extension NightscoutConfig {
             }
 
             debug(.service, "Settings imported successfully.")
+
+            DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
+                // stop blur
+                self.importStatus = .finished
+                // display next step
+                self.isProfileImportPresented = true
+            }
         }
 
         func offset(_ string: String) -> Int {
@@ -292,17 +303,6 @@ extension NightscoutConfig {
             return ((hours * 60) + minutes) * 60
         }
 
-        func saveError(_ string: String) {
-            coredataContext.performAndWait {
-                let saveToCoreData = ImportError(context: self.coredataContext)
-                saveToCoreData.date = Date()
-                saveToCoreData.error = string
-                if coredataContext.hasChanges {
-                    try? coredataContext.save()
-                }
-            }
-        }
-
         func backfillGlucose() async {
             backfilling = true
 
@@ -336,3 +336,11 @@ extension NightscoutConfig.StateModel: SettingsObserver {
         units = settingsManager.settings.units
     }
 }
+
+extension NightscoutConfig.StateModel {
+    enum ImportStatus {
+        case running
+        case finished
+        case failed
+    }
+}

+ 104 - 98
FreeAPS/Sources/Modules/NightscoutConfig/View/NightscoutConfigRootView.swift

@@ -50,114 +50,49 @@ extension NightscoutConfig {
         }
 
         var body: some View {
-            Form {
-                Section(
-                    header: Text("Nightscout Integration"),
-                    content: {
-                        NavigationLink("Connect", destination: NightscoutConnectView(state: state))
-                        NavigationLink("Upload", destination: NightscoutUploadView(state: state))
-                        NavigationLink("Fetch & Remote Control", destination: NightscoutFetchView(state: state))
-                    }
-                ).listRowBackground(Color.chart)
+            ZStack {
+                Form {
+                    Section(
+                        header: Text("Nightscout Integration"),
+                        content: {
+                            NavigationLink("Connect", destination: NightscoutConnectView(state: state))
+                            NavigationLink("Upload", destination: NightscoutUploadView(state: state))
+                            NavigationLink("Fetch & Remote Control", destination: NightscoutFetchView(state: state))
+                        }
+                    ).listRowBackground(Color.chart)
 
-                Section {
-                    VStack {
-                        Button {
-                            importAlert = Alert(
-                                title: Text("Import settings?"),
-                                message: Text(
-                                    "\n" +
-                                        NSLocalizedString(
-                                            "This will replace some or all of your current pump settings. Are you sure you want to import profile settings from Nightscout?",
-                                            comment: "Profile Import Alert"
-                                        ) +
-                                        "\n"
-                                ),
-                                primaryButton: .destructive(
-                                    Text("Yes, Import"),
-                                    action: {
-                                        Task {
-                                            await state.importSettings()
-                                            importedHasRun = true
-                                        }
-                                    }
-                                ),
-                                secondaryButton: .cancel()
-                            )
-                            isImportAlertPresented.toggle()
-                        } label: {
-                            Text("Import Settings")
-                                .font(.title3) }
-                            .frame(maxWidth: .infinity, alignment: .center)
-                            .buttonStyle(.bordered)
-                            .disabled(state.url.isEmpty || state.connecting)
-                            .alert(isPresented: $importedHasRun) {
-                                Alert(
-                                    title: Text(
-                                        (fetchedErrors.first?.error ?? "")
-                                            .count < 4 ? "Settings imported" : "Import Error"
-                                    ),
+                    Section {
+                        VStack {
+                            Button {
+                                importAlert = Alert(
+                                    title: Text("Import Therapy Settings?"),
                                     message: Text(
-                                        (fetchedErrors.first?.error ?? "").count < 4 ?
-                                            NSLocalizedString(
-                                                "\nNow please verify all of your new settings thoroughly: \n\n • DIA (Pump settings)\n • Basal Rates\n • Insulin Sensitivities\n • Carb Ratios\n • Target Glucose\n\n in Trio Settings -> Configuration.\n\nBad or invalid profile settings could have disastrous effects.",
-                                                comment: "Imported Profiles Alert"
-                                            ) :
-                                            NSLocalizedString(
-                                                fetchedErrors.first?.error ?? "",
-                                                comment: "Import Error"
-                                            )
+                                        NSLocalizedString(
+                                            "This will replace some or all of your current therapy settings. Are you sure you want to import profile settings from Nightscout?",
+                                            comment: "Nightscout Settings Import Alert"
+                                        )
                                     ),
                                     primaryButton: .destructive(
-                                        Text("OK")
+                                        Text("Yes, Import"),
+                                        action: {
+                                            Task {
+                                                await state.importSettings()
+                                            }
+                                        }
                                     ),
                                     secondaryButton: .cancel()
                                 )
-                            }
-
-                        HStack(alignment: .top) {
-                            Text(
-                                "You can import therapy settings from Nightscout. See hint for more information which settings will be overwritten."
-                            )
-                            .font(.footnote)
-                            .foregroundColor(.secondary)
-                            .lineLimit(nil)
-                            Spacer()
-                            Button(
-                                action: {
-                                    hintLabel = "Import Settings from Nightscout"
-                                    selectedVerboseHint =
-                                        "Importing settings from Nightscout will overwrite the following Trio therapy settings: \n • DIA (Pump settings) \n • Basal Profile \n • Insulin Sensitivities \n • Carb Ratios \n • Target Glucose"
-                                    shouldDisplayHint.toggle()
-                                },
-                                label: {
-                                    HStack {
-                                        Image(systemName: "questionmark.circle")
-                                    }
-                                }
-                            ).buttonStyle(BorderlessButtonStyle())
-                        }.padding(.top)
-                    }.padding(.vertical)
-                }.listRowBackground(Color.chart)
-
-                Section(
-                    content:
-                    {
-                        VStack {
-                            Button {
-                                Task {
-                                    await state.backfillGlucose()
-                                }
+                                isImportAlertPresented = true
                             } label: {
-                                Text("Backfill Glucose")
+                                Text("Import Settings")
                                     .font(.title3) }
                                 .frame(maxWidth: .infinity, alignment: .center)
                                 .buttonStyle(.bordered)
-                                .disabled(state.url.isEmpty || state.connecting || state.backfilling)
+                                .disabled(state.url.isEmpty || state.connecting)
 
                             HStack(alignment: .top) {
                                 Text(
-                                    "You can backfill missing glucose data from Nightscout."
+                                    "You can import therapy settings from Nightscout. See hint for more information which settings will be overwritten."
                                 )
                                 .font(.footnote)
                                 .foregroundColor(.secondary)
@@ -165,9 +100,9 @@ extension NightscoutConfig {
                                 Spacer()
                                 Button(
                                     action: {
-                                        hintLabel = "Backfill Glucose from Nightscout"
+                                        hintLabel = "Import Settings from Nightscout"
                                         selectedVerboseHint =
-                                            "Explanation… limitation… etc."
+                                            "Importing settings from Nightscout will overwrite the following Trio therapy settings: \n • DIA (Pump settings) \n • Basal Profile \n • Insulin Sensitivities \n • Carb Ratios \n • Target Glucose"
                                         shouldDisplayHint.toggle()
                                     },
                                     label: {
@@ -178,9 +113,80 @@ extension NightscoutConfig {
                                 ).buttonStyle(BorderlessButtonStyle())
                             }.padding(.top)
                         }.padding(.vertical)
-                    }
-                ).listRowBackground(Color.chart)
+                    }.listRowBackground(Color.chart)
+
+                    Section(
+                        content:
+                        {
+                            VStack {
+                                Button {
+                                    Task {
+                                        await state.backfillGlucose()
+                                    }
+                                } label: {
+                                    Text("Backfill Glucose")
+                                        .font(.title3) }
+                                    .frame(maxWidth: .infinity, alignment: .center)
+                                    .buttonStyle(.bordered)
+                                    .disabled(state.url.isEmpty || state.connecting || state.backfilling)
+
+                                HStack(alignment: .top) {
+                                    Text(
+                                        "You can backfill missing glucose data from Nightscout."
+                                    )
+                                    .font(.footnote)
+                                    .foregroundColor(.secondary)
+                                    .lineLimit(nil)
+                                    Spacer()
+                                    Button(
+                                        action: {
+                                            hintLabel = "Backfill Glucose from Nightscout"
+                                            selectedVerboseHint =
+                                                "Explanation… limitation… etc."
+                                            shouldDisplayHint.toggle()
+                                        },
+                                        label: {
+                                            HStack {
+                                                Image(systemName: "questionmark.circle")
+                                            }
+                                        }
+                                    ).buttonStyle(BorderlessButtonStyle())
+                                }.padding(.top)
+                            }.padding(.vertical)
+                        }
+                    ).listRowBackground(Color.chart)
+                }.blur(radius: state.importStatus == .running ? 5 : 0)
+
+                if state.importStatus == .running {
+                    CustomProgressView(text: "Importing Profile...")
+                }
+                //            .alert(isPresented: $importedHasRun) {
+                //                Alert(
+                //                    title: Text(
+                //                        (fetchedErrors.first?.error ?? "")
+                //                            .count < 4 ? "Settings imported" : "Import Error"
+                //                    ),
+                //                    message: Text(
+                //                        (fetchedErrors.first?.error ?? "").count < 4 ?
+                //                            NSLocalizedString(
+                //                                "\nNow please verify all of your new settings thoroughly: \n\n • DIA (Pump settings)\n • Basal Rates\n • Insulin Sensitivities\n • Carb Ratios\n • Target Glucose\n\n in Trio Settings -> Configuration.\n\nBad or invalid profile settings could have disastrous effects.",
+                //                                comment: "Imported Profiles Alert"
+                //                            ) :
+                //                            NSLocalizedString(
+                //                                fetchedErrors.first?.error ?? "",
+                //                                comment: "Import Error"
+                //                            )
+                //                    ),
+                //                    primaryButton: .destructive(
+                //                        Text("OK")
+                //                    ),
+                //                    secondaryButton: .cancel()
+                //                )
+                //            }
             }
+            .fullScreenCover(isPresented: $state.isProfileImportPresented, content: {
+                NightscoutImportResultView(resolver: resolver, state: state)
+            })
             .sheet(isPresented: $shouldDisplayHint) {
                 SettingInputHintView(
                     hintDetent: $hintDetent,

+ 111 - 39
FreeAPS/Sources/Modules/NightscoutConfig/View/ProfileImport/NightscoutImportResultView.swift

@@ -1,15 +1,23 @@
 import SwiftUI
+import Swinject
+
+struct NightscoutImportResultView: BaseView {
+    var resolver: any Swinject.Resolver
 
-struct NightscoutImportResultView: View {
     @ObservedObject var state: NightscoutConfig.StateModel
 
     @State private var shouldDisplayHint: Bool = false
-    @State var hintDetent = PresentationDetent.large
-    @State var selectedVerboseHint: String?
-    @State var hintLabel: String?
+    @State private var hintDetent = PresentationDetent.large
+    @State private var selectedVerboseHint: String?
+    @State private var hintLabel: String?
     @State private var decimalPlaceholder: Decimal = 0.0
     @State private var booleanPlaceholder: Bool = false
 
+    @State private var hasVisitedBasalProfileEditor = false
+    @State private var hasVisitedISFEditor = false
+    @State private var hasVisitedCREditor = false
+    @State private var hasVisitedPumpSettingsEditor = false
+
     @Environment(\.colorScheme) var colorScheme
     var color: LinearGradient {
         colorScheme == .dark ? LinearGradient(
@@ -28,49 +36,113 @@ struct NightscoutImportResultView: View {
             )
     }
 
+    private var allViewsVisited: Bool {
+        hasVisitedBasalProfileEditor &&
+            hasVisitedISFEditor &&
+            hasVisitedCREditor &&
+            hasVisitedPumpSettingsEditor
+    }
+
     var body: some View {
         NavigationStack {
-            VStack(alignment: .leading, spacing: 10) {
-                Text(
-                    "Trio has successfully imported your default Nightscout profile and applied it as therapy settings. This has replaced your previous therapy settings."
-                )
+            Form {
+                Section(
+                    header: Text("Imported Nightscout Data"),
+                    content: {
+                        Text(
+                            "Trio has successfully imported your default Nightscout profile and applied it as therapy settings. This has replaced your previous therapy settings."
+                        )
+                        Text("Please review the following settings:").bold()
+                    }
+                ).listRowBackground(Color.chart)
 
-                Text("Please review the following settings")
-                
-                Navigation
+                Section {
+                    NavigationLink(
+                        destination: BasalProfileEditor.RootView(resolver: resolver)
+                            .onDisappear { hasVisitedBasalProfileEditor = true }
+                    ) {
+                        HStack {
+                            Text("Basal Rates")
+                            if hasVisitedBasalProfileEditor {
+                                Image(systemName: "checkmark.circle.fill")
+                                    .imageScale(.large)
+                                    .fontWeight(.bold)
+                                    .foregroundStyle(Color.green)
+                            }
+                        }
+                    }.disabled(hasVisitedBasalProfileEditor)
 
-//                Text("• Basal Rates")
-//                Text("• Insulin Sensitivities")
-//                Text("• Carb Ratios")
-//                Text("• Glucose Targets")
-//                Text("• Duration of Insulin Action (DIA)")
+                    NavigationLink(
+                        destination: ISFEditor.RootView(resolver: resolver)
+                            .onDisappear { hasVisitedISFEditor = true }
+                    ) {
+                        HStack {
+                            Text("Insulin Sensitivities")
+                            if hasVisitedISFEditor {
+                                Image(systemName: "checkmark.circle.fill")
+                                    .imageScale(.large)
+                                    .fontWeight(.bold)
+                                    .foregroundStyle(Color.green)
+                            }
+                        }
+                    }.disabled(hasVisitedISFEditor)
 
-                Spacer()
+                    NavigationLink(
+                        destination: CarbRatioEditor.RootView(resolver: resolver)
+                            .onDisappear { hasVisitedCREditor = true }
+                    ) {
+                        HStack {
+                            Text("Carb Ratios")
+                            if hasVisitedCREditor {
+                                Image(systemName: "checkmark.circle.fill")
+                                    .imageScale(.large)
+                                    .fontWeight(.bold)
+                                    .foregroundStyle(Color.green)
+                            }
+                        }
+                    }.disabled(hasVisitedCREditor)
 
-                Button {
-                    Task {
-                        await state.importSettings()
-                    }
-                } label: {
-                    Text("Start Import")
-                        .font(.title3)
-                        .frame(maxWidth: .infinity, alignment: .center)
-                }
-                .buttonStyle(.borderedProminent)
-                .disabled(state.url.isEmpty || state.connecting || state.backfilling)
+                    NavigationLink(
+                        destination: PumpSettingsEditor.RootView(resolver: resolver)
+                            .onDisappear { hasVisitedPumpSettingsEditor = true }
+                    ) {
+                        HStack {
+                            Text("Duration of Insulin Action (DIA)")
+                            if hasVisitedPumpSettingsEditor {
+                                Image(systemName: "checkmark.circle.fill")
+                                    .imageScale(.large)
+                                    .fontWeight(.bold)
+                                    .foregroundStyle(Color.green)
+                            }
+                        }
+                    }.disabled(hasVisitedPumpSettingsEditor)
+                }.listRowBackground(Color.chart)
 
-                Spacer()
-            }.padding()
-                .toolbar(content: {
-                    ToolbarItem(placement: .topBarLeading) {
-                        Button(action: { state.isProfileImportPresented = false }, label: {
-                            Text("Cancel")
-                        })
+                Section {
+                    HStack {
+                        Button {
+                            state.isProfileImportPresented = false
+                        } label: {
+                            Text("Finish").font(.title3)
+                        }
+                        .disabled(!allViewsVisited)
+                        .frame(maxWidth: .infinity, alignment: .center)
+                        .tint(.white)
                     }
-                })
-                .navigationTitle("Nightscout Import")
-                .navigationBarTitleDisplayMode(.automatic)
-                .scrollContentBackground(.hidden).background(color)
+                }.listRowBackground(allViewsVisited ? Color(.systemBlue) : Color(.systemGray4))
+            }
+            .toolbar(content: {
+                ToolbarItem(placement: .topBarLeading) {
+                    Button(action: { state.isProfileImportPresented = false }, label: {
+                        Text("Cancel")
+                    })
+                }
+            })
+            .navigationTitle("Review Import")
+            .navigationBarTitleDisplayMode(.large)
+            .scrollContentBackground(.hidden).background(color)
+            .interactiveDismissDisabled(true)
+            .screenNavigation(self)
         }
     }
 }