mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
update welcome view and registration view
This commit is contained in:
parent
89164a353f
commit
9bfb50f8d8
15 changed files with 113 additions and 315 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -7,9 +7,12 @@ 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 selectedEnvironment = AppEnvironment.initialAppEnvironment
|
||||
|
||||
|
|
@ -19,68 +22,121 @@ struct WelcomeView: View {
|
|||
}
|
||||
}
|
||||
|
||||
@ViewBuilder func userInteractiveView(width: CGFloat) -> some View {
|
||||
Group {
|
||||
if showRegistrationView {
|
||||
RegistrationView()
|
||||
} else {
|
||||
GetStartedView(showRegistrationView: $showRegistrationView)
|
||||
}
|
||||
var headlineView: some View {
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
Text("Everything you read. Safe, organized, and easy to share.")
|
||||
.font(.appLargeTitle)
|
||||
|
||||
Button(
|
||||
action: { print("learn more button tapped") },
|
||||
label: {
|
||||
HStack(spacing: 4) {
|
||||
Text("Learn more")
|
||||
Image(systemName: "arrow.right")
|
||||
}
|
||||
.font(.appTitleThree)
|
||||
}
|
||||
)
|
||||
.foregroundColor(.appGrayTextContrast)
|
||||
}
|
||||
.frame(width: width)
|
||||
.zIndex(2)
|
||||
}
|
||||
|
||||
@ViewBuilder func primaryContent() -> some View {
|
||||
if horizontalSizeClass == .compact {
|
||||
GeometryReader { geometry in
|
||||
ZStack(alignment: .leading) {
|
||||
Color.systemBackground
|
||||
.edgesIgnoringSafeArea(.all)
|
||||
var footerView: some View {
|
||||
Text("By signing up, you agree to Omnivore’s\nTerms of Service and Privacy Policy")
|
||||
.font(.appSubheadline)
|
||||
}
|
||||
|
||||
if geometry.size.width < geometry.size.height, !isKeyboardOnScreen {
|
||||
VStack {
|
||||
Color.appDeepBackground.frame(height: 100)
|
||||
Spacer()
|
||||
var logoView: some View {
|
||||
Image.omnivoreTitleLogo
|
||||
.gesture(
|
||||
TapGesture(count: 2)
|
||||
.onEnded {
|
||||
if !Bundle.main.isAppStoreBuild {
|
||||
showDebugModal = true
|
||||
}
|
||||
.edgesIgnoringSafeArea(.all)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
VStack {
|
||||
if geometry.size.width < geometry.size.height, !isKeyboardOnScreen {
|
||||
RegistrationHeroImageView(tapGestureHandler: handleHiddenGestureAction)
|
||||
}
|
||||
userInteractiveView(width: geometry.size.width)
|
||||
Spacer()
|
||||
}
|
||||
var authProviderButtonStack: some View {
|
||||
let buttonGroup = Group {
|
||||
AppleSignInButton {
|
||||
viewModel.handleAppleSignInCompletion(result: $0, authenticator: authenticator)
|
||||
}
|
||||
|
||||
if AppKeys.sharedInstance?.iosClientGoogleId != nil {
|
||||
GoogleAuthButton {
|
||||
viewModel.handleGoogleAuth(authenticator: authenticator)
|
||||
}
|
||||
}
|
||||
} 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
return VStack(alignment: .center, spacing: 16) {
|
||||
if horizontalSizeClass == .regular {
|
||||
HStack { buttonGroup }
|
||||
} else {
|
||||
buttonGroup
|
||||
}
|
||||
|
||||
if let loginError = viewModel.loginError {
|
||||
LoginErrorMessageView(loginError: loginError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var compactContent: some View {
|
||||
VStack(alignment: .leading) {
|
||||
logoView
|
||||
Spacer()
|
||||
headlineView
|
||||
Spacer()
|
||||
authProviderButtonStack
|
||||
Spacer()
|
||||
footerView
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
|
||||
var wideContent: some View {
|
||||
GeometryReader { _ in
|
||||
VStack(alignment: .leading) {
|
||||
logoView
|
||||
Spacer()
|
||||
headlineView
|
||||
Spacer()
|
||||
authProviderButtonStack
|
||||
Spacer()
|
||||
footerView
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
primaryContent()
|
||||
.sheet(isPresented: $showDebugModal) {
|
||||
DebugMenuView(selectedEnvironment: $selectedEnvironment)
|
||||
ZStack(alignment: .leading) {
|
||||
Color.appDeepBackground
|
||||
.edgesIgnoringSafeArea(.all)
|
||||
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 caled
|
||||
}
|
||||
} else {
|
||||
if horizontalSizeClass == .compact {
|
||||
compactContent
|
||||
} else {
|
||||
wideContent
|
||||
}
|
||||
}
|
||||
.onReceive(Publishers.keyboardHeight) { isKeyboardOnScreen = $0 > 1 }
|
||||
.onAppear { selectedEnvironment = dataService.appEnvironment }
|
||||
}
|
||||
.sheet(isPresented: $showDebugModal) {
|
||||
DebugMenuView(selectedEnvironment: $selectedEnvironment)
|
||||
}
|
||||
.task { selectedEnvironment = dataService.appEnvironment }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "imagePlaceholder.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
|
@ -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
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 75 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 274 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 614 KiB |
|
|
@ -1,12 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "XXLIllustration.png",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.4 MiB |
|
|
@ -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()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
|
@ -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<Bool>) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue