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

Pp 628 implement default and custom bottom bar navigation #574

Merged
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
cdc971f
feat(GiniBankSDK): add default bottom bar to Skonto
mrkulik Jul 3, 2024
7c3e611
Merge branch 'PP-51-Delivery-Skonto-iOS' into PP-628-Implement-defaul…
mrkulik Jul 10, 2024
5cb577c
feat(GiniBankSDK): apply new formatting for Skonto bottom bar
mrkulik Jul 10, 2024
99282ef
feat(GiniCaptureSDK): add setContentCompressionResistancePriority & s…
mrkulik Jul 10, 2024
9f1eac7
feat(GiniBankSDK): add stackView to layout bottom navigation bar buttons
mrkulik Jul 10, 2024
43f1708
feat(GiniBankSDK): remove Trailing Whitespace for bottom bar view
mrkulik Jul 10, 2024
813d353
feat(GiniBankSDK): add back button action. Make components private an…
mrkulik Jul 10, 2024
08530ca
Merge branch 'PP-51-Delivery-Skonto-iOS' into PP-628-Implement-defaul…
mrkulik Jul 10, 2024
918ebb6
feat(GiniBankSDK): remove extra back button when bottom navigation is on
mrkulik Jul 11, 2024
772242f
feat(GiniBankSDK): update localization strings
mrkulik Jul 11, 2024
a07db01
feat(GiniBankSDK): change bottom padding for custom navigation bar
mrkulik Jul 11, 2024
a2f1b52
feat(GiniBankSDKExample): CustomSkontoBottomNavigationBar & Adapter
mrkulik Jul 11, 2024
12eba70
Merge branch 'PP-51-Delivery-Skonto-iOS' into PP-628-Implement-defaul…
mrkulik Jul 12, 2024
b43985d
feat(GiniBankSDK): Temporary remove help action for bottom navigation
mrkulik Jul 12, 2024
fa3302d
feat(GiniBankSDKExample): Temporary remove help action for custom bot…
mrkulik Jul 12, 2024
320436d
feat(GiniBankSDKExample): remove updateProceedButtonState from bottom…
mrkulik Jul 12, 2024
3f4ee9c
feat(GiniBankSDKExample): add tests for Skonto bottom bar switch option
mrkulik Jul 12, 2024
6ebd20f
feat(GiniBankSDKExample): remove unused methods for proceed button fr…
mrkulik Jul 12, 2024
7dbdc5b
feat(GiniBankSDKExample): set numberOfLines for skonto label in custo…
mrkulik Jul 12, 2024
efd172b
feat(GiniBankSDK): set numberOfLines for skonto label in default bott…
mrkulik Jul 12, 2024
5d11d90
feat(GiniBankSDK): set correct price formatting for default bottom bar
mrkulik Jul 12, 2024
e7fff46
feat(GiniBankSDKExample): replace priceWithCurrencySymbol with priceW…
mrkulik Jul 12, 2024
56cda2a
feat(GiniBankSDK): add divider view to default bottom bar. Move corne…
mrkulik Jul 12, 2024
3271224
Merge branch 'PP-51-Delivery-Skonto-iOS' into PP-628-Implement-defaul…
mrkulik Jul 12, 2024
b167b6b
feat(GiniBankSDK): remove unneed method updateProceedButtonState & se…
mrkulik Jul 12, 2024
dc5a3a9
feat(GiniBankSDK): add accessibility values for default bottom bar
mrkulik Jul 12, 2024
680b4ff
feat(GiniBankSDK): update layout & constraints for default bottom nav…
mrkulik Jul 12, 2024
0c47b9e
feat(GiniBankSDK): change localization string comment for proceed but…
mrkulik Jul 12, 2024
9768e9c
feat(GiniBankSDK): update buttons UI for custom navigation bar. Add a…
mrkulik Jul 12, 2024
727a6f6
feat(GiniBankSDK): localizedDiscountString
mrkulik Jul 16, 2024
92ce65e
feat(GiniBankSDK): localizedDiscountString for proceed view
mrkulik Jul 16, 2024
f86fbf9
feat(GiniBankSDK): update proceed button layout for default bottom bar
mrkulik Jul 16, 2024
1cc78de
Merge branch 'PP-51-Delivery-Skonto-iOS' into PP-628-Implement-defaul…
mrkulik Jul 16, 2024
48f3b63
feat(GiniBankSDK): add TODO for adaptor
mrkulik Jul 16, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ struct Price {
return Price.stringWithoutSymbol(from: value)
}

var germanStringWithoutCurrencyCode: String? {
var localizedStringWithoutCurrencyCode: String? {
return Price.localizedStringWithoutCurrencyCode(from: value)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
//
// DefaultSkontoBottomNavigationBar.swift
//
// Copyright © 2024 Gini GmbH. All rights reserved.
//

import UIKit
import GiniCaptureSDK

final class DefaultSkontoBottomNavigationBar: UIView {
private lazy var configuration = GiniBankConfiguration.shared

private lazy var proceedButton: MultilineTitleButton = {
let button = MultilineTitleButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.configure(with: configuration.primaryButtonConfiguration)
button.titleLabel?.font = configuration.textStyleFonts[.bodyBold]
let title = NSLocalizedStringPreferredGiniBankFormat("ginibank.skonto.paybutton.title",
comment: "Continue to pay")
button.setTitle(title, for: .normal)
button.accessibilityValue = title
button.setContentHuggingPriority(.defaultLow, for: .horizontal)
button.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
button.addTarget(self, action: #selector(proceedButtonClicked), for: .touchUpInside)
return button
}()

// MARK: Temporary remove help action
// private lazy var helpButton: GiniBarButton = {
// let button = GiniBarButton(ofType: .help)
// button.buttonView.translatesAutoresizingMaskIntoConstraints = false
// button.setContentHuggingPriority(.defaultHigh, for: .horizontal)
// button.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
// button.addAction(self, #selector(helpButtonClicked))
// return button
// }()

private lazy var backButton: GiniBarButton = {
let button = GiniBarButton(ofType: .back(title: ""))
button.buttonView.translatesAutoresizingMaskIntoConstraints = false
button.setContentHuggingPriority(.defaultHigh, for: .horizontal)
button.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
button.addAction(self, #selector(backButtonClicked))
return button
}()

private lazy var totalLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = configuration.textStyleFonts[.body]
label.textColor = .giniColorScheme().text.primary.uiColor()
label.adjustsFontForContentSizeCategory = true
label.setContentHuggingPriority(.required, for: .vertical)
let text = NSLocalizedStringPreferredGiniBankFormat("ginibank.skonto.total.title",
comment: "Total")
label.text = text
label.accessibilityValue = text
return label
}()

private lazy var totalValueLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.adjustsFontForContentSizeCategory = true
label.font = configuration.textStyleFonts[.title1Bold]
label.textColor = .giniColorScheme().text.primary.uiColor()
label.setContentHuggingPriority(.required, for: .vertical)
return label
}()

private lazy var skontoBadgeLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = configuration.textStyleFonts[.caption1]
label.textColor = .giniColorScheme().chips.textSuggestionEnabled.uiColor()
label.numberOfLines = 0
label.adjustsFontForContentSizeCategory = true
return label
}()

private lazy var skontoBadgeView: UIView = {
let view = UIView()
view.backgroundColor = .giniColorScheme().chips.suggestionEnabled.uiColor()
view.layer.cornerRadius = Constants.cornerRadius
view.layer.masksToBounds = true
view.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(skontoBadgeLabel)
return view
}()

private lazy var dividerView: UIView = {
let dividerView = UIView()
dividerView.backgroundColor = .giniColorScheme().bg.divider.uiColor()
dividerView.translatesAutoresizingMaskIntoConstraints = false
return dividerView
}()

private var proceedAction: (() -> Void)?
// MARK: Temporary remove help action
// private var helpAction: (() -> Void)?
private var backAction: (() -> Void)?

init(proceedAction: (() -> Void)?,
backAction: (() -> Void)?) {
self.proceedAction = proceedAction
self.backAction = backAction
super.init(frame: .zero)
setupView()
setupConstraints()
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

func updatePrice(with price: String?) {
totalValueLabel.text = price
totalValueLabel.accessibilityValue = price
}

func updateDiscountValue(with discount: String?) {
skontoBadgeLabel.text = discount
skontoBadgeLabel.accessibilityValue = discount
}

func updateDiscountBadge(enabled: Bool) {
skontoBadgeView.isHidden = !enabled
}

private func setupView() {
backgroundColor = .giniColorScheme().bg.surface.uiColor()

addSubview(proceedButton)
addSubview(totalLabel)
addSubview(totalValueLabel)
addSubview(skontoBadgeView)
addSubview(backButton.buttonView)
addSubview(dividerView)
skontoBadgeView.addSubview(skontoBadgeLabel)
}

private func setupConstraints() {
NSLayoutConstraint.activate([
dividerView.topAnchor.constraint(equalTo: topAnchor),
dividerView.leadingAnchor.constraint(equalTo: leadingAnchor),
dividerView.trailingAnchor.constraint(equalTo: trailingAnchor),
dividerView.heightAnchor.constraint(equalToConstant: Constants.dividerViewHeight),

totalLabel.topAnchor.constraint(equalTo: dividerView.bottomAnchor, constant: Constants.padding),
totalLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: Constants.padding),

totalValueLabel.topAnchor.constraint(equalTo: totalLabel.bottomAnchor),
totalValueLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: Constants.padding),

skontoBadgeView.centerYAnchor.constraint(equalTo: totalValueLabel.centerYAnchor),
skontoBadgeView.leadingAnchor.constraint(equalTo: totalValueLabel.trailingAnchor,
constant: Constants.badgeSpacing),
skontoBadgeView.trailingAnchor.constraint(lessThanOrEqualTo: trailingAnchor,
constant: -Constants.padding),

skontoBadgeLabel.topAnchor.constraint(equalTo: skontoBadgeView.topAnchor,
constant: Constants.badgeVerticalPadding),
skontoBadgeLabel.bottomAnchor.constraint(equalTo: skontoBadgeView.bottomAnchor,
constant: -Constants.badgeVerticalPadding),
skontoBadgeLabel.leadingAnchor.constraint(equalTo: skontoBadgeView.leadingAnchor,
constant: Constants.badgeHorizontalPadding),
skontoBadgeLabel.trailingAnchor.constraint(equalTo: skontoBadgeView.trailingAnchor,
constant: -Constants.badgeHorizontalPadding),

backButton.buttonView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: Constants.padding),
backButton.buttonView.centerYAnchor.constraint(equalTo: proceedButton.centerYAnchor),

proceedButton.topAnchor.constraint(equalTo: totalValueLabel.bottomAnchor,
constant: Constants.verticalPadding),
proceedButton.leadingAnchor.constraint(equalTo: backButton.buttonView.trailingAnchor),
proceedButton.centerXAnchor.constraint(equalTo: centerXAnchor),
proceedButton.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor,
constant: -Constants.verticalPadding),
proceedButton.heightAnchor.constraint(equalToConstant: Constants.payButtonHeight)
])
}

@objc private func proceedButtonClicked() {
proceedAction?()
}

// MARK: Temporary remove help action
// @objc private func helpButtonClicked() {
// helpAction?()
// }

@objc private func backButtonClicked() {
backAction?()
}
}

extension DefaultSkontoBottomNavigationBar {
private enum Constants {
static let padding: CGFloat = 24
static let verticalPadding: CGFloat = 16
static let payButtonHeight: CGFloat = 50
static let dividerViewHeight: CGFloat = 1
static let badgeHorizontalPadding: CGFloat = 6
static let badgeVerticalPadding: CGFloat = 2
static let badgeSpacing: CGFloat = 12
static let cornerRadius: CGFloat = 4
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//
// DefaultSkontoNavigationBarBottomAdapter.swift
//
// Copyright © 2024 Gini GmbH. All rights reserved.
//

import UIKit

final class DefaultSkontoNavigationBarBottomAdapter: SkontoNavigationBarBottomAdapter {

private var proceedButtonCallback: (() -> Void)?
// MARK: Temporary remove help action
// private var helpButtonCallback: (() -> Void)?
private var backButtonCallback: (() -> Void)?
private var view: DefaultSkontoBottomNavigationBar?

func setProceedButtonClickedActionCallback(_ callback: @escaping () -> Void) {
proceedButtonCallback = callback
}

// MARK: Temporary remove help action
// func setHelpButtonClickedActionCallback(_ callback: @escaping () -> Void) {
// helpButtonCallback = callback
// }

func setBackButtonClickedActionCallback(_ callback: @escaping () -> Void) {
backButtonCallback = callback
}

func updateTotalPrice(priceWithCurrencyCode price: String?) {
view?.updatePrice(with: price)
}

func updateDiscountValue(with discount: String?) {
view?.updateDiscountValue(with: discount)
}

func updateDiscountBadge(enabled: Bool) {
view?.updateDiscountBadge(enabled: enabled)
}

func injectedView() -> UIView {
let navigationBar = DefaultSkontoBottomNavigationBar(proceedAction: proceedButtonCallback,
backAction: backButtonCallback)
view = navigationBar
return navigationBar
}

func onDeinit() {
proceedButtonCallback = nil
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,7 @@ public protocol SkontoNavigationBarBottomAdapter: InjectedViewAdapter {
*
* - Parameter price: A string which contains the currency and the price
*/
func updateTotalPrice(priceWithCurrencySymbol price: String?)

/**
* Set the proceed button state. Called when state of the button should be changed
*
* - Parameter enabled: A bool value to reflect the state of the button
*/
func updateProceedButtonState(enabled: Bool)
func updateTotalPrice(priceWithCurrencyCode price: String?)

/**
* Set the discount value on the bottom navigation bar. Called when Skonto applies
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,22 +105,18 @@ public class SkontoViewController: UIViewController {
comment: "Back")
edgesForExtendedLayout = []
view.backgroundColor = .giniColorScheme().bg.background.uiColor()
if configuration.bottomNavigationBarEnabled {
let cancelButton = GiniBarButton(ofType: .back(title: backButtonTitle))
cancelButton.addAction(self, #selector(backButtonTapped))
navigationItem.rightBarButtonItem = cancelButton.barButton
navigationItem.hidesBackButton = true
} else {
if !configuration.bottomNavigationBarEnabled {
// MARK: Temporary remove help button
// let helpButton = GiniBarButton(ofType: .help)
// helpButton.addAction(self, #selector(helpButtonTapped))
// navigationItem.rightBarButtonItem = helpButton.barButton

let cancelButton = GiniBarButton(ofType: .back(title: backButtonTitle))
cancelButton.addAction(self, #selector(backButtonTapped))
navigationItem.leftBarButtonItem = cancelButton.barButton
let backButton = GiniBarButton(ofType: .back(title: backButtonTitle))
backButton.addAction(self, #selector(backButtonTapped))
navigationItem.leftBarButtonItem = backButton.barButton
} else {
navigationItem.hidesBackButton = true
}

view.addSubview(scrollView)
scrollView.addSubview(stackView)
stackView.addArrangedSubview(appliedGroupView)
Expand Down Expand Up @@ -229,11 +225,10 @@ public class SkontoViewController: UIViewController {

private func setupBottomNavigationBar() {
guard configuration.bottomNavigationBarEnabled else { return }

if let bottomBarAdapter = configuration.skontoNavigationBarBottomAdapter {
navigationBarBottomAdapter = bottomBarAdapter
} else {
// TODO: Implement default navigation bar when design will be available
navigationBarBottomAdapter = DefaultSkontoNavigationBarBottomAdapter()
}

navigationBarBottomAdapter?.setProceedButtonClickedActionCallback { [weak self] in
Expand All @@ -245,6 +240,10 @@ public class SkontoViewController: UIViewController {
// self?.helpButtonTapped()
// }

navigationBarBottomAdapter?.setBackButtonClickedActionCallback { [weak self] in
self?.backButtonTapped()
}

if let navigationBar = navigationBarBottomAdapter?.injectedView() {
bottomNavigationBar = navigationBar
view.addSubview(navigationBar)
Expand All @@ -262,11 +261,24 @@ public class SkontoViewController: UIViewController {
}

private func bindViewModel() {
configure()
viewModel.addStateChangeHandler { [weak self] in
guard let self else { return }
self.configure()
}
viewModel.endEditingAction = {
self.endEditing()
}
}

private func configure() {
let isSkontoApplied = viewModel.isSkontoApplied
navigationBarBottomAdapter?.updateDiscountBadge(enabled: isSkontoApplied)
navigationBarBottomAdapter?.updateDiscountValue(with: viewModel.localizedDiscountString)
let localizedStringWithCurrencyCode = viewModel.totalPrice.localizedStringWithCurrencyCode
navigationBarBottomAdapter?.updateTotalPrice(priceWithCurrencyCode: localizedStringWithCurrencyCode)
}

// MARK: Temporary remove help action
// @objc private func helpButtonTapped() {
// viewModel.helpButtonTapped()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ class SkontoViewModel {
}
}

var localizedDiscountString: String {
return String.localizedStringWithFormat(
NSLocalizedStringPreferredGiniBankFormat("ginibank.skonto.total.amount.skonto",
comment: "%@ Skonto discount"),
skontoFormattedPercentageDiscounted
)
}

weak var delegate: SkontoViewModelDelegate?

init(isSkontoApplied: Bool,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ class SkontoAmountView: UIView {
price: Price,
isEditable: Bool = true) {
self.titleLabelText = title
self.textFieldInitialText = price.germanStringWithoutCurrencyCode ?? ""
self.textFieldInitialText = price.localizedStringWithCurrencyCode ?? ""
self.currencyLabelText = price.currencyCode
self.isEditable = isEditable
super.init(frame: .zero)
Expand Down Expand Up @@ -124,7 +124,7 @@ class SkontoAmountView: UIView {

func configure(isEditable: Bool, price: Price) {
if isEditable {
textField.text = price.germanStringWithoutCurrencyCode ?? ""
textField.text = price.localizedStringWithoutCurrencyCode ?? ""
} else {
textField.text = price.localizedStringWithCurrencyCode ?? ""
}
Expand Down
Loading
Loading