From affdd3954229156a64fe2290b22d830ae20e2ecc Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Wed, 14 Aug 2024 03:10:12 -0700 Subject: [PATCH] Add onFirstAppear extension --- Meshtastic.xcodeproj/project.pbxproj | 4 +++ Meshtastic/Extensions/View.swift | 30 +++++++++++++++++++ .../Views/Settings/Config/ConfigHeader.swift | 4 +-- 3 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 Meshtastic/Extensions/View.swift diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index f5108298..c891cca9 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -93,6 +93,7 @@ DD6193772862F90F00E59241 /* CannedMessagesConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6193762862F90F00E59241 /* CannedMessagesConfig.swift */; }; DD6193792863875F00E59241 /* SerialConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6193782863875F00E59241 /* SerialConfig.swift */; }; DD6F65722C6AB8EC0053C113 /* SecureInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6F65712C6AB8EC0053C113 /* SecureInput.swift */; }; + DD6F65742C6CB80A0053C113 /* View.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6F65732C6CB80A0053C113 /* View.swift */; }; DD73FD1128750779000852D6 /* PositionLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD73FD1028750779000852D6 /* PositionLog.swift */; }; DD769E0328D18BF1001A3F05 /* DeviceMetricsLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD769E0228D18BF0001A3F05 /* DeviceMetricsLog.swift */; }; DD77093B2AA1ABB8007A8BF0 /* BluetoothTips.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD77093A2AA1ABB8007A8BF0 /* BluetoothTips.swift */; }; @@ -347,6 +348,7 @@ DD6193782863875F00E59241 /* SerialConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SerialConfig.swift; sourceTree = ""; }; DD68BAE72C417A74004C01A0 /* MeshtasticDataModelV 40.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModelV 40.xcdatamodel"; sourceTree = ""; }; DD6F65712C6AB8EC0053C113 /* SecureInput.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureInput.swift; sourceTree = ""; }; + DD6F65732C6CB80A0053C113 /* View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = View.swift; sourceTree = ""; }; DD73FD1028750779000852D6 /* PositionLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PositionLog.swift; sourceTree = ""; }; DD769E0228D18BF0001A3F05 /* DeviceMetricsLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceMetricsLog.swift; sourceTree = ""; }; DD77093A2AA1ABB8007A8BF0 /* BluetoothTips.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BluetoothTips.swift; sourceTree = ""; }; @@ -1001,6 +1003,7 @@ DDD5BB172C2F9C36007E03CA /* OSLogEntryLog.swift */, DDF45C362BC46A5A005ED5F2 /* TimeZone.swift */, DDD5BB0C2C285F00007E03CA /* Logger.swift */, + DD6F65732C6CB80A0053C113 /* View.swift */, ); path = Extensions; sourceTree = ""; @@ -1374,6 +1377,7 @@ DDB6ABE228B13FB500384BA1 /* PositionConfigEnums.swift in Sources */, DD994B69295F88B60013760A /* IntervalEnums.swift in Sources */, DDDCD5702BB26F5C00BE6B60 /* NodeListFilter.swift in Sources */, + DD6F65742C6CB80A0053C113 /* View.swift in Sources */, DD1933762B0835D500771CD5 /* PositionAltitudeChart.swift in Sources */, DD415828285859C4009B0E59 /* TelemetryConfig.swift in Sources */, DDB6CCFB2AAF805100945AF6 /* NodeMapSwiftUI.swift in Sources */, diff --git a/Meshtastic/Extensions/View.swift b/Meshtastic/Extensions/View.swift new file mode 100644 index 00000000..7349f36d --- /dev/null +++ b/Meshtastic/Extensions/View.swift @@ -0,0 +1,30 @@ +// +// View.swift +// Meshtastic +// +// Copyright(c) Garth Vander Houwen 8/14/24. +// + +import SwiftUI + +public extension View { + func onFirstAppear(_ action: @escaping () -> ()) -> some View { + modifier(FirstAppear(action: action)) + } +} + +private struct FirstAppear: ViewModifier { + let action: () -> () + + // Use this to only fire your block one time + @State private var hasAppeared = false + + func body(content: Content) -> some View { + // And then, track it here + content.onAppear { + guard !hasAppeared else { return } + hasAppeared = true + action() + } + } +} diff --git a/Meshtastic/Views/Settings/Config/ConfigHeader.swift b/Meshtastic/Views/Settings/Config/ConfigHeader.swift index cef7f98c..3f59a01a 100644 --- a/Meshtastic/Views/Settings/Config/ConfigHeader.swift +++ b/Meshtastic/Views/Settings/Config/ConfigHeader.swift @@ -23,12 +23,12 @@ struct ConfigHeader: View { .foregroundColor(.orange) } else { Text("Remote administration for: \(node?.user?.longName ?? "Unknown")") - .onAppear(perform: onAppear) + .onFirstAppear(onAppear) .font(.title3) } } else if node != nil && node?.num ?? 0 == bleManager.connectedPeripheral?.num ?? -1 { Text("Configuration for: \(node?.user?.longName ?? "Unknown")") - .onAppear(perform: onAppear) + .onFirstAppear(onAppear) } else { Text("Please connect to a radio to configure settings.") .font(.callout)