diff --git a/TypeaheadAI.xcodeproj/project.pbxproj b/TypeaheadAI.xcodeproj/project.pbxproj index 29ef15f..0d5f400 100644 --- a/TypeaheadAI.xcodeproj/project.pbxproj +++ b/TypeaheadAI.xcodeproj/project.pbxproj @@ -50,6 +50,7 @@ 2B4BDB8B2ACC281E00E55D78 /* IntentManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B4BDB8A2ACC281E00E55D78 /* IntentManager.swift */; }; 2B520D812AC7FD4800310426 /* AuxiliaryMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B520D802AC7FD4800310426 /* AuxiliaryMenuView.swift */; }; 2B520D852AC82EA100310426 /* AccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B520D842AC82EA100310426 /* AccountView.swift */; }; + 2B5FFA632B211F7E001F9B74 /* LoggedInOnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B5FFA622B211F7E001F9B74 /* LoggedInOnboardingView.swift */; }; 2B7BBAC02AF2B4D300E4CE1F /* QuickActionDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B7BBABF2AF2B4D300E4CE1F /* QuickActionDetails.swift */; }; 2B7BBAC22AF3347100E4CE1F /* NewQuickActionForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B7BBAC12AF3347100E4CE1F /* NewQuickActionForm.swift */; }; 2B7D357C2B00B48600E85AEF /* LoggedOutOnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B7D357B2B00B48600E85AEF /* LoggedOutOnboardingView.swift */; }; @@ -168,6 +169,7 @@ 2B4BDB8A2ACC281E00E55D78 /* IntentManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntentManager.swift; sourceTree = ""; }; 2B520D802AC7FD4800310426 /* AuxiliaryMenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuxiliaryMenuView.swift; sourceTree = ""; }; 2B520D842AC82EA100310426 /* AccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountView.swift; sourceTree = ""; }; + 2B5FFA622B211F7E001F9B74 /* LoggedInOnboardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggedInOnboardingView.swift; sourceTree = ""; }; 2B7BBABF2AF2B4D300E4CE1F /* QuickActionDetails.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickActionDetails.swift; sourceTree = ""; }; 2B7BBAC12AF3347100E4CE1F /* NewQuickActionForm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewQuickActionForm.swift; sourceTree = ""; }; 2B7D357B2B00B48600E85AEF /* LoggedOutOnboardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggedOutOnboardingView.swift; sourceTree = ""; }; @@ -315,6 +317,7 @@ children = ( 2B44FA972ABA783F00C6B542 /* OnboardingView.swift */, 2B7D357B2B00B48600E85AEF /* LoggedOutOnboardingView.swift */, + 2B5FFA622B211F7E001F9B74 /* LoggedInOnboardingView.swift */, 2B7D35832B01B14100E85AEF /* IntroOnboardingView.swift */, 2BAA97942B042AD400EC6A63 /* PermissionsOnboardingView.swift */, 2B8CD49A2B076AE6003E0589 /* ActivateOnboardingView.swift */, @@ -768,6 +771,7 @@ 2BA7F0A92A9ABBE2003D38BA /* ClientManager.swift in Sources */, 2B44FA982ABA783F00C6B542 /* OnboardingView.swift in Sources */, 2B8CD4992B06DD36003E0589 /* FunctionManager.swift in Sources */, + 2B5FFA632B211F7E001F9B74 /* LoggedInOnboardingView.swift in Sources */, 2BE0EC272AA17F9100E47C52 /* MouseClickMonitor.swift in Sources */, 2BE0EC222AA0956C00E47C52 /* ModalView.swift in Sources */, 2B92BDB92AA3A2DD00E65CFA /* CustomModalWindow.swift in Sources */, diff --git a/TypeaheadAI/Supabase/SupabaseManager.swift b/TypeaheadAI/Supabase/SupabaseManager.swift index 365bbbb..798e447 100644 --- a/TypeaheadAI/Supabase/SupabaseManager.swift +++ b/TypeaheadAI/Supabase/SupabaseManager.swift @@ -22,7 +22,8 @@ class SupabaseManager: ObservableObject { category: "SupabaseManager" ) - @Published var uuid: String? + @Published var uuid: String? // Deprecate? + @Published var user: TypeaheadUser? init() { Task { @@ -35,6 +36,7 @@ class SupabaseManager: ObservableObject { do { let session = try await client.auth.session self.uuid = session.user.id.uuidString + self.user = TypeaheadUser(uuid: session.user.id, productId: 0) } catch { logger.info("Not signed-in") } @@ -46,7 +48,8 @@ class SupabaseManager: ObservableObject { let session = try await client.auth.session let user = session.user - uuid = user.id.uuidString + self.uuid = user.id.uuidString + self.user = TypeaheadUser(uuid: user.id, productId: 0) } @MainActor @@ -54,6 +57,7 @@ class SupabaseManager: ObservableObject { let response = try await client.auth.signIn(email: email, password: password) let user = response.user uuid = user.id.uuidString + self.user = TypeaheadUser(uuid: user.id, productId: 0) let _ = try await client.auth.session } @@ -70,6 +74,7 @@ class SupabaseManager: ObservableObject { @MainActor func signout() async throws { uuid = nil + user = nil try await client.auth.signOut() } @@ -78,5 +83,28 @@ class SupabaseManager: ObservableObject { let session = try await client.auth.session(from: from) let user = session.user self.uuid = user.id.uuidString + self.user = TypeaheadUser(uuid: user.id, productId: 0) + } + + @MainActor + func getUser() async throws -> TypeaheadUser? { + guard let uuidString = uuid, let id = UUID(uuidString: uuidString) else { + return nil + } + + return TypeaheadUser(uuid: id, productId: 0) + } + + /// TODO: Need a proper mapping of product -> payment links. Maybe we can provide this from the backend? + func openStripeCheckout() async throws { + if user?.productId == 0, let uuidString = user?.uuid.uuidString { + let url = URL(string: "https://stripe.com?client-reference-id=\(uuidString)")! + NSWorkspace.shared.open(url) + } } } + +struct TypeaheadUser { + let uuid: UUID + let productId: Int +} diff --git a/TypeaheadAI/Views/Onboarding/LoggedInOnboardingView.swift b/TypeaheadAI/Views/Onboarding/LoggedInOnboardingView.swift new file mode 100644 index 0000000..340c301 --- /dev/null +++ b/TypeaheadAI/Views/Onboarding/LoggedInOnboardingView.swift @@ -0,0 +1,55 @@ +// +// LoggedInOnboardingView.swift +// TypeaheadAI +// +// Created by Jeff Hara on 12/6/23. +// + +import SwiftUI + +struct LoggedInOnboardingView: View { + @ObservedObject var supabaseManager: SupabaseManager + + var body: some View { + VStack(alignment: .center) { + Image("SplashIcon") + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: 250) + + Spacer() + + if let user = supabaseManager.user { + if user.productId == 0 { + subscribe + } else { + Text("This subscription is not supported in this version.") + } + } else { + Text("Unable to sign-in. Please check your Internet connection or restart the app.") + } + } + } + + @ViewBuilder + private var subscribe: some View { + RoundedButton("Subscribe", isAccent: true) { + Task { + try await supabaseManager.openStripeCheckout() + } + } + } +} + +#Preview { + LoggedInOnboardingView(supabaseManager: SupabaseManager()) + .frame(width: 400, height: 450) +} + +#Preview { + var supabaseManager = SupabaseManager() + supabaseManager.user = TypeaheadUser(uuid: UUID(), productId: 0) + + return LoggedInOnboardingView(supabaseManager: supabaseManager) + .frame(width: 400, height: 450) +} diff --git a/TypeaheadAI/Views/Onboarding/OnboardingView.swift b/TypeaheadAI/Views/Onboarding/OnboardingView.swift index 9011e3c..bf7b337 100644 --- a/TypeaheadAI/Views/Onboarding/OnboardingView.swift +++ b/TypeaheadAI/Views/Onboarding/OnboardingView.swift @@ -55,7 +55,9 @@ struct OnboardingView: View { @ViewBuilder var panel: some View { - if step == 1 { + if step == 0 { + AnyView(LoggedInOnboardingView(supabaseManager: supabaseManager)) + } else if step == 1 { AnyView(IntroOnboardingView()) } else if step == 2 { AnyView(PermissionsOnboardingView()) diff --git a/TypeaheadAI/Views/Onboarding/PermissionsOnboardingView.swift b/TypeaheadAI/Views/Onboarding/PermissionsOnboardingView.swift index 65a01f7..5ba2045 100644 --- a/TypeaheadAI/Views/Onboarding/PermissionsOnboardingView.swift +++ b/TypeaheadAI/Views/Onboarding/PermissionsOnboardingView.swift @@ -16,7 +16,7 @@ struct PermissionsOnboardingView: View, CanScreenshot { Text( """ - Before we can get started, Typeahead will need some permissions in order to use your clipboard and to see what's on your screen, but it will only use your clipboard and your screen when you activate it. + Typeahead needs your permission to use your clipboard and to see what's on your screen, but it will only use your clipboard and your screen when you activate it. In **System Settings**, under the **Privacy & Security** tab, please navigate to the **Accessibility** options and add Typeahead to your allowed apps. @@ -28,20 +28,26 @@ struct PermissionsOnboardingView: View, CanScreenshot { Spacer() - RoundedButton("Check Accessibility Permissions", isAccent: true) { - // Simulate a key press to trigger permission request - let source = CGEventSource(stateID: .hidSystemState)! - let cmdCDown = CGEvent(keyboardEventSource: source, virtualKey: 0x08, keyDown: true)! - cmdCDown.post(tap: .cghidEventTap) - } + HStack { + Spacer() - Spacer() + RoundedButton("Check Accessibility Permissions", isAccent: true) { + // Simulate a key press to trigger permission request + let source = CGEventSource(stateID: .hidSystemState)! + let cmdCDown = CGEvent(keyboardEventSource: source, virtualKey: 0x08, keyDown: true)! + cmdCDown.post(tap: .cghidEventTap) + } - RoundedButton("Check Screen Recording Permissions", isAccent: true) { - // Take a screenshot to trigger permission request - Task { - _ = try await screenshot() + Spacer() + + RoundedButton("Check Screen Recording Permissions", isAccent: true) { + // Take a screenshot to trigger permission request + Task { + _ = try await screenshot() + } } + + Spacer() } Spacer()