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

Clean up fundraising config calls #4846

Merged
merged 6 commits into from
May 15, 2024
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -486,7 +486,7 @@ extension WKDonateViewModel: PKPaymentAuthorizationControllerDelegate {

let paymentNetwork = payment.token.paymentMethod.network?.rawValue

let dataController = WKDonateDataController()
let dataController = WKDonateDataController.shared
dataController.submitPayment(amount: finalAmount, countryCode: countryCode, currencyCode: currencyCode, languageCode: languageCode, paymentToken: paymentToken, paymentNetwork: paymentNetwork, donorNameComponents: donorNameComponents, recurring: recurring, donorEmail: donorEmail, donorAddressComponents: donorAddressComponents, emailOptIn: emailOptIn, transactionFee: transactionFeeOptInViewModel.isSelected, bannerID: bannerID, appVersion: appVersion) { [weak self] result in

guard let self else {
Expand Down
7 changes: 4 additions & 3 deletions Components/Tests/ComponentsTests/WKDonateViewModelTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@ final class WKDonateViewModelTests: XCTestCase {
WKDataEnvironment.current.basicService = WKMockBasicService()
WKDataEnvironment.current.serviceEnvironment = .staging

let controller = WKDonateDataController()
let controller = WKDonateDataController.shared

controller.fetchConfigs(for: "US") { result in
switch result {
case .success:
self.paymentMethods = controller.paymentMethods
self.donateConfig = controller.donateConfig
let data = controller.loadConfigs()
self.paymentMethods = data.paymentMethods
self.donateConfig = data.donateConfig
completion(nil)
case .failure(let error):
completion(error)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,43 +5,65 @@ import Contacts

// MARK: - Properties

private let service = WKDataEnvironment.current.basicService
private let sharedCacheStore = WKDataEnvironment.current.sharedCacheStore
var service: WKService?
var sharedCacheStore: WKKeyValueStore?

private(set) var donateConfig: WKDonateConfig?
private(set) var paymentMethods: WKPaymentMethods?
private var donateConfig: WKDonateConfig?
private var paymentMethods: WKPaymentMethods?

private let cacheDirectoryName = WKSharedCacheDirectoryNames.donorExperience.rawValue
private let cacheDonateConfigFileName = "AppsDonationConfig"
private let cachePaymentMethodsFileName = "PaymentMethods"
private let cacheDonateConfigContainerFileName = "AppsDonationConfig"
private let cachePaymentMethodsResponseFileName = "PaymentMethods"

// MARK: - Lifecycle

public override init() {

}
@objc(sharedInstance)
public static let shared = WKDonateDataController()

private init(service: WKService? = WKDataEnvironment.current.basicService, sharedCacheStore: WKKeyValueStore? = WKDataEnvironment.current.sharedCacheStore) {
self.service = service
self.sharedCacheStore = sharedCacheStore
}

// MARK: - Public

public func loadConfigs() -> (donateConfig: WKDonateConfig?, paymentMethods: WKPaymentMethods?) {

// First pull from memory
guard donateConfig == nil,
paymentMethods == nil else {
return (donateConfig, paymentMethods)
}

let donateConfigResponse: WKDonateConfigResponse? = try? sharedCacheStore?.load(key: cacheDirectoryName, cacheDonateConfigFileName)
let paymentMethodsResponse: WKPaymentMethods? = try? sharedCacheStore?.load(key: cacheDirectoryName, cachePaymentMethodsFileName)
// Fall back to persisted objects if within seven days
let donateConfig: WKDonateConfig? = try? sharedCacheStore?.load(key: cacheDirectoryName, cacheDonateConfigContainerFileName)
let paymentMethods: WKPaymentMethods? = try? sharedCacheStore?.load(key: cacheDirectoryName, cachePaymentMethodsResponseFileName)

guard let donateConfigCachedDate = donateConfig?.cachedDate,
let paymentMethodsCachedDate = paymentMethods?.cachedDate else {
return (nil, nil)
}

let sevenDays = TimeInterval(60 * 60 * 24 * 7)
guard (-donateConfigCachedDate.timeIntervalSinceNow) < sevenDays,
(-paymentMethodsCachedDate.timeIntervalSinceNow) < sevenDays else {
return (nil, nil)
}

donateConfig = donateConfigResponse?.config
paymentMethods = paymentMethodsResponse
self.donateConfig = donateConfig
self.paymentMethods = paymentMethods

return (donateConfig, paymentMethods)
return (self.donateConfig, self.paymentMethods)
}

@objc public func fetchConfigs(countryCode: String) {
@objc public func fetchConfigsForCountryCode(_ countryCode: String, completion: @escaping (Error?) -> Void) {
fetchConfigs(for: countryCode) { result in

switch result {
case .success:
completion(nil)
case .failure(let error):
completion(error)
}
}
}

Expand Down Expand Up @@ -72,53 +94,66 @@ import Contacts

var errors: [Error] = []

var donateConfig: WKDonateConfig?
var paymentMethods: WKPaymentMethods?

group.enter()
let paymentMethodsRequest = WKBasicServiceRequest(url: paymentMethodsURL, method: .GET, parameters: paymentMethodParameters, acceptType: .json)
service.performDecodableGET(request: paymentMethodsRequest) { [weak self] (result: Result<WKPaymentMethods, Error>) in
service.performDecodableGET(request: paymentMethodsRequest) { (result: Result<WKPaymentMethods, Error>) in
defer {
group.leave()
}

guard let self else {
return
}

switch result {
case .success(let paymentMethods):
self.paymentMethods = paymentMethods
try? self.sharedCacheStore?.save(key: cacheDirectoryName, cachePaymentMethodsFileName, value: paymentMethods)
case .success(let response):
paymentMethods = response
case .failure(let error):
errors.append(error)
}
}

group.enter()
let donateConfigRequest = WKBasicServiceRequest(url: donateConfigURL, method: .GET, parameters: donateConfigParameters, acceptType: .json)
service.performDecodableGET(request: donateConfigRequest) { [weak self] (result: Result<WKDonateConfigResponse, Error>) in
service.performDecodableGET(request: donateConfigRequest) { (result: Result<WKDonateConfigResponse, Error>) in

defer {
group.leave()
}

guard let self else {
return
}

switch result {
case .success(let response):
self.donateConfig = response.config
try? self.sharedCacheStore?.save(key: cacheDirectoryName, cacheDonateConfigFileName, value: response)
donateConfig = response.config
case .failure(let error):
errors.append(error)
}
}

group.notify(queue: .main) {

if let firstError = errors.first {
self.donateConfig = nil
self.paymentMethods = nil
completion(.failure(firstError))
return
}

guard var donateConfig,
var paymentMethods else {
self.donateConfig = nil
self.paymentMethods = nil
completion(.failure(WKServiceError.unexpectedResponse))
return
}

donateConfig.cachedDate = Date()
paymentMethods.cachedDate = Date()

self.donateConfig = donateConfig
self.paymentMethods = paymentMethods

try? self.sharedCacheStore?.save(key: self.cacheDirectoryName, self.cacheDonateConfigContainerFileName, value: donateConfig)
try? self.sharedCacheStore?.save(key: self.cacheDirectoryName, self.cachePaymentMethodsResponseFileName, value: paymentMethods)

completion(.success(()))
}
}
Expand Down Expand Up @@ -192,4 +227,11 @@ import Contacts
}
})
}

// MARK: - Internal

func reset() {
donateConfig = nil
paymentMethods = nil
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ public struct WKDonateConfig: Codable {
public let currencyAmountPresets: [String: [Decimal]]
public let currencyTransactionFees: [String: Decimal]
public let countryCodeEmailOptInRequired: [String]
var cachedDate: Date?

public func transactionFee(for currencyCode: String) -> Decimal? {
return currencyTransactionFees[currencyCode] ?? currencyTransactionFees["default"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Foundation
public struct WKDonateConfigResponse: Codable {

static var currentVersion = 1
let config: WKDonateConfig
var config: WKDonateConfig

public init(from decoder: Decoder) throws {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ public struct WKPaymentMethods: Codable {
}

private let response: Response
var cachedDate: Date?

public var applePayPaymentNetworks: [PKPaymentNetwork] {
let brands = self.response.paymentMethods.first { $0.type == "applepay" }?.brands ?? []

Expand Down
26 changes: 14 additions & 12 deletions WKData/Tests/WKDataTests/WKDonateDataControllerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,19 @@ import Contacts
@testable import WKDataMocks

final class WKDonateDataControllerTests: XCTestCase {

private let controller: WKDonateDataController = WKDonateDataController.shared

override func setUp() async throws {
WKDataEnvironment.current.basicService = WKMockBasicService()
WKDataEnvironment.current.serviceEnvironment = .staging
WKDataEnvironment.current.sharedCacheStore = WKMockKeyValueStore()
self.controller.reset()
self.controller.service = WKDataEnvironment.current.basicService
self.controller.sharedCacheStore = WKDataEnvironment.current.sharedCacheStore
}

func testFetchDonateConfig() {
let controller = WKDonateDataController()

let expectation = XCTestExpectation(description: "Fetch Donate Configs")

Expand All @@ -29,7 +33,7 @@ final class WKDonateDataControllerTests: XCTestCase {

wait(for: [expectation], timeout: 10.0)

let donateData = WKDonateDataController().loadConfigs()
let donateData = controller.loadConfigs()

let paymentMethods = donateData.paymentMethods
let donateConfig = donateData.donateConfig
Expand All @@ -53,7 +57,6 @@ final class WKDonateDataControllerTests: XCTestCase {
}

func testDonateSubmitPayment() {
let controller = WKDonateDataController()

let expectation = XCTestExpectation(description: "Submit Payment")

Expand All @@ -76,7 +79,7 @@ final class WKDonateDataControllerTests: XCTestCase {

func testFetchDonateConfigWithNoCacheAndNoInternetConnection() {
WKDataEnvironment.current.basicService = WKMockServiceNoInternetConnection()
let controller = WKDonateDataController()
controller.service = WKDataEnvironment.current.basicService

let expectation = XCTestExpectation(description: "Fetch Donate Configs")

Expand All @@ -95,7 +98,7 @@ final class WKDonateDataControllerTests: XCTestCase {

wait(for: [expectation], timeout: 10.0)

let donateData = WKDonateDataController().loadConfigs()
let donateData = controller.loadConfigs()

let paymentMethods = donateData.paymentMethods
let donateConfig = donateData.donateConfig
Expand All @@ -115,22 +118,21 @@ final class WKDonateDataControllerTests: XCTestCase {
var notConnectedDonateConfig: WKDonateConfig?

// First fetch successfully to populate cache
let connectedController = WKDonateDataController()
connectedController.fetchConfigs(for: "US") { result in
controller.fetchConfigs(for: "US") { result in
switch result {
case .success:

let donateData = WKDonateDataController().loadConfigs()
let donateData = self.controller.loadConfigs()

connectedPaymentMethods = donateData.paymentMethods
connectedDonateConfig = donateData.donateConfig

// Drop Internet Connection
WKDataEnvironment.current.basicService = WKMockServiceNoInternetConnection()
let disconnectedController = WKDonateDataController()
self.controller.service = WKDataEnvironment.current.basicService

// Fetch again
disconnectedController.fetchConfigs(for: "US") { result in
self.controller.fetchConfigs(for: "US") { result in
switch result {
case .success:

Expand All @@ -139,7 +141,7 @@ final class WKDonateDataControllerTests: XCTestCase {
case .failure:

// Despite failure, we still expect to be able to load configs from cache
let donateData = disconnectedController.loadConfigs()
let donateData = self.controller.loadConfigs()
notConnectedPaymentMethods = donateData.paymentMethods
notConnectedDonateConfig = donateData.donateConfig

Expand All @@ -165,7 +167,7 @@ final class WKDonateDataControllerTests: XCTestCase {

func testDonateSubmitPaymentNoInternetConnection() {
WKDataEnvironment.current.basicService = WKMockServiceNoInternetConnection()
let controller = WKDonateDataController()
controller.service = WKDataEnvironment.current.basicService

let expectation = XCTestExpectation(description: "Submit Payment")

Expand Down
8 changes: 4 additions & 4 deletions Wikipedia/Code/ArticleViewController+Announcements.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ extension ArticleViewController {

let shouldShowMaybeLater = dataController.showShowMaybeLaterOption(asset: asset, currentDate: Date())

wmf_showFundraisingAnnouncement(theme: theme, asset: asset, primaryButtonTapHandler: { sender in
wmf_showFundraisingAnnouncement(theme: theme, asset: asset, primaryButtonTapHandler: { button, _ in

AppInteractionFunnel.shared.logFundraisingCampaignModalDidTapDonate(project: project)
self.pushToDonateForm(asset: asset, sourceView: sender as? UIButton)
self.pushToDonateForm(asset: asset, sourceView: button)
dataController.markAssetAsPermanentlyHidden(asset: asset)

}, secondaryButtonTapHandler: { sender in
}, secondaryButtonTapHandler: { _, _ in
AppInteractionFunnel.shared.logFundraisingCampaignModalDidTapMaybeLater(project: project)


Expand All @@ -44,7 +44,7 @@ extension ArticleViewController {
dataController.markAssetAsPermanentlyHidden(asset: asset)
}

}, optionalButtonTapHandler: { sender in
}, optionalButtonTapHandler: { _, _ in
AppInteractionFunnel.shared.logFundraisingCampaignModalDidTapAlreadyDonated(project: project)
self.donateAlreadyDonated()
dataController.markAssetAsPermanentlyHidden(asset: asset)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ extension ArticleViewController {
vcToPresentSurvey = presentedNavVC.viewControllers.count == 1 ? livingDocVC : nil
}

vcToPresentSurvey?.wmf_showAnnouncementPanel(announcement: surveyAnnouncementResult.announcement, style: .minimal, primaryButtonTapHandler: { (sender) in
vcToPresentSurvey?.wmf_showAnnouncementPanel(announcement: surveyAnnouncementResult.announcement, style: .minimal, primaryButtonTapHandler: { _, _ in
self.navigate(to: actionURL, useSafari: true)
// dismiss handler is called
}, secondaryButtonTapHandler: { (sender) in
}, secondaryButtonTapHandler: { _, _ in
// dismiss handler is called
}, footerLinkAction: { (url) in
self.navigate(to: url, useSafari: true)
Expand Down
2 changes: 1 addition & 1 deletion Wikipedia/Code/DescriptionEditViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ protocol DescriptionEditViewControllerDelegate: AnyObject {
return
}

wmf_showAbuseFilterWarningPanel(messageHtml: error.messageHtml, linkBaseURL: error.linkBaseURL, currentTitle: currentTitle, theme: theme, goBackIsOnlyDismiss: true, publishAnywayTapHandler: { [weak self] _ in
wmf_showAbuseFilterWarningPanel(messageHtml: error.messageHtml, linkBaseURL: error.linkBaseURL, currentTitle: currentTitle, theme: theme, goBackIsOnlyDismiss: true, publishAnywayTapHandler: { [weak self] _, _ in

self?.dismiss(animated: true) {
self?.save()
Expand Down
2 changes: 1 addition & 1 deletion Wikipedia/Code/DiffContainerViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -992,7 +992,7 @@ private extension DiffContainerViewController {
if UserDefaults.standard.bool(forKey: key) {
return
}
let panelVC = DiffEducationalPanelViewController(showCloseButton: false, primaryButtonTapHandler: { [weak self] (action) in
let panelVC = DiffEducationalPanelViewController(showCloseButton: false, primaryButtonTapHandler: { [weak self] _, _ in
self?.presentedViewController?.dismiss(animated: true)
}, secondaryButtonTapHandler: nil, dismissHandler: nil, discardDismissHandlerOnPrimaryButtonTap: true, theme: theme)
present(panelVC, animated: true)
Expand Down
2 changes: 1 addition & 1 deletion Wikipedia/Code/EditSaveViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,7 @@ class EditSaveViewController: WMFScrollViewController, Themeable, UITextFieldDel
mode = .abuseFilterWarning
abuseFilterCode = displayError.code

wmf_showAbuseFilterWarningPanel(messageHtml: displayError.messageHtml, linkBaseURL: displayError.linkBaseURL, currentTitle: currentTitle, theme: theme, goBackIsOnlyDismiss: false, publishAnywayTapHandler: { [weak self] _ in
wmf_showAbuseFilterWarningPanel(messageHtml: displayError.messageHtml, linkBaseURL: displayError.linkBaseURL, currentTitle: currentTitle, theme: theme, goBackIsOnlyDismiss: false, publishAnywayTapHandler: { [weak self] _, _ in

self?.dismiss(animated: true) {
self?.save()
Expand Down