diff --git a/apple/OmnivoreKit/Sources/App/Views/Profile/ProfileView.swift b/apple/OmnivoreKit/Sources/App/Views/Profile/ProfileView.swift index 70aa2f66e..2d4eef29d 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Profile/ProfileView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Profile/ProfileView.swift @@ -135,7 +135,7 @@ struct ProfileView: View { } } -private extension BasicWebAppView { +extension BasicWebAppView { static func privacyPolicyWebView(baseURL: URL) -> BasicWebAppView { omnivoreWebView(path: "/app/privacy", baseURL: baseURL) } diff --git a/apple/OmnivoreKit/Sources/App/Views/Registration/RegistrationView.swift b/apple/OmnivoreKit/Sources/App/Views/Registration/RegistrationView.swift index 930f196e5..f7d5a496c 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Registration/RegistrationView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Registration/RegistrationView.swift @@ -84,69 +84,10 @@ final class RegistrationViewModel: ObservableObject { } } -struct RegistrationView: View { - @EnvironmentObject var authenticator: Authenticator - @EnvironmentObject var dataService: DataService - @Environment(\.horizontalSizeClass) var horizontalSizeClass - @StateObject private var viewModel = RegistrationViewModel() - - var authenticationView: some View { - VStack(spacing: 0) { - VStack(spacing: 28) { - if horizontalSizeClass == .regular { - Spacer() - } - - VStack(alignment: .center, spacing: 16) { - Text(LocalText.registrationViewHeadline) - .font(.appTitle) - .multilineTextAlignment(.center) - .padding(.bottom, horizontalSizeClass == .compact ? 0 : 50) - .padding(.top, horizontalSizeClass == .compact ? 30 : 0) - - AppleSignInButton { - viewModel.handleAppleSignInCompletion(result: $0, authenticator: authenticator) - } - - if AppKeys.sharedInstance?.iosClientGoogleId != nil { - GoogleAuthButton { - viewModel.handleGoogleAuth(authenticator: authenticator) - } - } - } - - if let loginError = viewModel.loginError { - LoginErrorMessageView(loginError: loginError) - } - - Spacer() - } - .frame(maxWidth: 316) - .padding(.horizontal, 16) - } - } - - var body: some View { - if let registrationState = viewModel.registrationState { - if case let RegistrationViewModel.RegistrationState.createProfile(userProfile) = registrationState { - CreateProfileView(userProfile: userProfile) - } else if case let RegistrationViewModel.RegistrationState.newAppleSignUp(userProfile) = registrationState { - NewAppleSignupView( - userProfile: userProfile, - showProfileEditView: { viewModel.registrationState = .createProfile(userProfile: userProfile) } - ) - } else { - authenticationView - } - } else { - authenticationView - } - } -} - private func presentingViewController() -> PlatformViewController? { #if os(iOS) - return UIApplication.shared.windows + let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene + return scene?.windows .filter(\.isKeyWindow) .first? .rootViewController diff --git a/apple/OmnivoreKit/Sources/App/Views/WelcomeView.swift b/apple/OmnivoreKit/Sources/App/Views/WelcomeView.swift index 96a5e7521..4a45a70e3 100644 --- a/apple/OmnivoreKit/Sources/App/Views/WelcomeView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/WelcomeView.swift @@ -7,11 +7,19 @@ import Views struct WelcomeView: View { @EnvironmentObject var dataService: DataService + @EnvironmentObject var authenticator: Authenticator @Environment(\.horizontalSizeClass) var horizontalSizeClass + + @StateObject private var viewModel = RegistrationViewModel() + @State private var showRegistrationView = false - @State private var isKeyboardOnScreen = false @State private var showDebugModal = false + @State private var showTermsLinks = false + @State private var showTermsModal = false + @State private var showPrivacyModal = false + @State private var showAboutPage = false @State private var selectedEnvironment = AppEnvironment.initialAppEnvironment + @State private var containerSize: CGSize = .zero func handleHiddenGestureAction() { if !Bundle.main.isAppStoreBuild { @@ -19,68 +27,183 @@ struct WelcomeView: View { } } - @ViewBuilder func userInteractiveView(width: CGFloat) -> some View { + var headlineText: some View { Group { - if showRegistrationView { - RegistrationView() + if horizontalSizeClass == .compact { + Text("Everything you read. Safe, organized, and easy to share.") } else { - GetStartedView(showRegistrationView: $showRegistrationView) + Text("Everything you read. Safe,\norganized, and easy to share.") } } - .frame(width: width) - .zIndex(2) + .font(.appLargeTitle) } - @ViewBuilder func primaryContent() -> some View { - if horizontalSizeClass == .compact { - GeometryReader { geometry in - ZStack(alignment: .leading) { - Color.systemBackground - .edgesIgnoringSafeArea(.all) + var headlineView: some View { + VStack(alignment: .leading, spacing: 8) { + headlineText - if geometry.size.width < geometry.size.height, !isKeyboardOnScreen { - VStack { - Color.appDeepBackground.frame(height: 100) - Spacer() - } - .edgesIgnoringSafeArea(.all) - } - - VStack { - if geometry.size.width < geometry.size.height, !isKeyboardOnScreen { - RegistrationHeroImageView(tapGestureHandler: handleHiddenGestureAction) - } - userInteractiveView(width: geometry.size.width) - Spacer() + Button( + action: { showAboutPage = true }, + label: { + HStack(spacing: 4) { + Text("Learn more") + Image(systemName: "arrow.right") } + .font(.appTitleThree) } + ) + .foregroundColor(.appGrayTextContrast) + } + } + + var footerView: some View { + Group { + Text("By signing up, you agree to Omnivore’s\n") + + Text("Terms of Service").underline() + + Text(" and ") + + Text("Privacy Policy").underline() + } + .font(.appSubheadline) + .confirmationDialog("", isPresented: $showTermsLinks, titleVisibility: .hidden) { + Button("View Terms of Service") { + showTermsModal = true } - } else { - GeometryReader { geometry in - ZStack(alignment: .leading) { - SplitColorBackground(width: geometry.size.width) - VStack { - TitleLogoView(handleHiddenGestureAction: handleHiddenGestureAction) - Spacer() - } - .padding() + Button("View Privacy Policy") { + showPrivacyModal = true + } + } + .sheet(isPresented: $showPrivacyModal) { + VStack { + HStack { + Spacer() + Button( + action: { + showPrivacyModal = false + }, + label: { + Image(systemName: "xmark.circle").foregroundColor(.appGrayTextContrast) + } + ) + } + .padding() + BasicWebAppView.privacyPolicyWebView(baseURL: dataService.appEnvironment.webAppBaseURL) + } + } + .sheet(isPresented: $showTermsModal) { + VStack { + HStack { + Spacer() + Button( + action: { + showTermsModal = false + }, + label: { + Image(systemName: "xmark.circle").foregroundColor(.appGrayTextContrast) + } + ) + } + .padding() + BasicWebAppView.termsConditionsWebView(baseURL: dataService.appEnvironment.webAppBaseURL) + } + } + .sheet(isPresented: $showAboutPage) { + if let url = URL(string: "https://omnivore.app/about") { + SafariView(url: url) + } + } + .onTapGesture { + showTermsLinks = true + } + } - HStack(spacing: 0) { - userInteractiveView(width: geometry.size.width * 0.5) - ReadingIllustrationXXLView(width: geometry.size.width * 0.5) + var logoView: some View { + Image.omnivoreTitleLogo + .gesture( + TapGesture(count: 2) + .onEnded { + if !Bundle.main.isAppStoreBuild { + showDebugModal = true + } } + ) + } + + var authProviderButtonStack: some View { + let useHorizontalLayout = containerSize.width > 400 + + let buttonGroup = Group { + AppleSignInButton { + viewModel.handleAppleSignInCompletion(result: $0, authenticator: authenticator) + } + + if AppKeys.sharedInstance?.iosClientGoogleId != nil { + GoogleAuthButton { + viewModel.handleGoogleAuth(authenticator: authenticator) } } } + + return + VStack(alignment: .center, spacing: 16) { + if useHorizontalLayout { + HStack { buttonGroup } + } else { + buttonGroup + } + + if let loginError = viewModel.loginError { + HStack { + LoginErrorMessageView(loginError: loginError) + Spacer() + } + } + } } public var body: some View { - primaryContent() - .sheet(isPresented: $showDebugModal) { - DebugMenuView(selectedEnvironment: $selectedEnvironment) + ZStack(alignment: .leading) { + Color.appBackground + .edgesIgnoringSafeArea(.all) + .modifier(SizeModifier()) + .onPreferenceChange(SizePreferenceKey.self) { + self.containerSize = $0 + } + if let registrationState = viewModel.registrationState { + if case let RegistrationViewModel.RegistrationState.createProfile(userProfile) = registrationState { + CreateProfileView(userProfile: userProfile) + } else if case let RegistrationViewModel.RegistrationState.newAppleSignUp(userProfile) = registrationState { + NewAppleSignupView( + userProfile: userProfile, + showProfileEditView: { viewModel.registrationState = .createProfile(userProfile: userProfile) } + ) + } else { + EmptyView() // will never be called + } + } else { + VStack(alignment: .leading, spacing: containerSize.height < 500 ? 12 : 50) { + logoView + .padding(.bottom, 20) + headlineView + if containerSize.width > 400 { + authProviderButtonStack + } else { + HStack { + Spacer() + authProviderButtonStack + Spacer() + } + } + footerView + Spacer() + } + .padding() + .sheet(isPresented: $showDebugModal) { + DebugMenuView(selectedEnvironment: $selectedEnvironment) + } } - .onReceive(Publishers.keyboardHeight) { isKeyboardOnScreen = $0 > 1 } - .onAppear { selectedEnvironment = dataService.appEnvironment } + } + .preferredColorScheme(.light) + .task { selectedEnvironment = dataService.appEnvironment } } } diff --git a/apple/OmnivoreKit/Sources/Views/Buttons/AppleSignInButton.swift b/apple/OmnivoreKit/Sources/Views/Buttons/AppleSignInButton.swift index b8381201f..a4e206e52 100644 --- a/apple/OmnivoreKit/Sources/Views/Buttons/AppleSignInButton.swift +++ b/apple/OmnivoreKit/Sources/Views/Buttons/AppleSignInButton.swift @@ -18,8 +18,9 @@ public struct AppleSignInButton: View { }, onCompletion: onCompletion ) - .frame(height: 44) + .frame(height: 54) + .frame(maxWidth: 300) .cornerRadius(8) - .signInWithAppleButtonStyle(colorScheme == .dark ? .white : .black) + .signInWithAppleButtonStyle(.white) } } diff --git a/apple/OmnivoreKit/Sources/Views/Buttons/GoogleAuthButton.swift b/apple/OmnivoreKit/Sources/Views/Buttons/GoogleAuthButton.swift index 9296252c7..fdfffccef 100644 --- a/apple/OmnivoreKit/Sources/Views/Buttons/GoogleAuthButton.swift +++ b/apple/OmnivoreKit/Sources/Views/Buttons/GoogleAuthButton.swift @@ -12,13 +12,14 @@ public struct GoogleAuthButton: View { HStack(spacing: 8) { Image.googleIcon .resizable() - .frame(width: 12, height: 12) + .frame(width: 16, height: 16) Text(LocalText.googleAuthButton) - .font(isMacApp ? .appCaption : .appBody) + .font(isMacApp ? .appCaption : .appTitleThree) .foregroundColor(.black) + .fontWeight(Font.Weight.medium) } - .frame(maxWidth: .infinity) - .frame(height: isMacApp ? 30 : 44) + .frame(maxWidth: 300) + .frame(height: isMacApp ? 30 : 54) } .buttonStyle(GoogleButtonStyle()) } diff --git a/apple/OmnivoreKit/Sources/Views/Colors/Colors.xcassets/_background.colorset/Contents.json b/apple/OmnivoreKit/Sources/Views/Colors/Colors.xcassets/_background.colorset/Contents.json index f39b6241a..eddecb30f 100644 --- a/apple/OmnivoreKit/Sources/Views/Colors/Colors.xcassets/_background.colorset/Contents.json +++ b/apple/OmnivoreKit/Sources/Views/Colors/Colors.xcassets/_background.colorset/Contents.json @@ -5,9 +5,9 @@ "color-space" : "srgb", "components" : { "alpha" : "1.000", - "blue" : "0x9F", - "green" : "0xEA", - "red" : "0xFF" + "blue" : "0xA8", + "green" : "0xEB", + "red" : "0xFB" } }, "idiom" : "universal" @@ -23,9 +23,9 @@ "color-space" : "srgb", "components" : { "alpha" : "1.000", - "blue" : "0x9F", - "green" : "0xEA", - "red" : "0xFF" + "blue" : "0xA8", + "green" : "0xEB", + "red" : "0xFB" } }, "idiom" : "universal" diff --git a/apple/OmnivoreKit/Sources/Views/Images/Images.swift b/apple/OmnivoreKit/Sources/Views/Images/Images.swift index 9fbd99fe2..b8ba31b74 100644 --- a/apple/OmnivoreKit/Sources/Views/Images/Images.swift +++ b/apple/OmnivoreKit/Sources/Views/Images/Images.swift @@ -3,10 +3,7 @@ import SwiftUI public extension Image { static var smallOmnivoreLogo: Image { Image("_smallOmnivoreLogo", bundle: .module) } static var omnivoreTitleLogo: Image { Image("_omnivoreTitleLogo", bundle: .module) } - static var readingIllustration: Image { Image("_readingIllustration", bundle: .module) } - static var readingIllustrationXXL: Image { Image("_readingIllustrationXXL", bundle: .module) } static var googleIcon: Image { Image("_googleIcon", bundle: .module) } - static var feedItemPlaceholder: Image { Image("_feedItemPlaceholder", bundle: .module) } static var sunHorizon: Image { Image("_sun-horizon", bundle: .module) } static var mountains: Image { Image("_mountains", bundle: .module) } static var moon: Image { Image("_moon", bundle: .module) } diff --git a/apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/_feedItemPlaceholder.imageset/Contents.json b/apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/_feedItemPlaceholder.imageset/Contents.json deleted file mode 100644 index 41df4eec6..000000000 --- a/apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/_feedItemPlaceholder.imageset/Contents.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "images" : [ - { - "filename" : "imagePlaceholder.pdf", - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/_feedItemPlaceholder.imageset/imagePlaceholder.pdf b/apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/_feedItemPlaceholder.imageset/imagePlaceholder.pdf deleted file mode 100644 index 8a8ace209..000000000 Binary files a/apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/_feedItemPlaceholder.imageset/imagePlaceholder.pdf and /dev/null differ diff --git a/apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/_readingIllustration.imageset/Contents.json b/apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/_readingIllustration.imageset/Contents.json deleted file mode 100644 index 35363756d..000000000 --- a/apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/_readingIllustration.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "filename" : "Mask Group.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "Mask Group@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "Mask Group@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/_readingIllustration.imageset/Mask Group.png b/apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/_readingIllustration.imageset/Mask Group.png deleted file mode 100644 index 96f2e1d1a..000000000 Binary files a/apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/_readingIllustration.imageset/Mask Group.png and /dev/null differ diff --git a/apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/_readingIllustration.imageset/Mask Group@2x.png b/apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/_readingIllustration.imageset/Mask Group@2x.png deleted file mode 100644 index c50cf319b..000000000 Binary files a/apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/_readingIllustration.imageset/Mask Group@2x.png and /dev/null differ diff --git a/apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/_readingIllustration.imageset/Mask Group@3x.png b/apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/_readingIllustration.imageset/Mask Group@3x.png deleted file mode 100644 index 61dedfc11..000000000 Binary files a/apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/_readingIllustration.imageset/Mask Group@3x.png and /dev/null differ diff --git a/apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/_readingIllustrationXXL.imageset/Contents.json b/apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/_readingIllustrationXXL.imageset/Contents.json deleted file mode 100644 index d4463ad6c..000000000 --- a/apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/_readingIllustrationXXL.imageset/Contents.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "images" : [ - { - "filename" : "XXLIllustration.png", - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/_readingIllustrationXXL.imageset/XXLIllustration.png b/apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/_readingIllustrationXXL.imageset/XXLIllustration.png deleted file mode 100644 index c94aaf30f..000000000 Binary files a/apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/_readingIllustrationXXL.imageset/XXLIllustration.png and /dev/null differ diff --git a/apple/OmnivoreKit/Sources/Views/RegistrationViews/RegistrationHeroImageView.swift b/apple/OmnivoreKit/Sources/Views/RegistrationViews/RegistrationHeroImageView.swift deleted file mode 100644 index e57096d91..000000000 --- a/apple/OmnivoreKit/Sources/Views/RegistrationViews/RegistrationHeroImageView.swift +++ /dev/null @@ -1,25 +0,0 @@ -import SwiftUI - -public struct RegistrationHeroImageView: View { - let tapGestureHandler: () -> Void - - public init(tapGestureHandler: @escaping () -> Void) { - self.tapGestureHandler = tapGestureHandler - } - - public var body: some View { - ZStack(alignment: .topLeading) { - Image.readingIllustration - .resizable() - .aspectRatio(contentMode: .fit) - Image.omnivoreTitleLogo - .padding() - .gesture( - TapGesture(count: 2) - .onEnded { - tapGestureHandler() - } - ) - } - } -} diff --git a/apple/OmnivoreKit/Sources/Views/RegistrationViews/ToggleAuthFlowButton.swift b/apple/OmnivoreKit/Sources/Views/RegistrationViews/ToggleAuthFlowButton.swift deleted file mode 100644 index f7c417fd3..000000000 --- a/apple/OmnivoreKit/Sources/Views/RegistrationViews/ToggleAuthFlowButton.swift +++ /dev/null @@ -1,41 +0,0 @@ -import Models -import SwiftUI - -struct ToggleAuthFlowButton: View { - let authFlow: AuthFlow - let action: () -> Void - - var buttonTitle: String { - switch authFlow { - case .signIn: - return "Don’t have an account? " - case .signUp: - return "Already have an account? " - } - } - - var buttonTitleSuffix: String { - switch authFlow { - case .signIn: - return "Sign Up" - case .signUp: - return "Log In" - } - } - - var body: some View { - Button( - action: action, - label: { - Text(buttonTitle) - .font(.appFootnote) - .foregroundColor(.appGrayText) - + Text(buttonTitleSuffix) - .underline() - .font(.appFootnote) - .foregroundColor(.red) - } - ) - .buttonStyle(PlainButtonStyle()) - } -} diff --git a/apple/OmnivoreKit/Sources/Views/SizeModifier.swift b/apple/OmnivoreKit/Sources/Views/SizeModifier.swift new file mode 100644 index 000000000..c10f4de99 --- /dev/null +++ b/apple/OmnivoreKit/Sources/Views/SizeModifier.swift @@ -0,0 +1,23 @@ +import SwiftUI + +public struct SizePreferenceKey: PreferenceKey { + public static var defaultValue: CGSize = .zero + + public static func reduce(value: inout CGSize, nextValue: () -> CGSize) { + value = nextValue() + } +} + +public struct SizeModifier: ViewModifier { + public init() {} + + private var sizeView: some View { + GeometryReader { geometry in + Color.clear.preference(key: SizePreferenceKey.self, value: geometry.size) + } + } + + public func body(content: Content) -> some View { + content.background(sizeView) + } +} diff --git a/apple/OmnivoreKit/Sources/Views/WelcomeView/WelcomeViewComponents.swift b/apple/OmnivoreKit/Sources/Views/WelcomeView/WelcomeViewComponents.swift deleted file mode 100644 index f4532c1b9..000000000 --- a/apple/OmnivoreKit/Sources/Views/WelcomeView/WelcomeViewComponents.swift +++ /dev/null @@ -1,83 +0,0 @@ -import SwiftUI - -public struct ReadingIllustrationXXLView: View { - let width: CGFloat - - public init(width: CGFloat) { - self.width = width - } - - public var body: some View { - Image.readingIllustrationXXL - .resizable() - .aspectRatio(contentMode: .fill) - .frame(width: width) - .clipped() - .edgesIgnoringSafeArea([.vertical, .trailing]) - } -} - -public struct TitleLogoView: View { - let handleHiddenGestureAction: () -> Void - - public init(handleHiddenGestureAction: @escaping () -> Void) { - self.handleHiddenGestureAction = handleHiddenGestureAction - } - - public var body: some View { - Image.omnivoreTitleLogo - .renderingMode(.template) - .foregroundColor(.appGrayTextContrast) - .frame(height: 40) - .gesture( - TapGesture(count: 2) - .onEnded { - handleHiddenGestureAction() - } - ) - } -} - -public struct GetStartedView: View { - @Environment(\.horizontalSizeClass) var horizontalSizeClass - @Binding var showRegistrationView: Bool - - public init(showRegistrationView: Binding) { - self._showRegistrationView = showRegistrationView - } - - public var body: some View { - HStack { - VStack(alignment: .leading, spacing: 32) { - Text("A better social\nreading experience\nstarts with Omnivore.") - .font(.appTitle) - .multilineTextAlignment(.leading) - - BorderedButton(color: .appGrayTextContrast, text: "Get Started") { - showRegistrationView = true - } - .frame(width: 220) - } - .padding(.leading, horizontalSizeClass == .compact ? 16 : 80) - .padding(.top, horizontalSizeClass == .compact ? 16 : 0) - - Spacer() - } - } -} - -public struct SplitColorBackground: View { - let width: CGFloat - - public init(width: CGFloat) { - self.width = width - } - - public var body: some View { - HStack(spacing: 0) { - Color.systemBackground.frame(width: width * 0.5) - Color.appBackground.frame(width: width * 0.5) - } - .edgesIgnoringSafeArea(.all) - } -}