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

EULA link and checkbox for "send marketing messages" #273

Merged
merged 10 commits into from
Feb 6, 2024
70 changes: 58 additions & 12 deletions Authorization/Authorization/Presentation/Base/FieldsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import SwiftUI
import Core
import Theme

struct FieldsView: View {

Expand All @@ -17,7 +18,8 @@ struct FieldsView: View {
let proxy: GeometryProxy
@Environment(\.colorScheme) var colorScheme
@State private var text: String = ""

@State private var sendMarketing: Bool = true

var body: some View {
ForEach(0..<fields.count, id: \.self) { index in
let config = fields[index]
Expand Down Expand Up @@ -54,23 +56,67 @@ struct FieldsView: View {
case .checkbox:
EmptyView()
.id(index)
Text("Checkbox is not support")
case .plaintext:
HTMLFormattedText(
cssInjector.injectCSS(
colorScheme: colorScheme,
html: config.field.label,
type: .discovery,
fontSize: 90, screenWidth: proxy.size.width)
)
.id(UUID())
.padding(.horizontal, -6)

plaintext(config: config)
case .unknown:
Text("This field not support")
}
}
}

@ViewBuilder
private func plaintext(config: FieldConfiguration) -> some View {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the config parameter name is conflicting with self.config. How about renaming it to fieldConfig?

if config.field.isHonorCode,
let eulaURL = self.config.agreement.eulaURL,
let tosURL = self.config.agreement.tosURL,
let policy = self.config.agreement.privacyPolicyURL {
let text = AuthLocalization.SignUp.agreement(
"\(self.config.platformName)",
eulaURL,
"\(self.config.platformName)",
tosURL,
policy
)
let checkBox = fields.first(where: { $0.field.type == .checkbox })
checkBox.flatMap { _ in
CheckBoxView(
checked: $sendMarketing,
text: AuthLocalization.SignUp.marketingEmailTitle("\(self.config.platformName)"),
font: Theme.Fonts.labelSmall
)
.padding(.vertical, 10)
.onAppear {
checkBox?.text = "\(sendMarketing)"
}
.onChange(of: sendMarketing) { newValue in
checkBox?.text = "\(newValue)"
Comment on lines +89 to +93
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason to assign boolean text to text of the checkbox? I've commented the code and it was working fine.

}
}
Text(.init(text))
.tint(Theme.Colors.accentColor)
.foregroundStyle(Theme.Colors.textSecondary)
.font(Theme.Fonts.labelSmall)
.padding(.vertical, 3)
.id(UUID())
.environment(\.openURL, OpenURLAction(handler: handleURL))
Divider()
} else {
HTMLFormattedText(
cssInjector.injectCSS(
colorScheme: colorScheme,
html: config.field.label,
type: .discovery,
fontSize: 90, screenWidth: proxy.size.width)
)
.id(UUID())
.padding(.horizontal, -6)
}
}

private func handleURL(_ url: URL) -> OpenURLAction.Result {
router.showWebBrowser(title: url.host ?? "", url: url)
return .handled
}
}

#if DEBUG
Expand Down
28 changes: 28 additions & 0 deletions Authorization/Authorization/Presentation/Login/SignInView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ public struct SignInView: View {
}
)
}
agreements
Spacer()
}
.padding(.horizontal, 24)
Expand Down Expand Up @@ -198,6 +199,33 @@ public struct SignInView: View {
.ignoresSafeArea(.all, edges: .horizontal)
.background(Theme.Colors.background.ignoresSafeArea(.all))
}

@ViewBuilder
private var agreements: some View {
if let eulaURL = viewModel.config.agreement.eulaURL,
let tosURL = viewModel.config.agreement.tosURL,
let policy = viewModel.config.agreement.privacyPolicyURL {
let text = AuthLocalization.SignIn.agreement(
"\(viewModel.config.platformName)",
eulaURL,
"\(viewModel.config.platformName)",
tosURL,
policy
)
Text(.init(text))
.tint(Theme.Colors.accentColor)
.foregroundStyle(Theme.Colors.textSecondary)
.font(Theme.Fonts.labelSmall)
.padding(.top, viewModel.socialAuthEnabled ? 0 : 15)
.padding(.bottom, 15)
.environment(\.openURL, OpenURLAction(handler: handleURL))
}
}

private func handleURL(_ url: URL) -> OpenURLAction.Result {
viewModel.router.showWebBrowser(title: url.host ?? "", url: url)
return .handled
}
}

#if DEBUG
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,30 +86,47 @@ public struct SignUpView: View {
.accessibilityIdentifier("social_auth_success_subtext_text")
}

let requiredFields = viewModel.fields.filter {$0.field.required}
let nonRequiredFields = viewModel.fields.filter {!$0.field.required}

FieldsView(fields: requiredFields,
router: viewModel.router,
config: viewModel.config,
cssInjector: viewModel.cssInjector,
proxy: proxy)

let requiredFields = viewModel.requiredFields
let nonRequiredFields = viewModel.nonRequiredFields

FieldsView(
fields: requiredFields,
router: viewModel.router,
config: viewModel.config,
cssInjector: viewModel.cssInjector,
proxy: proxy
)

if !viewModel.isShowProgress {
DisclosureGroup(isExpanded: $disclosureGroupOpen, content: {
FieldsView(fields: nonRequiredFields,
DisclosureGroup(isExpanded: $disclosureGroupOpen) {
FieldsView(
fields: nonRequiredFields,
router: viewModel.router,
config: viewModel.config,
cssInjector: viewModel.cssInjector,
proxy: proxy).padding(.horizontal, 1)
}, label: {
proxy: proxy
)
.padding(.horizontal, 1)
} label: {
Text(disclosureGroupOpen
? AuthLocalization.SignUp.hideFields
: AuthLocalization.SignUp.showFields)
})
}
.accessibilityLabel("optional_fields_text")
.padding(.top, 10)
}


FieldsView(
fields: viewModel.agreementsFields,
router: viewModel.router,
config: viewModel.config,
cssInjector: viewModel.cssInjector,
proxy: proxy
)
.transaction { transaction in
transaction.animation = nil
}

if viewModel.isShowProgress {
HStack(alignment: .center) {
ProgressBar(size: 40, lineWidth: 8)
Expand All @@ -124,7 +141,7 @@ public struct SignUpView: View {
}
viewModel.trackCreateAccountClicked()
}
.padding(.top, 40)
.padding(.top, 30)
.frame(maxWidth: .infinity)
.accessibilityLabel("signup_button")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,32 @@ public class SignUpViewModel: ObservableObject {
}

@Published var fields: [FieldConfiguration] = []

var requiredFields: [FieldConfiguration] {
fields
.filter {
$0.field.required &&
!$0.field.isHonorCode &&
$0.field.type != .checkbox
}
Copy link
Contributor

@saeedbashir saeedbashir Feb 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please format it like

fields.filter {
            $0.field.required &&
            !$0.field.isHonorCode &&
            $0.field.type != .checkbox
        }

because at other places the same pattern is being used in this file.

}
var agreementsFields: [FieldConfiguration] {
fields.filter {
$0.field.isHonorCode ||
$0.field.type == .checkbox
}
}
var nonRequiredFields: [FieldConfiguration] {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about renaming it to optionalFields?

fields.filter { !$0.field.required }
}

let router: AuthorizationRouter
let config: ConfigProtocol
let cssInjector: CSSInjector

private let interactor: AuthInteractorProtocol
private let analytics: AuthorizationAnalytics
private let validator: Validator

public init(
interactor: AuthInteractorProtocol,
router: AuthorizationRouter,
Expand Down
13 changes: 13 additions & 0 deletions Authorization/Authorization/SwiftGen/Strings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ public enum AuthLocalization {
public static let title = AuthLocalization.tr("Localizable", "FORGOT.TITLE", fallback: "Forgot password")
}
public enum SignIn {
/// By signing in to this app, you agree to the [%@ End User License Agreement](%@) and [%@ Terms of Service and Honor Code](%@) and vou acknowledge that edX and each Member process your personal data in
/// accordance with the [Privacy Policy.](%@)
public static func agreement(_ p1: Any, _ p2: Any, _ p3: Any, _ p4: Any, _ p5: Any) -> String {
return AuthLocalization.tr("Localizable", "SIGN_IN.AGREEMENT", String(describing: p1), String(describing: p2), String(describing: p3), String(describing: p4), String(describing: p5), fallback: "By signing in to this app, you agree to the [%@ End User License Agreement](%@) and [%@ Terms of Service and Honor Code](%@) and vou acknowledge that edX and each Member process your personal data in\naccordance with the [Privacy Policy.](%@)")
}
/// Email
public static let email = AuthLocalization.tr("Localizable", "SIGN_IN.EMAIL", fallback: "Email")
/// Email or username
Expand All @@ -68,10 +73,18 @@ public enum AuthLocalization {
public static let welcomeBack = AuthLocalization.tr("Localizable", "SIGN_IN.WELCOME_BACK", fallback: "Welcome back! Please authorize to continue.")
}
public enum SignUp {
/// By creating an account, you agree to the [%@ End User License Agreement](%@) and [%@ Terms of Service and Honor Code](%@) and vou acknowledge that edX and each Member process your personal data inaccordance with the [Privacy Policy.](%@)
public static func agreement(_ p1: Any, _ p2: Any, _ p3: Any, _ p4: Any, _ p5: Any) -> String {
return AuthLocalization.tr("Localizable", "SIGN_UP.AGREEMENT", String(describing: p1), String(describing: p2), String(describing: p3), String(describing: p4), String(describing: p5), fallback: "By creating an account, you agree to the [%@ End User License Agreement](%@) and [%@ Terms of Service and Honor Code](%@) and vou acknowledge that edX and each Member process your personal data inaccordance with the [Privacy Policy.](%@)")
}
/// Create account
public static let createAccountBtn = AuthLocalization.tr("Localizable", "SIGN_UP.CREATE_ACCOUNT_BTN", fallback: "Create account")
/// Hide optional Fields
public static let hideFields = AuthLocalization.tr("Localizable", "SIGN_UP.HIDE_FIELDS", fallback: "Hide optional Fields")
/// I agree that %@ may send me marketing messages.
public static func marketingEmailTitle(_ p1: Any) -> String {
return AuthLocalization.tr("Localizable", "SIGN_UP.MARKETING_EMAIL_TITLE", String(describing: p1), fallback: "I agree that %@ may send me marketing messages.")
}
/// Show optional Fields
public static let showFields = AuthLocalization.tr("Localizable", "SIGN_UP.SHOW_FIELDS", fallback: "Show optional Fields")
/// Create new account.
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 @@ -12,6 +12,9 @@
"SIGN_IN.EMAIL_OR_USERNAME" = "Email or username";
"SIGN_IN.PASSWORD" = "Password";
"SIGN_IN.FORGOT_PASS_BTN" = "Forgot password?";
"SIGN_IN.AGREEMENT" = "By signing in to this app, you agree to the [%@ End User License Agreement](%@) and [%@ Terms of Service and Honor Code](%@) and vou acknowledge that edX and each Member process your personal data in
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type vou it should be you and edX should be replaced platform name from config.

accordance with the [Privacy Policy.](%@)";


"ERROR.INVALID_EMAIL_ADDRESS" = "Invalid email address";
"ERROR.INVALID_PASSWORD_LENGHT" = "Invalid password lenght";
Expand All @@ -26,6 +29,8 @@
"SIGN_UP.SHOW_FIELDS" = "Show optional Fields";
"SIGN_UP.SUCCESS_SIGNIN_LABEL" = "You've successfully signed in.";
"SIGN_UP.SUCCESS_SIGNIN_SUBLABEL" = "We just need a little more information before you start learning.";
"SIGN_UP.AGREEMENT" = "By creating an account, you agree to the [%@ End User License Agreement](%@) and [%@ Terms of Service and Honor Code](%@) and vou acknowledge that edX and each Member process your personal data inaccordance with the [Privacy Policy.](%@)";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"SIGN_UP.MARKETING_EMAIL_TITLE" = "I agree that %@ may send me marketing messages.";

"FORGOT.TITLE"= "Forgot password";
"FORGOT.DESCRIPTION" = "Please enter your log-in or recovery email address below and we will send you an email with instructions.";
Expand Down
4 changes: 4 additions & 0 deletions Authorization/Authorization/uk.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
"SIGN_IN.EMAIL" = "Пошта";
"SIGN_IN.PASSWORD" = "Пароль";
"SIGN_IN.FORGOT_PASS_BTN" = "Забули пароль?";
"SIGN_IN.AGREEMENT" = "By signing in to this app, you agree to the [%@ End User License Agreement](%@) and [%@ Terms of Service and Honor Code](%@) and vou acknowledge that edX and each Member process your personal data in
accordance with the [Privacy Policy.](%@)";

"ERROR.INVALID_EMAIL_ADDRESS" = "невірна адреса електронної пошти";
"ERROR.INVALID_PASSWORD_LENGHT" = "Пароль занадто короткий або занадто довгий";
Expand All @@ -24,6 +26,8 @@
"SIGN_UP.SHOW_FIELDS" = "Показати необовʼязкові поля";
"SIGN_UP.SUCCESS_SIGNIN_LABEL" = "You've successfully signed in.";
"SIGN_UP.SUCCESS_SIGNIN_SUBLABEL" = "We just need a little more information before you start learning.";
"SIGN_UP.AGREEMENT" = "By creating an account, you agree to the [%@ End User License Agreement](%@) and [%@ Terms of Service and Honor Code](%@) and vou acknowledge that edX and each Member process your personal data inaccordance with the [Privacy Policy.](%@)";
"SIGN_UP.MARKETING_EMAIL_TITLE" = "I agree that %@ may send me marketing messages.";

"FORGOT.TITLE"= "Відновлення паролю";
"FORGOT.DESCRIPTION" = "Будь ласка, введіть свою адресу електронної пошти для входу або відновлення нижче, і ми надішлемо вам електронний лист з інструкціями.";
Expand Down
Loading
Loading