mirror of
https://github.com/jkcoxson/LocalDevVPN.git
synced 2026-03-02 06:26:16 +01:00
add Setup
This commit is contained in:
Binary file not shown.
@@ -3,4 +3,52 @@
|
|||||||
uuid = "E9FBFA8B-D737-454C-9876-DFFB697E8C2D"
|
uuid = "E9FBFA8B-D737-454C-9876-DFFB697E8C2D"
|
||||||
type = "1"
|
type = "1"
|
||||||
version = "2.0">
|
version = "2.0">
|
||||||
|
<Breakpoints>
|
||||||
|
<BreakpointProxy
|
||||||
|
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||||
|
<BreakpointContent
|
||||||
|
uuid = "EBA30226-5BCA-4729-8062-C41AF6A700D2"
|
||||||
|
shouldBeEnabled = "Yes"
|
||||||
|
ignoreCount = "0"
|
||||||
|
continueAfterRunningActions = "No"
|
||||||
|
filePath = "StosVPN/ContentView.swift"
|
||||||
|
startingColumnNumber = "9223372036854775807"
|
||||||
|
endingColumnNumber = "9223372036854775807"
|
||||||
|
startingLineNumber = "612"
|
||||||
|
endingLineNumber = "612"
|
||||||
|
landmarkName = "body"
|
||||||
|
landmarkType = "24">
|
||||||
|
<Locations>
|
||||||
|
<Location
|
||||||
|
uuid = "EBA30226-5BCA-4729-8062-C41AF6A700D2 - 41082001a4351f4b"
|
||||||
|
shouldBeEnabled = "Yes"
|
||||||
|
ignoreCount = "0"
|
||||||
|
continueAfterRunningActions = "No"
|
||||||
|
symbolName = "closure #3 () -> SwiftUI.TupleView<(SwiftUI.HStack<SwiftUI.TupleView<(SwiftUI.Text, SwiftUI.Spacer, SwiftUI.Text)>>, SwiftUI.Button<SwiftUI.Text>, SwiftUI.NavigationLink<SwiftUI.Text, StosVPN.HelpView>)> in closure #1 () -> SwiftUI.TupleView<(SwiftUI.Section<SwiftUI.Text, SwiftUI.TupleView<(SwiftUI.Toggle<SwiftUI.Text>, SwiftUI.NavigationLink<SwiftUI.Label<SwiftUI.Text, SwiftUI.Image>, StosVPN.ConnectionLogView>)>, SwiftUI.EmptyView>, SwiftUI.Section<SwiftUI.Text, SwiftUI.TupleView<(SwiftUI.HStack<SwiftUI.TupleView<(SwiftUI.Text, SwiftUI.Spacer, <<opaque return type of SwiftUI.View.keyboardType(__C.UIKeyboardType) -> some>>.0)>>, SwiftUI.HStack<SwiftUI.TupleView<(SwiftUI.Text, SwiftUI.Spacer, <<opaque return type of SwiftUI.View.keyboardType(__C.UIKeyboardType) -> some>>.0)>>, SwiftUI.HStack<SwiftUI.TupleView<(SwiftUI.Text, SwiftUI.Spacer, <<opaque return type of SwiftUI.View.keyboardType(__C.UIKeyboardType) -> some>>.0)>>)>, SwiftUI.EmptyView>, SwiftUI.Section<SwiftUI.Text, SwiftUI.TupleView<(SwiftUI.HStack<SwiftUI.TupleView<(SwiftUI.Text, SwiftUI.Spacer, SwiftUI.Text)>>, SwiftUI.Button<SwiftUI.Text>, SwiftUI.NavigationLink<SwiftUI.Text, StosVPN.HelpView>)>, SwiftUI.EmptyView>)> in closure #1 () -> <<opaque return type of SwiftUI.View.toolbar<τ_0_0 where τ_1_0: SwiftUI.ToolbarContent>(content: () -> τ_1_0) -> some>>.0 in StosVPN.SettingsView.body.getter : some"
|
||||||
|
moduleName = "StosVPN.debug.dylib"
|
||||||
|
usesParentBreakpointCondition = "Yes"
|
||||||
|
urlString = "file:///Users/stossy11/Developer/StosVPN/StosVPN/ContentView.swift"
|
||||||
|
startingColumnNumber = "9223372036854775807"
|
||||||
|
endingColumnNumber = "9223372036854775807"
|
||||||
|
startingLineNumber = "613"
|
||||||
|
endingLineNumber = "613">
|
||||||
|
</Location>
|
||||||
|
<Location
|
||||||
|
uuid = "EBA30226-5BCA-4729-8062-C41AF6A700D2 - e2b02a9d9e8fd01"
|
||||||
|
shouldBeEnabled = "Yes"
|
||||||
|
ignoreCount = "0"
|
||||||
|
continueAfterRunningActions = "No"
|
||||||
|
symbolName = "closure #2 @Swift.MainActor () -> () in closure #3 () -> SwiftUI.TupleView<(SwiftUI.HStack<SwiftUI.TupleView<(SwiftUI.Text, SwiftUI.Spacer, SwiftUI.Text)>>, SwiftUI.Button<SwiftUI.Text>, SwiftUI.NavigationLink<SwiftUI.Text, StosVPN.HelpView>)> in closure #1 () -> SwiftUI.TupleView<(SwiftUI.Section<SwiftUI.Text, SwiftUI.TupleView<(SwiftUI.Toggle<SwiftUI.Text>, SwiftUI.NavigationLink<SwiftUI.Label<SwiftUI.Text, SwiftUI.Image>, StosVPN.ConnectionLogView>)>, SwiftUI.EmptyView>, SwiftUI.Section<SwiftUI.Text, SwiftUI.TupleView<(SwiftUI.HStack<SwiftUI.TupleView<(SwiftUI.Text, SwiftUI.Spacer, <<opaque return type of SwiftUI.View.keyboardType(__C.UIKeyboardType) -> some>>.0)>>, SwiftUI.HStack<SwiftUI.TupleView<(SwiftUI.Text, SwiftUI.Spacer, <<opaque return type of SwiftUI.View.keyboardType(__C.UIKeyboardType) -> some>>.0)>>, SwiftUI.HStack<SwiftUI.TupleView<(SwiftUI.Text, SwiftUI.Spacer, <<opaque return type of SwiftUI.View.keyboardType(__C.UIKeyboardType) -> some>>.0)>>)>, SwiftUI.EmptyView>, SwiftUI.Section<SwiftUI.Text, SwiftUI.TupleView<(SwiftUI.HStack<SwiftUI.TupleView<(SwiftUI.Text, SwiftUI.Spacer, SwiftUI.Text)>>, SwiftUI.Button<SwiftUI.Text>, SwiftUI.NavigationLink<SwiftUI.Text, StosVPN.HelpView>)>, SwiftUI.EmptyView>)> in closure #1 () -> <<opaque return type of SwiftUI.View.toolbar<τ_0_0 where τ_1_0: SwiftUI.ToolbarContent>(content: () -> τ_1_0) -> some>>.0 in StosVPN.SettingsView.body.getter : some"
|
||||||
|
moduleName = "StosVPN.debug.dylib"
|
||||||
|
usesParentBreakpointCondition = "Yes"
|
||||||
|
urlString = "file:///Users/stossy11/Developer/StosVPN/StosVPN/ContentView.swift"
|
||||||
|
startingColumnNumber = "9223372036854775807"
|
||||||
|
endingColumnNumber = "9223372036854775807"
|
||||||
|
startingLineNumber = "614"
|
||||||
|
endingLineNumber = "614">
|
||||||
|
</Location>
|
||||||
|
</Locations>
|
||||||
|
</BreakpointContent>
|
||||||
|
</BreakpointProxy>
|
||||||
|
</Breakpoints>
|
||||||
</Bucket>
|
</Bucket>
|
||||||
|
|||||||
@@ -297,6 +297,7 @@ struct ContentView: View {
|
|||||||
@State private var showSettings = false
|
@State private var showSettings = false
|
||||||
@State var tunnel = false
|
@State var tunnel = false
|
||||||
@AppStorage("autoConnect") private var autoConnect = false
|
@AppStorage("autoConnect") private var autoConnect = false
|
||||||
|
@AppStorage("hasNotCompletedSetup") private var hasNotCompletedSetup = true
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationStack {
|
NavigationStack {
|
||||||
@@ -335,10 +336,12 @@ struct ContentView: View {
|
|||||||
tunnelManager.startVPN()
|
tunnelManager.startVPN()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.sheet(isPresented: $showSettings) {
|
.sheet(isPresented: $showSettings) {
|
||||||
SettingsView()
|
SettingsView()
|
||||||
}
|
}
|
||||||
|
.sheet(isPresented: $hasNotCompletedSetup) {
|
||||||
|
SetupView()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -607,8 +610,8 @@ struct SettingsView: View {
|
|||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
}
|
}
|
||||||
|
|
||||||
NavigationLink(destination: PrivacyPolicyView()) {
|
Button("Privacy Policy") {
|
||||||
Text("Privacy Policy")
|
UIApplication.shared.open(URL(string: "https://github.com/stossy11/PrivacyPolicy/blob/main/PrivacyPolicy.md")!)
|
||||||
}
|
}
|
||||||
|
|
||||||
NavigationLink(destination: HelpView()) {
|
NavigationLink(destination: HelpView()) {
|
||||||
@@ -727,6 +730,151 @@ struct HelpView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct SetupView: View {
|
||||||
|
@Environment(\.dismiss) private var dismiss
|
||||||
|
@AppStorage("hasNotCompletedSetup") private var hasNotCompletedSetup = true
|
||||||
|
@State private var currentPage = 0
|
||||||
|
|
||||||
|
let pages = [
|
||||||
|
SetupPage(
|
||||||
|
title: "Welcome to StosVPN",
|
||||||
|
description: "A simple local network tunnel for everyone",
|
||||||
|
imageName: "checkmark.shield.fill",
|
||||||
|
details: "StosVPN creates a local network interface on your device that anyone can use for development, testing, and accessing local servers."
|
||||||
|
),
|
||||||
|
SetupPage(
|
||||||
|
title: "Why Use StosVPN?",
|
||||||
|
description: "Perfect for developers and everyday users",
|
||||||
|
imageName: "person.2.fill",
|
||||||
|
details: "• Access local web servers and development environments\n• Test applications that require specific network configurations\n• Connect to local network services without complex setup\n• Create isolated network environments for testing"
|
||||||
|
),
|
||||||
|
SetupPage(
|
||||||
|
title: "Easy to Use",
|
||||||
|
description: "Just one tap to connect",
|
||||||
|
imageName: "hand.tap.fill",
|
||||||
|
details: "StosVPN is designed to be simple and straightforward. Just tap the connect button to establish a local network tunnel with pre-configured settings that work for most users."
|
||||||
|
),
|
||||||
|
SetupPage(
|
||||||
|
title: "Privacy Focused",
|
||||||
|
description: "Your data stays on your device",
|
||||||
|
imageName: "lock.shield.fill",
|
||||||
|
details: "StosVPN creates a local tunnel that doesn't route traffic through external servers. All network traffic remains on your device, ensuring your privacy and security."
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
NavigationStack {
|
||||||
|
VStack {
|
||||||
|
TabView(selection: $currentPage) {
|
||||||
|
ForEach(0..<pages.count, id: \.self) { index in
|
||||||
|
SetupPageView(page: pages[index])
|
||||||
|
.tag(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .always))
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
if currentPage == pages.count - 1 {
|
||||||
|
Button {
|
||||||
|
hasNotCompletedSetup = false
|
||||||
|
dismiss()
|
||||||
|
} label: {
|
||||||
|
Text("Get Started")
|
||||||
|
.font(.headline)
|
||||||
|
.fontWeight(.semibold)
|
||||||
|
.frame(height: 50)
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
.background(
|
||||||
|
LinearGradient(
|
||||||
|
gradient: Gradient(colors: [Color.blue.opacity(0.8), Color.blue]),
|
||||||
|
startPoint: .leading,
|
||||||
|
endPoint: .trailing
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.foregroundColor(.white)
|
||||||
|
.cornerRadius(10)
|
||||||
|
.padding(.horizontal)
|
||||||
|
}
|
||||||
|
.padding(.bottom)
|
||||||
|
} else {
|
||||||
|
Button {
|
||||||
|
withAnimation {
|
||||||
|
currentPage += 1
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
Text("Next")
|
||||||
|
.font(.headline)
|
||||||
|
.fontWeight(.semibold)
|
||||||
|
.frame(height: 50)
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
.background(
|
||||||
|
LinearGradient(
|
||||||
|
gradient: Gradient(colors: [Color.blue.opacity(0.8), Color.blue]),
|
||||||
|
startPoint: .leading,
|
||||||
|
endPoint: .trailing
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.foregroundColor(.white)
|
||||||
|
.cornerRadius(10)
|
||||||
|
.padding(.horizontal)
|
||||||
|
}
|
||||||
|
.padding(.bottom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationTitle("Setup")
|
||||||
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
.toolbar {
|
||||||
|
ToolbarItem(placement: .topBarTrailing) {
|
||||||
|
Button("Skip") {
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SetupPage {
|
||||||
|
let title: String
|
||||||
|
let description: String
|
||||||
|
let imageName: String
|
||||||
|
let details: String
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SetupPageView: View {
|
||||||
|
let page: SetupPage
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack(spacing: 30) {
|
||||||
|
Image(systemName: page.imageName)
|
||||||
|
.font(.system(size: 80))
|
||||||
|
.foregroundColor(.blue)
|
||||||
|
.padding(.top, 50)
|
||||||
|
|
||||||
|
Text(page.title)
|
||||||
|
.font(.title)
|
||||||
|
.fontWeight(.bold)
|
||||||
|
.multilineTextAlignment(.center)
|
||||||
|
|
||||||
|
Text(page.description)
|
||||||
|
.font(.headline)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
.multilineTextAlignment(.center)
|
||||||
|
|
||||||
|
ScrollView {
|
||||||
|
Text(page.details)
|
||||||
|
.font(.body)
|
||||||
|
.multilineTextAlignment(.leading)
|
||||||
|
.padding(.horizontal)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.padding()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#Preview {
|
#Preview {
|
||||||
ContentView()
|
ContentView()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user