diff --git a/apple/OmnivoreKit/Sources/Binders/RootViewModel.swift b/apple/OmnivoreKit/Sources/Binders/RootViewModel.swift index f64616d46..9259d3f65 100644 --- a/apple/OmnivoreKit/Sources/Binders/RootViewModel.swift +++ b/apple/OmnivoreKit/Sources/Binders/RootViewModel.swift @@ -170,7 +170,7 @@ public struct RootView: View { #endif } else { - WelcomeView(viewModel: WelcomeViewModel.make(services: viewModel.services)) + WelcomeView(services: viewModel.services) .accessibilityElement() .accessibilityIdentifier("welcomeView") } diff --git a/apple/OmnivoreKit/Sources/Binders/Scenes/WelcomeScene.swift b/apple/OmnivoreKit/Sources/Binders/Scenes/WelcomeScene.swift deleted file mode 100644 index f9fcf48a3..000000000 --- a/apple/OmnivoreKit/Sources/Binders/Scenes/WelcomeScene.swift +++ /dev/null @@ -1,26 +0,0 @@ -import Services -import SwiftUI -import Utils -import Views - -extension WelcomeViewModel { - static func make(services: Services) -> WelcomeViewModel { - let registrationViewModel = RegistrationViewModel.make(services: services) - let viewModel = WelcomeViewModel(registrationViewModel: registrationViewModel) - viewModel.bind(services: services) - return viewModel - } - - func bind(services: Services) { - performActionSubject.sink { [weak self] action in - switch action { - case .hiddenGesturePerformed: - if !Bundle.main.isAppStoreBuild { - self?.debugMenuViewModel = DebugMenuViewModel.make(services: services) - self?.showDebugModal = true - } - } - } - .store(in: &subscriptions) - } -} diff --git a/apple/OmnivoreKit/Sources/Binders/Views/WelcomeView.swift b/apple/OmnivoreKit/Sources/Binders/Views/WelcomeView.swift new file mode 100644 index 000000000..9d80f2283 --- /dev/null +++ b/apple/OmnivoreKit/Sources/Binders/Views/WelcomeView.swift @@ -0,0 +1,83 @@ +import Combine +import Services +import SwiftUI +import Utils +import Views + +struct WelcomeView: View { + @Environment(\.horizontalSizeClass) var horizontalSizeClass + let services: Services + @State private var showRegistrationView = false + @State private var isKeyboardOnScreen = false + @State private var showDebugModal = false + + func handleHiddenGestureAction() { + if !Bundle.main.isAppStoreBuild { + showDebugModal = true + } + } + + @ViewBuilder func userInteractiveView(width: CGFloat) -> some View { + Group { + if showRegistrationView { + RegistrationView(viewModel: RegistrationViewModel.make(services: services)) + } else { + GetStartedView(showRegistrationView: $showRegistrationView) + } + } + .frame(width: width) + .zIndex(2) + } + + @ViewBuilder func primaryContent() -> some View { + if horizontalSizeClass == .compact { + GeometryReader { geometry in + ZStack(alignment: .leading) { + Color.systemBackground + .edgesIgnoringSafeArea(.all) + + 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() + } + } + } + } else { + GeometryReader { geometry in + ZStack(alignment: .leading) { + SplitColorBackground(width: geometry.size.width) + + VStack { + TitleLogoView(handleHiddenGestureAction: handleHiddenGestureAction) + Spacer() + } + .padding() + + HStack(spacing: 0) { + userInteractiveView(width: geometry.size.width * 0.5) + ReadingIllustrationXXLView(width: geometry.size.width * 0.5) + } + } + } + } + } + + public var body: some View { + primaryContent() + .sheet(isPresented: $showDebugModal) { + DebugMenuView(viewModel: DebugMenuViewModel.make(services: services)) + } + .onReceive(Publishers.keyboardHeight) { isKeyboardOnScreen = $0 > 1 } + } +} diff --git a/apple/OmnivoreKit/Sources/Views/KeyboardManagement.swift b/apple/OmnivoreKit/Sources/Views/KeyboardManagement.swift index bbac86b51..ca0e42a3a 100644 --- a/apple/OmnivoreKit/Sources/Views/KeyboardManagement.swift +++ b/apple/OmnivoreKit/Sources/Views/KeyboardManagement.swift @@ -5,7 +5,7 @@ import SwiftUI import UIKit #endif -extension Publishers { +public extension Publishers { static var keyboardHeight: AnyPublisher { #if os(iOS) let willShow = NotificationCenter.default diff --git a/apple/OmnivoreKit/Sources/Views/RegistrationViews/RegistrationHeroImageView.swift b/apple/OmnivoreKit/Sources/Views/RegistrationViews/RegistrationHeroImageView.swift index 5f8bccb50..e57096d91 100644 --- a/apple/OmnivoreKit/Sources/Views/RegistrationViews/RegistrationHeroImageView.swift +++ b/apple/OmnivoreKit/Sources/Views/RegistrationViews/RegistrationHeroImageView.swift @@ -1,9 +1,13 @@ import SwiftUI -struct RegistrationHeroImageView: View { +public struct RegistrationHeroImageView: View { let tapGestureHandler: () -> Void - var body: some View { + public init(tapGestureHandler: @escaping () -> Void) { + self.tapGestureHandler = tapGestureHandler + } + + public var body: some View { ZStack(alignment: .topLeading) { Image.readingIllustration .resizable() diff --git a/apple/OmnivoreKit/Sources/Views/WelcomeView/WelcomeView.swift b/apple/OmnivoreKit/Sources/Views/WelcomeView/WelcomeView.swift deleted file mode 100644 index a9ae1c09a..000000000 --- a/apple/OmnivoreKit/Sources/Views/WelcomeView/WelcomeView.swift +++ /dev/null @@ -1,169 +0,0 @@ -import Combine -import Models -import SwiftUI - -public final class WelcomeViewModel: ObservableObject { - @Published public var showDebugModal: Bool = false - @Published public var showRegistrationModal: Bool = false - - public let registrationViewModel: RegistrationViewModel - public var debugMenuViewModel: DebugMenuViewModel? - - public enum Action { - case hiddenGesturePerformed - } - - public var subscriptions = Set() - public let performActionSubject = PassthroughSubject() - - public init(registrationViewModel: RegistrationViewModel) { - self.registrationViewModel = registrationViewModel - } -} - -public struct WelcomeView: View { - @Environment(\.horizontalSizeClass) var horizontalSizeClass - @ObservedObject private var viewModel: WelcomeViewModel - @State private var showRegistrationView = false - @State private var isKeyboardOnScreen = false - - public init(viewModel: WelcomeViewModel) { - self.viewModel = viewModel - } - - @ViewBuilder func userInteractiveView(width: CGFloat) -> some View { - Group { - if showRegistrationView { - RegistrationView(viewModel: viewModel.registrationViewModel) - } else { - GetStartedView(showRegistrationView: $showRegistrationView) - } - } - .frame(width: width) - .zIndex(2) - } - - @ViewBuilder func primaryContent() -> some View { - if horizontalSizeClass == .compact { - GeometryReader { geometry in - ZStack(alignment: .leading) { - Color.systemBackground - .edgesIgnoringSafeArea(.all) - - 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: { viewModel.performActionSubject.send(.hiddenGesturePerformed) } - ) - } - userInteractiveView(width: geometry.size.width) - Spacer() - } - } - } - } else { - GeometryReader { geometry in - ZStack(alignment: .leading) { - SplitColorBackground(width: geometry.size.width) - - VStack { - TitleLogoView { - viewModel.performActionSubject.send(.hiddenGesturePerformed) - } - Spacer() - } - .padding() - - HStack(spacing: 0) { - userInteractiveView(width: geometry.size.width * 0.5) - ReadingIllustrationXXLView(width: geometry.size.width * 0.5) - } - } - } - } - } - - public var body: some View { - primaryContent() - .sheet(isPresented: $viewModel.showDebugModal) { - if let debugMenuViewModel = viewModel.debugMenuViewModel { - DebugMenuView(viewModel: debugMenuViewModel) - } - } - .onReceive(Publishers.keyboardHeight) { isKeyboardOnScreen = $0 > 1 } - } -} - -public struct ReadingIllustrationXXLView: View { - let width: CGFloat - - 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 var body: some View { - Image.omnivoreTitleLogo - .renderingMode(.template) - .foregroundColor(.appGrayTextContrast) - .frame(height: 40) - .gesture( - TapGesture(count: 2) - .onEnded { - handleHiddenGestureAction() - } - ) - } -} - -struct GetStartedView: View { - @Environment(\.horizontalSizeClass) var horizontalSizeClass - @Binding var showRegistrationView: Bool - - 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() - } - } -} - -struct SplitColorBackground: View { - let width: CGFloat - - var body: some View { - HStack(spacing: 0) { - Color.systemBackground.frame(width: width * 0.5) - Color.appBackground.frame(width: width * 0.5) - } - .edgesIgnoringSafeArea(.all) - } -} diff --git a/apple/OmnivoreKit/Sources/Views/WelcomeView/WelcomeViewComponents.swift b/apple/OmnivoreKit/Sources/Views/WelcomeView/WelcomeViewComponents.swift new file mode 100644 index 000000000..f4532c1b9 --- /dev/null +++ b/apple/OmnivoreKit/Sources/Views/WelcomeView/WelcomeViewComponents.swift @@ -0,0 +1,83 @@ +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) + } +}