diff --git a/Localizable.xcstrings b/Localizable.xcstrings index a5670f11..4fff08b0 100644 --- a/Localizable.xcstrings +++ b/Localizable.xcstrings @@ -12692,6 +12692,9 @@ } } } + }, + "Enable Location Sharing" : { + }, "Enable Notifications" : { "localizations" : { @@ -20848,9 +20851,6 @@ } } } - }, - "Meshtastic uses your phone's location to enable a number of features. You can update your location permissions at any time from Settings > App Settings > Open Settings." : { - }, "Meshtastic® Copyright Meshtastic LLC" : { "localizations" : { @@ -33453,6 +33453,9 @@ } } } + }, + "Share Location" : { + }, "Share QR Code" : { "localizations" : { diff --git a/Meshtastic/Views/Onboarding/DeviceOnboarding.swift b/Meshtastic/Views/Onboarding/DeviceOnboarding.swift index e9ce4593..c1a73ebc 100644 --- a/Meshtastic/Views/Onboarding/DeviceOnboarding.swift +++ b/Meshtastic/Views/Onboarding/DeviceOnboarding.swift @@ -13,9 +13,9 @@ struct DeviceOnboarding: View { @EnvironmentObject var bleManager: BLEManager @State var navigationPath: [SetupGuide] = [] @State var locationStatus = LocationsHandler.shared.manager.authorizationStatus - + @AppStorage("provideLocation") private var provideLocation: Bool = false + @AppStorage("provideLocationInterval") private var provideLocationInterval: Int = 30 @Environment(\.dismiss) var dismiss - /// The Title View var title: some View { VStack { @@ -41,20 +41,18 @@ struct DeviceOnboarding: View { VStack(alignment: .leading, spacing: 16) { makeRow( icon: "antenna.radiowaves.left.and.right", - title: "Stay Connected Anywhere", - subtitle: "Communicate off-the-grid with your friends and community without cell service." + title: "Stay Connected Anywhere".localized, + subtitle: "Communicate off-the-grid with your friends and community without cell service.".localized ) - makeRow( icon: "point.3.connected.trianglepath.dotted", - title: "Create Your Own Networks", - subtitle: "Easily set up private mesh networks for secure and reliable communication in remote areas." + title: "Create Your Own Networks".localized, + subtitle: "Easily set up private mesh networks for secure and reliable communication in remote areas.".localized ) - makeRow( icon: "location", - title: "Track and Share Locations", - subtitle: "Share your location in real-time and keep your group coordinated with integrated GPS features." + title: "Track and Share Locations".localized, + subtitle: "Share your location in real-time and keep your group coordinated with integrated GPS features.".localized ) } .padding() @@ -78,8 +76,8 @@ struct DeviceOnboarding: View { } var notificationView: some View { - ScrollView(.vertical) { - VStack { + VStack { + ScrollView(.vertical) { VStack { Text("App Notifications") .font(.largeTitle.bold()) @@ -94,18 +92,18 @@ struct DeviceOnboarding: View { .fixedSize(horizontal: false, vertical: true) makeRow( icon: "message", - title: "Incoming Messages", - subtitle: "Meshtastic notifications for channel messages and direct messages" + title: "Incoming Messages".localized, + subtitle: "Notifications for channel and direct messages.".localized ) makeRow( icon: "flipphone", - title: "New Nodes", - subtitle: "Allow Meshtastic to send notifications for messages, newly discovered nodes and low battery alerts for the connected device." + title: "New Nodes".localized, + subtitle: "Notifications for newly discovered nodes.".localized ) makeRow( icon: "battery.25percent", - title: "Low Battery", - subtitle: "Allow Meshtastic to send notifications for messages, newly discovered nodes and low battery alerts for the connected device." + title: "Low Battery".localized, + subtitle: "Notifications for low battery alerts for the connected device.".localized ) Text("Critical Alerts") .font(.title2.bold()) @@ -113,31 +111,31 @@ struct DeviceOnboarding: View { .fixedSize(horizontal: false, vertical: true) makeRow( icon: "exclamationmark.triangle.fill", - subtitle: "Select packets sent as critical will ignore the mute switch and Do Not Disturb settings in the OS notification center." + subtitle: "Select packets sent as critical will ignore the mute switch and Do Not Disturb settings in the OS notification center.".localized ) } .padding() - Spacer() - Button { - Task { - await requestNotificationsPermissions() - await goToNextStep(after: .notifications) - } - } label: { - Text("Configure notification permissions") - .frame(maxWidth: .infinity) - } - .buttonBorderShape(.capsule) - .controlSize(.large) - .padding() - .buttonStyle(.borderedProminent) } + Spacer() + Button { + Task { + await requestNotificationsPermissions() + await goToNextStep(after: .notifications) + } + } label: { + Text("Configure notification permissions") + .frame(maxWidth: .infinity) + } + .buttonBorderShape(.capsule) + .controlSize(.large) + .padding() + .buttonStyle(.borderedProminent) } } var locationView: some View { - ScrollView(.vertical) { - VStack { + VStack { + ScrollView(.vertical) { VStack { Text("Phone Location") .font(.largeTitle.bold()) @@ -145,47 +143,62 @@ struct DeviceOnboarding: View { .fixedSize(horizontal: false, vertical: true) } VStack(alignment: .leading, spacing: 16) { - Text("Meshtastic uses your phone's location to enable a number of features. You can update your location permissions at any time from Settings > App Settings > Open Settings.") + Text(createLocationString()) .font(.body.bold()) .multilineTextAlignment(.center) .fixedSize(horizontal: false, vertical: true) makeRow( icon: "location", - title: "Share Location", - subtitle: "Use your phone GPS to send locations to your node to instead of using a hardware GPS on your node." + title: "Share Location".localized, + subtitle: "Use your phone GPS to send locations to your node to instead of using a hardware GPS on your node.".localized ) + Toggle(isOn: $provideLocation ) { + Label { + Text("Enable Location Sharing") + } icon: { + Image(systemName: "location.circle") + } + } + .fixedSize() + .scaleEffect(0.85) + .padding(.leading, 52) + .tint(.accentColor) + .onChange(of: provideLocation) { + UserDefaults.provideLocationInterval = 30 + UserDefaults.enableSmartPosition = true + } makeRow( icon: "lines.measurement.horizontal", - title: "Distance Measurements", - subtitle: "Used to display the distance between your phone and other Meshtastic nodes where positions are available." + title: "Distance Measurements".localized, + subtitle: "Display the distance between your phone and other Meshtastic nodes with positions.".localized ) makeRow( icon: "line.3.horizontal.decrease.circle", - title: "Distance Filters", - subtitle: "Filter the node list and mesh map based on proximity to your phone." + title: "Distance Filters".localized, + subtitle: "Filter the node list and mesh map based on proximity to your phone.".localized ) makeRow( icon: "mappin", title: "Mesh Map Location", - subtitle: "Enables the blue location dot for your phone in the mesh map." + subtitle: "Enables the blue location dot for your phone in the mesh map.".localized ) } .padding() - Spacer() - Button { - Task { - await requestLocationPermissions() - } - } label: { - Text("Configure Location Permissions") - .frame(maxWidth: .infinity) - } - .padding() - .buttonBorderShape(.capsule) - .controlSize(.large) - .padding() - .buttonStyle(.borderedProminent) } + Spacer() + Button { + Task { + await requestLocationPermissions() + } + } label: { + Text("Configure Location Permissions") + .frame(maxWidth: .infinity) + } + .padding() + .buttonBorderShape(.capsule) + .controlSize(.large) + .padding() + .buttonStyle(.borderedProminent) } } @@ -216,15 +229,14 @@ struct DeviceOnboarding: View { .symbolRenderingMode(.multicolor) .font(.subheadline) .aspectRatio(contentMode: .fit) - .padding() - .frame(width: 72, height: 72) - + .padding(.horizontal) + .padding(.vertical, 8) + .frame(width: 72, height: 60) VStack(alignment: .leading) { Text(title) .font(.subheadline.weight(.semibold)) .foregroundColor(.primary) .fixedSize(horizontal: false, vertical: true) - Text(subtitle) .font(.subheadline) .foregroundColor(.secondary) @@ -232,7 +244,6 @@ struct DeviceOnboarding: View { }.multilineTextAlignment(.leading) }.accessibilityElement(children: .combine) } - // MARK: Navigation func goToNextStep(after step: SetupGuide?) async { switch step { @@ -259,6 +270,16 @@ struct DeviceOnboarding: View { } } + // MARK: Formatting + func createLocationString() -> AttributedString { + var fullText = AttributedString("Meshtastic uses your phone's location to enable a number of features. You can update your location permissions at any time from settings.") + if let range = fullText.range(of: "settings") { + fullText[range].link = URL(string: UIApplication.openSettingsURLString)! + fullText[range].foregroundColor = .blue + } + return fullText + } + // MARK: Permission Checks func requestNotificationsPermissions() async { let center = UNUserNotificationCenter.current()