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: Course Upgrade Option with IAP #445

Closed
wants to merge 63 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
a2c5be8
feat: initial setup for course upgrade
saeedbashir Apr 30, 2024
7790f82
chore: improvements
saeedbashir Apr 30, 2024
74641de
refactor: correct API calling
saeedbashir Apr 30, 2024
193d0bc
chore: execute api response parsing
saeedbashir Apr 30, 2024
a8287cf
chore: blocks API parsing for course upgrade
saeedbashir May 3, 2024
72dbd11
chore: parse course access in blocks API
saeedbashir May 7, 2024
3a09895
feat: upgrade button on my course screen
forgotvas May 7, 2024
c62d132
chore: added upgrade view
forgotvas May 8, 2024
0910561
chore: add course upgrade unlock view
saeedbashir May 8, 2024
c2653af
Merge branch 'develop' into saeed/courseUpgrade
saeedbashir May 8, 2024
cd321db
fix: resolve conflicts after merge
saeedbashir May 8, 2024
6a27ded
Merge branch 'saeed/courseUpgrade' into vkuznetsov/courseUpgradeUI
forgotvas May 8, 2024
8792c88
chore: removed spaces
forgotvas May 8, 2024
b5291f9
chore: refactor
forgotvas May 8, 2024
680960d
chore: refactor
forgotvas May 8, 2024
af5b03c
feat: added payment button in course container
forgotvas May 8, 2024
0104482
chore: added missed code
forgotvas May 8, 2024
a4bc8a2
chore: width
forgotvas May 8, 2024
a5e3345
chore: refactor
forgotvas May 9, 2024
34ef3a5
chore: refactor
forgotvas May 9, 2024
b7b9e10
chore: refactor
forgotvas May 10, 2024
2663e81
chore: refactor
forgotvas May 10, 2024
626c4c8
chore: refactor
forgotvas May 10, 2024
d93979c
chore: parse upgrade deadline in blocks API
saeedbashir May 14, 2024
000ce2b
chore: adding get help button workable in IAP
saeedbashir May 15, 2024
70b40a6
Merge branch 'saeed/courseUpgrade' into vkuznetsov/courseUpgradeUI
forgotvas May 17, 2024
f355fd9
chore: update colors
forgotvas May 20, 2024
384c0fc
chore: refactor
forgotvas May 22, 2024
1692763
chore: fixed error alert
forgotvas May 22, 2024
a4a1315
chore: removed useless comments
forgotvas May 22, 2024
708519b
chore: refactor
forgotvas May 22, 2024
6d16822
chore: refactor
forgotvas May 22, 2024
7a731f3
chore: warning fix
forgotvas May 22, 2024
3e4ff5b
chore: fix for loading view
forgotvas May 22, 2024
3f910fe
chore: fix tests
forgotvas May 22, 2024
a1e0fb3
fix: crash for analytics
forgotvas May 23, 2024
d9ac858
Merge pull request #1 from saeedbashir/vkuznetsov/courseUpgradeUI
saeedbashir May 24, 2024
a15bdac
chore: fix snackbar
forgotvas May 24, 2024
57ed3d3
chore: do not hide view when purchasing
forgotvas May 24, 2024
fee0e14
chore: removed debug methods
forgotvas May 24, 2024
5888c32
Merge branch 'saeed/courseUpgrade' into vkuznetsov/courseUpgradeUI
forgotvas May 24, 2024
6680349
chore: changed payment snackbar style
forgotvas May 27, 2024
ea17953
chore: refresh courses list when payment complete
forgotvas May 27, 2024
6ded0fd
chore: error handling improvements
saeedbashir May 28, 2024
7c1fd2c
chore: parse is_sel_paced and used in analytics
saeedbashir May 28, 2024
75aa280
chore: added some unit tests
forgotvas May 28, 2024
552af15
chore: added tests for router flow
forgotvas May 29, 2024
457e54e
Merge branch 'saeed/courseUpgrade' into vkuznetsov/courseUpgradeUI
forgotvas May 29, 2024
c0802fa
chore: merge conflict
forgotvas May 29, 2024
f49c998
chore: push core data changes for isselfpaced
saeedbashir May 29, 2024
db26730
Merge branch 'saeed/courseUpgrade' into vkuznetsov/courseUpgradeUI
forgotvas May 29, 2024
c357d1a
chore: fixed tests
forgotvas May 29, 2024
d27f974
chore: save in progress IAP info for unfulfilled case
saeedbashir May 29, 2024
70cbb2a
Merge pull request #2 from saeedbashir/vkuznetsov/courseUpgradeUI
saeedbashir May 29, 2024
ce4a6a0
chore: analytics improvements, error handling improvements, unfulfill…
saeedbashir May 30, 2024
b560a24
chore: add loader for unfulfilled case
saeedbashir May 31, 2024
970a0ed
chore: restore purchases implementation and other improvements
saeedbashir Jun 5, 2024
d6514fc
refactor: update flow and restore button color
saeedbashir Jun 6, 2024
800cd33
Merge branch 'develop' into saeed/courseUpgrade
saeedbashir Jun 7, 2024
646840f
chore: fix after develop merge
saeedbashir Jun 7, 2024
e6dc12c
Merge branch 'develop' into saeed/courseUpgrade
saeedbashir Jun 11, 2024
18ba99d
Vkuznetsov/course upgrade UI (#3)
forgotvas Jun 12, 2024
7d2e834
chore: add alert type in alert action analytics
saeedbashir Jun 20, 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
1,657 changes: 1,594 additions & 63 deletions Authorization/AuthorizationTests/AuthorizationMock.generated.swift

Large diffs are not rendered by default.

218 changes: 217 additions & 1 deletion Core/Core.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

156 changes: 156 additions & 0 deletions Core/Core/Analytics/CoreAnalytics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,72 @@ public protocol CoreAnalytics {
value: String,
oldValue: String
)

func trackCourseUpgradePaymentError(
_ event: AnalyticsEvent,
biValue: EventBIValue,
courseID: String,
blockID: String?,
pacing: String,
coursePrice: String,
screen: CourseUpgradeScreen,
error: String
)

func trackCourseUpgradeError(
courseID: String,
blockID: String?,
pacing: String,
coursePrice: String?,
screen: CourseUpgradeScreen,
error: String,
flowType: UpgradeMode
)

func trackCourseUpgradeErrorAction(
courseID: String,
blockID: String?,
pacing: String,
coursePrice: String?,
screen: CourseUpgradeScreen,
alertType: UpgradeAlertType,
errorAction: String,
error: String,
flowType: UpgradeMode
)

func trackCourseUpgradeSuccess(
courseID: String,
blockID: String?,
pacing: String,
coursePrice: String,
screen: CourseUpgradeScreen,
flowType: UpgradeMode
)

func trackUpgradeNow(
courseID: String,
blockID: String?,
pacing: String,
screen: CourseUpgradeScreen,
coursePrice: String
)

func trackCourseUpgradeLoadError(
courseID: String,
blockID: String?,
pacing: String,
screen: CourseUpgradeScreen
)

func trackCourseUnfulfilledPurchaseInitiated(
courseID: String,
pacing: String,
screen: CourseUpgradeScreen,
flowType: UpgradeMode
)

func trackRestorePurchaseClicked()
}

public extension CoreAnalytics {
Expand All @@ -42,6 +108,71 @@ public class CoreAnalyticsMock: CoreAnalytics {
value: String,
oldValue: String
) {}

public func trackCourseUpgradePaymentError(
_ event: AnalyticsEvent,
biValue: EventBIValue,
courseID: String,
blockID: String? = nil,
pacing: String,
coursePrice: String,
screen: CourseUpgradeScreen,
error: String
) {}

public func trackCourseUpgradeError(
courseID: String,
blockID: String? = nil,
pacing: String,
coursePrice: String? = nil,
screen: CourseUpgradeScreen,
error: String,
flowType: UpgradeMode
) {}

public func trackCourseUpgradeErrorAction(
courseID: String,
blockID: String? = nil,
pacing: String,
coursePrice: String? = nil,
screen: CourseUpgradeScreen,
alertType: UpgradeAlertType,
errorAction: String,
error: String,
flowType: UpgradeMode
) {}

public func trackCourseUpgradeSuccess(
courseID: String,
blockID: String? = nil,
pacing: String,
coursePrice: String,
screen: CourseUpgradeScreen,
flowType: UpgradeMode) {}

public func trackUpgradeNow(
courseID: String,
blockID: String? = nil,
pacing: String,
screen: CourseUpgradeScreen,
coursePrice: String
) {}

public func trackCourseUpgradeLoadError(
courseID: String,
blockID: String? = nil,
pacing: String,
screen: CourseUpgradeScreen
) {}

public func trackCourseUnfulfilledPurchaseInitiated(
courseID: String,
pacing: String,
screen: CourseUpgradeScreen,
flowType: UpgradeMode
) {}

public func trackRestorePurchaseClicked() {}
}
#endif

Expand Down Expand Up @@ -124,6 +255,16 @@ public enum AnalyticsEvent: String {
case whatnewPopup = "WhatsNew:Pop up Viewed"
case whatnewDone = "WhatsNew:Done"
case whatnewClose = "WhatsNew:Close"
case upgradeNowClicked = "Payments: Upgrade Now Clicked"
case courseUpgradePriceLoadError = "Payments: Price Load Error"
case courseUpgradePaymentError = "Payments: Payment Error"
case courseUpgradePaymentCancelError = "Payments: Canceled by User"
case courseUpgradeError = "Payments: Course Upgrade Error"
case courseUpgradeErrorAction = "Payments: Error Alert Action"
case courseUpgradeSuccess = "Payments: Course Upgrade Success"
case courseUpgradeUnfulfilledPurchaseInitiated = "Payments: Unfulfilled Purchase Initiated"
case courseUpgradeRestorePurchaseClicked = "Payments: Restore Purchases Clicked"

}

public enum EventBIValue: String {
Expand Down Expand Up @@ -205,6 +346,15 @@ public enum EventBIValue: String {
case whatnewPopup = "edx.bi.app.whats_new.popup.viewed"
case whatnewDone = "edx.bi.app.whats_new.done"
case whatnewClose = "edx.bi.app.whats_new.close"
case upgradeNowClicked = "edx.bi.app.payments.upgrade_now.clicked"
case courseUpgradePriceLoadError = "edx.bi.app.payments.price_load_error"
case courseUpgradePaymentError = "edx.bi.app.payments.payment_error"
case courseUpgradePaymentCancelError = "edx.bi.app.payments.canceled_by_user"
case courseUpgradeError = "edx.bi.app.payments.course_upgrade_error"
case courseUpgradeErrorAction = "edx.bi.app.payments.error_alert_action"
case courseUpgradeSuccess = "edx.bi.app.payments.course_upgrade_success"
case courseUpgradeUnfulfilledPurchaseInitiated = "edx.bi.app.payments.unfulfilled_purchase.initiated"
case courseUpgradeRestorePurchaseClicked = "edx.bi.app.payments.restore_purchases.clicked"
}

public struct EventParamKey {
Expand Down Expand Up @@ -241,6 +391,11 @@ public struct EventParamKey {
public static let pacing = "pacing"
public static let dialog = "dialog"
public static let snackbar = "snackbar"
public static let price = "price"
public static let error = "error"
public static let errorAction = "error_action"
public static let flowType = "flow_type"
public static let alertType = "alert_type"
}

public struct EventCategory {
Expand All @@ -251,4 +406,5 @@ public struct EventCategory {
public static let profile = "profile"
public static let video = "video"
public static let course = "course"
public static let inAppPurchases = "in_app_purchases"
}
6 changes: 6 additions & 0 deletions Core/Core/Assets.xcassets/UpgradeAccess/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "arrow.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true
}
}
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "calendar.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "checkmark.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
41 changes: 40 additions & 1 deletion Core/Core/Configuration/BaseRouter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,26 @@ public protocol BaseRouter {
func presentView(transitionStyle: UIModalTransitionStyle, view: any View, completion: (() -> Void)?)

func presentView(transitionStyle: UIModalTransitionStyle, animated: Bool, content: () -> any View)

func presentNativeAlert(title: String?, message: String?, actions: [UIAlertAction])
@MainActor
func showUpgradeInfo(
productName: String,
message: String,
sku: String,
courseID: String,
screen: CourseUpgradeScreen,
pacing: String
) async
@MainActor
func hideUpgradeInfo(animated: Bool) async
@MainActor
func showUpgradeLoaderView(animated: Bool) async
@MainActor
func hideUpgradeLoaderView(animated: Bool) async
@MainActor
func showRestoreProgressView()
@MainActor
func hideRestoreProgressView()
}

extension BaseRouter {
Expand Down Expand Up @@ -125,5 +144,25 @@ open class BaseRouterMock: BaseRouter {

public func presentView(transitionStyle: UIModalTransitionStyle, animated: Bool, content: () -> any View) {}

public func presentNativeAlert(title: String?, message: String?, actions: [UIAlertAction]) {}
@MainActor
public func showUpgradeInfo(
productName: String,
message: String,
sku: String,
courseID: String,
screen: CourseUpgradeScreen,
pacing: String
) async {}
@MainActor
public func hideUpgradeInfo(animated: Bool) async {}
@MainActor
public func showUpgradeLoaderView(animated: Bool) async {}
@MainActor
public func hideUpgradeLoaderView(animated: Bool) async {}
@MainActor
public func showRestoreProgressView() {}
@MainActor
public func hideRestoreProgressView() {}
}
#endif
6 changes: 6 additions & 0 deletions Core/Core/Configuration/Config/Config.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public protocol ConfigProtocol {
var segment: SegmentConfig { get }
var program: DiscoveryConfig { get }
var URIScheme: String { get }
var ecommerceURL: String? { get }
}

public enum TokenType: String {
Expand All @@ -49,6 +50,7 @@ private enum ConfigKeys: String {
case appstoreID = "APP_STORE_ID"
case faq = "FAQ_URL"
case URIScheme = "URI_SCHEME"
case ecommerceURL = "ECOMMERCE_URL"
}

public class Config {
Expand Down Expand Up @@ -160,6 +162,10 @@ extension Config: ConfigProtocol {
public var URIScheme: String {
return string(for: ConfigKeys.URIScheme.rawValue) ?? ""
}

public var ecommerceURL: String? {
return string(for: ConfigKeys.ecommerceURL.rawValue)
}
}

// Mark - For testing and SwiftUI preview
Expand Down
41 changes: 41 additions & 0 deletions Core/Core/Configuration/ServerConfig/IAPConfig.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//
// IAPConfig.swift
// Core
//
// Created by Saeed Bashir on 4/29/24.
//

import Foundation

public class IAPConfig: NSObject {

enum Keys: String, RawStringExtractable {
case enabled = "enabled"
case disabledVersions = "ios_disabled_versions"
case restoreEnabled = "restore_enabled"
}

public var enabled: Bool = false
private var disabledVersions: [String] = []
public var restoreEnabled: Bool = false

init(dictionary: [String: Any]) {
enabled = dictionary[Keys.enabled] as? Bool ?? false
disabledVersions = dictionary[Keys.disabledVersions] as? [String] ?? []
restoreEnabled = dictionary[Keys.restoreEnabled] as? Bool ?? false

if let info = Bundle.main.infoDictionary,
let currentVersion = info["CFBundleShortVersionString"] as? String,
disabledVersions.contains(currentVersion) {
enabled = false
}
}
}

private let Key = "iap_config"

extension ServerConfig {
public var iapConfig: IAPConfig {
return IAPConfig(dictionary: config[Key] as? [String: AnyObject] ?? [:])
}
}
Loading
Loading