Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: pre-login mobile app exploration #139

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions Authorization/Authorization.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@
0770DE7128D0C0E7006D8A5D /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0770DE7028D0C0E7006D8A5D /* Strings.swift */; };
5FB79D2802949372CDAF08D6 /* Pods_App_Authorization_AuthorizationTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4FAE9B7FD61FF88C9C4FE1E8 /* Pods_App_Authorization_AuthorizationTests.framework */; };
DE843D6BB1B9DDA398494890 /* Pods_App_Authorization.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 47BCFB7C19382EECF15131B6 /* Pods_App_Authorization.framework */; };
E03261642AE64676002CA7EB /* StartupViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E03261632AE64676002CA7EB /* StartupViewModel.swift */; };
E03261662AE64AF4002CA7EB /* StartupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E03261652AE64AF4002CA7EB /* StartupView.swift */; };
E03261682AE9F156002CA7EB /* LogistrationBottomView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E03261672AE9F156002CA7EB /* LogistrationBottomView.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -75,6 +78,9 @@
96C85172770225EB81A6D2DA /* Pods-App-Authorization.releasedev.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App-Authorization.releasedev.xcconfig"; path = "Target Support Files/Pods-App-Authorization/Pods-App-Authorization.releasedev.xcconfig"; sourceTree = "<group>"; };
9BF6A1004A955E24527FCF0F /* Pods-App-Authorization-AuthorizationTests.releaseprod.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App-Authorization-AuthorizationTests.releaseprod.xcconfig"; path = "Target Support Files/Pods-App-Authorization-AuthorizationTests/Pods-App-Authorization-AuthorizationTests.releaseprod.xcconfig"; sourceTree = "<group>"; };
A99D45203C981893C104053A /* Pods-App-Authorization-AuthorizationTests.releasestage.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App-Authorization-AuthorizationTests.releasestage.xcconfig"; path = "Target Support Files/Pods-App-Authorization-AuthorizationTests/Pods-App-Authorization-AuthorizationTests.releasestage.xcconfig"; sourceTree = "<group>"; };
E03261632AE64676002CA7EB /* StartupViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartupViewModel.swift; sourceTree = "<group>"; };
E03261652AE64AF4002CA7EB /* StartupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartupView.swift; sourceTree = "<group>"; };
E03261672AE9F156002CA7EB /* LogistrationBottomView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogistrationBottomView.swift; sourceTree = "<group>"; };
E78971D8E6ED2116BBF9FD66 /* Pods-App-Authorization.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App-Authorization.release.xcconfig"; path = "Target Support Files/Pods-App-Authorization/Pods-App-Authorization.release.xcconfig"; sourceTree = "<group>"; };
F52826C68AEA1CF4769389EA /* Pods-App-Authorization.releasestage.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App-Authorization.releasestage.xcconfig"; path = "Target Support Files/Pods-App-Authorization/Pods-App-Authorization.releasestage.xcconfig"; sourceTree = "<group>"; };
F5802BBA113276950ABCD9B3 /* Pods-App-Authorization.releaseprod.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App-Authorization.releaseprod.xcconfig"; path = "Target Support Files/Pods-App-Authorization/Pods-App-Authorization.releaseprod.xcconfig"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -139,6 +145,7 @@
071009CC28D1E24000344290 /* Presentation */ = {
isa = PBXGroup;
children = (
E03261622AE6464A002CA7EB /* Startup */,
020C31BD290AADA700D6DEA2 /* Base */,
071009C528D1D9FA00344290 /* Login */,
07169462296D93E000E3DED6 /* Registration */,
Expand Down Expand Up @@ -258,6 +265,16 @@
path = ../Pods;
sourceTree = "<group>";
};
E03261622AE6464A002CA7EB /* Startup */ = {
isa = PBXGroup;
children = (
E03261632AE64676002CA7EB /* StartupViewModel.swift */,
E03261652AE64AF4002CA7EB /* StartupView.swift */,
E03261672AE9F156002CA7EB /* LogistrationBottomView.swift */,
);
path = Startup;
sourceTree = "<group>";
};
/* End PBXGroup section */

/* Begin PBXHeadersBuildPhase section */
Expand Down Expand Up @@ -471,11 +488,14 @@
0770DE7128D0C0E7006D8A5D /* Strings.swift in Sources */,
025F40E229D360E20064C183 /* ResetPasswordViewModel.swift in Sources */,
02066B462906D72F00F4307E /* SignUpViewModel.swift in Sources */,
E03261642AE64676002CA7EB /* StartupViewModel.swift in Sources */,
02A2ACDB2A4B016100FBBBBB /* AuthorizationAnalytics.swift in Sources */,
E03261682AE9F156002CA7EB /* LogistrationBottomView.swift in Sources */,
025F40E029D1E2FC0064C183 /* ResetPasswordView.swift in Sources */,
020C31CB290BF49900D6DEA2 /* FieldsView.swift in Sources */,
0770DE4E28D0A677006D8A5D /* SignInView.swift in Sources */,
02F3BFE5292533720051930C /* AuthorizationRouter.swift in Sources */,
E03261662AE64AF4002CA7EB /* StartupView.swift in Sources */,
071009C728D1DA4F00344290 /* SignInViewModel.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
36 changes: 25 additions & 11 deletions Authorization/Authorization/Presentation/Login/SignInView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import SwiftUI
import Core
import Swinject

public struct SignInView: View {

Expand All @@ -29,6 +30,19 @@ public struct SignInView: View {
.resizable()
.edgesIgnoringSafeArea(.top)
}.frame(maxWidth: .infinity, maxHeight: 200)
if viewModel.config.features.startupScreenEnabled {
VStack {
Button(action: { viewModel.router.back() }, label: {
CoreAssets.arrowLeft.swiftUIImage.renderingMode(.template)
.backButtonStyle(color: .white)
})
.foregroundColor(Theme.Colors.styledButtonText)
.padding(.leading, isHorizontal ? 48 : 0)
.padding(.top, 11)

}.frame(maxWidth: .infinity, alignment: .topLeading)
.padding(.top, isHorizontal ? 20 : 0)
}

VStack(alignment: .center) {
CoreAssets.appLogo.swiftUIImage
Expand Down Expand Up @@ -83,21 +97,23 @@ public struct SignInView: View {
.stroke(lineWidth: 1)
.fill(Theme.Colors.textInputStroke)
)

HStack {
Button(AuthLocalization.SignIn.registerBtn) {
viewModel.trackSignUpClicked()
viewModel.router.showRegisterScreen()
}.foregroundColor(Theme.Colors.accentColor)

Spacer()

if !viewModel.config.features.startupScreenEnabled {
Button(AuthLocalization.SignIn.registerBtn) {
viewModel.trackSignUpClicked()
viewModel.router.showRegisterScreen()
}.foregroundColor(Theme.Colors.accentColor)

Spacer()
}

Button(AuthLocalization.SignIn.forgotPassBtn) {
viewModel.trackForgotPasswordClicked()
viewModel.router.showForgotPasswordScreen()
}.foregroundColor(Theme.Colors.accentColor)
.padding(.top, 0)
}
.padding(.top, 10)

if viewModel.isShowProgress {
HStack(alignment: .center) {
ProgressBar(size: 40, lineWidth: 8)
Expand Down Expand Up @@ -153,8 +169,6 @@ public struct SignInView: View {
}
}
.hideNavigationBar()
.navigationBarBackButtonHidden(true)
.navigationBarHidden(true)
.ignoresSafeArea(.all, edges: .horizontal)
.background(Theme.Colors.background.ignoresSafeArea(.all))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public class SignInViewModel: ObservableObject {
}

let router: AuthorizationRouter
private let config: ConfigProtocol
let config: ConfigProtocol
private let interactor: AuthInteractorProtocol
private let analytics: AuthorizationAnalytics
private let validator: Validator
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//
// LogistrationBottomView.swift
// Authorization
//
// Created by SaeedBashir on 10/26/23.
//

import Foundation
import SwiftUI
import Core

public struct LogistrationBottomView: View {
@ObservedObject
private var viewModel: StartupViewModel

@Environment(\.isHorizontal) private var isHorizontal

public init(viewModel: StartupViewModel) {
self.viewModel = viewModel
}

public var body: some View {
VStack(alignment: .leading) {
HStack(spacing: 24) {
StyledButton(AuthLocalization.SignIn.registerBtn) {
viewModel.router.showRegisterScreen()
viewModel.tracksignUpClicked()
}
.frame(maxWidth: .infinity)

StyledButton(
AuthLocalization.SignIn.logInTitle,
action: { viewModel.router.showLoginScreen() },
color: .white,
textColor: Theme.Colors.accentColor,
borderColor: Theme.Colors.textInputStroke
)
.frame(width: 100)
}
.padding(.horizontal, isHorizontal ? 0 : 0)
}
.padding(.horizontal, isHorizontal ? 10 : 24)
}
}

struct LogistrationBottomView_Previews: PreviewProvider {
static var previews: some View {
let vm = StartupViewModel(
interactor: AuthInteractor.mock,
router: AuthorizationRouterMock(),
analytics: AuthorizationAnalyticsMock()
)
LogistrationBottomView(viewModel: vm)
.preferredColorScheme(.light)
.previewDisplayName("StartupView Light")
.loadFonts()

LogistrationBottomView(viewModel: vm)
.preferredColorScheme(.dark)
.previewDisplayName("StartupView Dark")
.loadFonts()
}
}
124 changes: 124 additions & 0 deletions Authorization/Authorization/Presentation/Startup/StartupView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
//
// StartupView.swift
// Authorization
//
// Created by SaeedBashir on 10/23/23.
//

import Foundation
import SwiftUI
import Core

public struct StartupView: View {

@State private var searchQuery: String = ""

@Environment(\.isHorizontal) private var isHorizontal

@ObservedObject
private var viewModel: StartupViewModel

public init(viewModel: StartupViewModel) {
self.viewModel = viewModel
}

public var body: some View {
ZStack(alignment: .top) {
VStack(alignment: .leading) {
CoreAssets.appLogo.swiftUIImage
.resizable()
.frame(maxWidth: 189, maxHeight: 54)
.padding(.top, isHorizontal ? 20 : 40)
.padding(.bottom, isHorizontal ? 0 : 20)
.padding(.horizontal, isHorizontal ? 10 : 24)
.colorMultiply(Theme.Colors.accentColor)

VStack {
VStack(alignment: .leading) {
Text(AuthLocalization.Startup.infoMessage)
.font(Theme.Fonts.titleLarge)
.foregroundColor(Theme.Colors.textPrimary)
.padding(.bottom, isHorizontal ? 10 : 20 )

Text(AuthLocalization.Startup.searchTitle)
.font(Theme.Fonts.bodyLarge)
.bold()
.foregroundColor(Theme.Colors.textPrimary)
.padding(.top, isHorizontal ? 0 : 24)

HStack(spacing: 11) {
Image(systemName: "magnifyingglass")
.padding(.leading, 16)
.padding(.top, 1)
TextField(AuthLocalization.Startup.searchPlaceholder, text: $searchQuery, onCommit: {
if searchQuery.isEmpty { return }
viewModel.router.showDiscoveryScreen(searchQuery: searchQuery, fromStartupScreen: true)
})
.autocapitalization(.none)
.autocorrectionDisabled()
.frame(minHeight: 50)
.submitLabel(.search)

}.overlay(
Theme.Shapes.textInputShape
.stroke(lineWidth: 1)
.fill(Theme.Colors.textInputStroke)
)
.background(
Theme.Shapes.textInputShape
.fill(Theme.Colors.textInputBackground)
)

Button {
viewModel.router.showDiscoveryScreen(searchQuery: searchQuery, fromStartupScreen: true)
} label: {
Text(AuthLocalization.Startup.exploreAllCourses)
.underline()
.foregroundColor(Theme.Colors.accentColor)
.font(Theme.Fonts.bodyLarge)
}
.padding(.top, isHorizontal ? 0 : 5)
Spacer()
}
.padding(.horizontal, isHorizontal ? 10 : 24)

LogistrationBottomView(viewModel: viewModel)
}
.padding(.top, 10)
.padding(.bottom, 2)
}
.onDisappear {
searchQuery = ""
}
}
.hideNavigationBar()
.padding(.all, isHorizontal ? 1 : 0)
.background(Theme.Colors.background.ignoresSafeArea(.all))
.ignoresSafeArea(.keyboard, edges: .bottom)
.onTapGesture {
UIApplication.shared.endEditing()
}
}
}

#if DEBUG
struct StartupView_Previews: PreviewProvider {
static var previews: some View {
let vm = StartupViewModel(
interactor: AuthInteractor.mock,
router: AuthorizationRouterMock(),
analytics: AuthorizationAnalyticsMock()
)

StartupView(viewModel: vm)
.preferredColorScheme(.light)
.previewDisplayName("StartupView Light")
.loadFonts()

StartupView(viewModel: vm)
.preferredColorScheme(.dark)
.previewDisplayName("StartupView Dark")
.loadFonts()
}
}
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// StartupViewModel.swift
// Authorization
//
// Created by SaeedBashir on 10/23/23.
//

import Foundation
import Core

public class StartupViewModel: ObservableObject {
let router: AuthorizationRouter
private let interactor: AuthInteractorProtocol
private let analytics: AuthorizationAnalytics
@Published var searchQuery: String?

public init(
interactor: AuthInteractorProtocol,
router: AuthorizationRouter,
analytics: AuthorizationAnalytics
) {
self.interactor = interactor
self.router = router
self.analytics = analytics
}

func tracksignUpClicked() {
analytics.signUpClicked()
}
}
10 changes: 10 additions & 0 deletions Authorization/Authorization/SwiftGen/Strings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,16 @@ public enum AuthLocalization {
/// Sign up
public static let title = AuthLocalization.tr("Localizable", "SIGN_UP.TITLE", fallback: "Sign up")
}
public enum Startup {
/// Explore all courses
public static let exploreAllCourses = AuthLocalization.tr("Localizable", "STARTUP.EXPLORE_ALL_COURSES", fallback: "Explore all courses")
/// Courses and programs from the world's best universities in your pocket.
public static let infoMessage = AuthLocalization.tr("Localizable", "STARTUP.INFO_MESSAGE", fallback: "Courses and programs from the world's best universities in your pocket.")
/// Search our 3000+ courses
public static let searchPlaceholder = AuthLocalization.tr("Localizable", "STARTUP.SEARCH_PLACEHOLDER", fallback: "Search our 3000+ courses")
/// What do you want to learn?
public static let searchTitle = AuthLocalization.tr("Localizable", "STARTUP.SEARCH_TITLE", fallback: "What do you want to learn?")
}
}
// swiftlint:enable explicit_type_interface function_parameter_count identifier_name line_length
// swiftlint:enable nesting type_body_length type_name vertical_whitespace_opening_braces
Expand Down
5 changes: 5 additions & 0 deletions Authorization/Authorization/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,8 @@
"FORGOT.REQUEST" = "Reset password";
"FORGOT.CHECK_TITLE" = "Check your email";
"FORGOT.CHECK_Description" = "We have sent a password recover instructions to your email ";

"STARTUP.INFO_MESSAGE" = "Courses and programs from the world's best universities in your pocket.";
"STARTUP.SEARCH_TITLE" = "What do you want to learn?";
"STARTUP.SEARCH_PLACEHOLDER" = "Search our 3000+ courses";
"STARTUP.EXPLORE_ALL_COURSES" = "Explore all courses";
5 changes: 5 additions & 0 deletions Authorization/Authorization/uk.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,8 @@
"FORGOT.REQUEST" = "Відновити пароль";
"FORGOT.CHECK_TITLE" = "Перевірте свою електронну пошту";
"FORGOT.CHECK_Description" = "Ми надіслали інструкції щодо відновлення пароля на вашу електронну пошту ";

"STARTUP.INFO_MESSAGE" = "Courses and programs from the world's best universities in your pocket.";
"STARTUP.SEARCH_TITLE" = "What do you want to learn?";
"STARTUP.SEARCH_PLACEHOLDER" = "Search our 3000+ courses";
"STARTUP.EXPLORE_ALL_COURSES" = "Explore all courses";
Loading