@tyrads.com/tyrads-sdk 3.1.0-beta.0 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/build.gradle +1 -1
- package/android/src/main/java/com/tyradssdk/TyradsSdkModule.kt +130 -46
- package/ios/Tyrads/AcmoAssets.swift +14 -0
- package/ios/Tyrads/ApiHeaders.swift +1 -0
- package/ios/Tyrads/Tyrads.swift +184 -57
- package/ios/Tyrads/WebViewController.swift +27 -3
- package/ios/Tyrads/core/utils/AcmoKeyNames.swift +29 -0
- package/ios/Tyrads/core/utils/ColorExtension.swift +55 -0
- package/ios/Tyrads/core/utils/Services/LocalizationService.swift +175 -0
- package/ios/Tyrads/helpers/device_details.swift +148 -46
- package/ios/Tyrads/legal/AcmoPrivacyPage.swift +353 -0
- package/ios/Tyrads/legal/PrivacyPageController.swift +31 -0
- package/ios/Tyrads/user/AcmoUserUpdatePage.swift +302 -0
- package/ios/Tyrads/user/AcmoUsersUpdateController.swift +26 -0
- package/ios/Tyrads/user/Repository.swift +89 -0
- package/ios/TyradsSdk.mm +15 -3
- package/ios/TyradsSdk.swift +101 -46
- package/lib/commonjs/acmo/core/helpers/native_methods.js +37 -0
- package/lib/commonjs/acmo/core/helpers/native_methods.js.map +1 -0
- package/lib/commonjs/acmo/core/helpers/numeral.js +19 -0
- package/lib/commonjs/acmo/core/helpers/numeral.js.map +1 -0
- package/lib/commonjs/acmo/core/services/localization_service.js +164 -0
- package/lib/commonjs/acmo/core/services/localization_service.js.map +1 -0
- package/lib/commonjs/acmo/core/storage/storage.js +15 -1
- package/lib/commonjs/acmo/core/storage/storage.js.map +1 -1
- package/lib/commonjs/acmo/modules/dashboard/components/active_offers_button.js +5 -2
- package/lib/commonjs/acmo/modules/dashboard/components/active_offers_button.js.map +1 -1
- package/lib/commonjs/acmo/modules/dashboard/components/custom_scroller.js +1 -2
- package/lib/commonjs/acmo/modules/dashboard/components/custom_scroller.js.map +1 -1
- package/lib/commonjs/acmo/modules/dashboard/components/custom_shimmer.js +1 -2
- package/lib/commonjs/acmo/modules/dashboard/components/custom_shimmer.js.map +1 -1
- package/lib/commonjs/acmo/modules/dashboard/components/offer_card.js +4 -6
- package/lib/commonjs/acmo/modules/dashboard/components/offer_card.js.map +1 -1
- package/lib/commonjs/acmo/modules/dashboard/components/offer_list_item.js +14 -9
- package/lib/commonjs/acmo/modules/dashboard/components/offer_list_item.js.map +1 -1
- package/lib/commonjs/acmo/modules/dashboard/components/premium_empty_widget.js +6 -2
- package/lib/commonjs/acmo/modules/dashboard/components/premium_empty_widget.js.map +1 -1
- package/lib/commonjs/acmo/modules/dashboard/components/premium_header.js +4 -4
- package/lib/commonjs/acmo/modules/dashboard/components/premium_header.js.map +1 -1
- package/lib/commonjs/acmo/modules/dashboard/components/premium_loading.js +4 -11
- package/lib/commonjs/acmo/modules/dashboard/components/premium_loading.js.map +1 -1
- package/lib/commonjs/acmo/modules/dashboard/repository.js +1 -1
- package/lib/commonjs/acmo/modules/dashboard/top_offers.js +16 -2
- package/lib/commonjs/acmo/modules/dashboard/top_offers.js.map +1 -1
- package/lib/commonjs/acmo/modules/localization/localization_context.js +55 -0
- package/lib/commonjs/acmo/modules/localization/localization_context.js.map +1 -0
- package/lib/commonjs/index.js +38 -5
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/acmo/core/helpers/native_methods.js +33 -0
- package/lib/module/acmo/core/helpers/native_methods.js.map +1 -0
- package/lib/module/acmo/core/helpers/numeral.js +14 -0
- package/lib/module/acmo/core/helpers/numeral.js.map +1 -0
- package/lib/module/acmo/core/services/localization_service.js +159 -0
- package/lib/module/acmo/core/services/localization_service.js.map +1 -0
- package/lib/module/acmo/core/storage/storage.js +13 -0
- package/lib/module/acmo/core/storage/storage.js.map +1 -1
- package/lib/module/acmo/modules/dashboard/components/active_offers_button.js +5 -2
- package/lib/module/acmo/modules/dashboard/components/active_offers_button.js.map +1 -1
- package/lib/module/acmo/modules/dashboard/components/offer_card.js +3 -3
- package/lib/module/acmo/modules/dashboard/components/offer_card.js.map +1 -1
- package/lib/module/acmo/modules/dashboard/components/offer_list_item.js +14 -9
- package/lib/module/acmo/modules/dashboard/components/offer_list_item.js.map +1 -1
- package/lib/module/acmo/modules/dashboard/components/premium_empty_widget.js +6 -2
- package/lib/module/acmo/modules/dashboard/components/premium_empty_widget.js.map +1 -1
- package/lib/module/acmo/modules/dashboard/components/premium_header.js +4 -4
- package/lib/module/acmo/modules/dashboard/components/premium_header.js.map +1 -1
- package/lib/module/acmo/modules/dashboard/components/premium_loading.js +5 -12
- package/lib/module/acmo/modules/dashboard/components/premium_loading.js.map +1 -1
- package/lib/module/acmo/modules/dashboard/repository.js +1 -1
- package/lib/module/acmo/modules/dashboard/top_offers.js +15 -0
- package/lib/module/acmo/modules/dashboard/top_offers.js.map +1 -1
- package/lib/module/acmo/modules/localization/localization_context.js +45 -0
- package/lib/module/acmo/modules/localization/localization_context.js.map +1 -0
- package/lib/module/index.js +38 -6
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/commonjs/src/acmo/core/helpers/native_methods.d.ts +6 -0
- package/lib/typescript/commonjs/src/acmo/core/helpers/native_methods.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/acmo/core/helpers/numeral.d.ts +2 -0
- package/lib/typescript/commonjs/src/acmo/core/helpers/numeral.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/acmo/core/services/localization_service.d.ts +18 -0
- package/lib/typescript/commonjs/src/acmo/core/services/localization_service.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/acmo/core/storage/storage.d.ts +1 -0
- package/lib/typescript/commonjs/src/acmo/core/storage/storage.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/acmo/modules/dashboard/components/active_offers_button.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/acmo/modules/dashboard/components/offer_card.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/acmo/modules/dashboard/components/offer_list_item.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/acmo/modules/dashboard/components/premium_empty_widget.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/acmo/modules/dashboard/components/premium_header.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/acmo/modules/dashboard/components/premium_loading.d.ts +0 -1
- package/lib/typescript/commonjs/src/acmo/modules/dashboard/components/premium_loading.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/acmo/modules/dashboard/repository.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/acmo/modules/dashboard/top_offers.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/acmo/modules/localization/localization_context.d.ts +14 -0
- package/lib/typescript/commonjs/src/acmo/modules/localization/localization_context.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/index.d.ts +4 -0
- package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
- package/lib/typescript/module/src/acmo/core/helpers/native_methods.d.ts +6 -0
- package/lib/typescript/module/src/acmo/core/helpers/native_methods.d.ts.map +1 -0
- package/lib/typescript/module/src/acmo/core/helpers/numeral.d.ts +2 -0
- package/lib/typescript/module/src/acmo/core/helpers/numeral.d.ts.map +1 -0
- package/lib/typescript/module/src/acmo/core/services/localization_service.d.ts +18 -0
- package/lib/typescript/module/src/acmo/core/services/localization_service.d.ts.map +1 -0
- package/lib/typescript/module/src/acmo/core/storage/storage.d.ts +1 -0
- package/lib/typescript/module/src/acmo/core/storage/storage.d.ts.map +1 -1
- package/lib/typescript/module/src/acmo/modules/dashboard/components/active_offers_button.d.ts.map +1 -1
- package/lib/typescript/module/src/acmo/modules/dashboard/components/offer_card.d.ts.map +1 -1
- package/lib/typescript/module/src/acmo/modules/dashboard/components/offer_list_item.d.ts.map +1 -1
- package/lib/typescript/module/src/acmo/modules/dashboard/components/premium_empty_widget.d.ts.map +1 -1
- package/lib/typescript/module/src/acmo/modules/dashboard/components/premium_header.d.ts.map +1 -1
- package/lib/typescript/module/src/acmo/modules/dashboard/components/premium_loading.d.ts +0 -1
- package/lib/typescript/module/src/acmo/modules/dashboard/components/premium_loading.d.ts.map +1 -1
- package/lib/typescript/module/src/acmo/modules/dashboard/repository.d.ts.map +1 -1
- package/lib/typescript/module/src/acmo/modules/dashboard/top_offers.d.ts.map +1 -1
- package/lib/typescript/module/src/acmo/modules/localization/localization_context.d.ts +14 -0
- package/lib/typescript/module/src/acmo/modules/localization/localization_context.d.ts.map +1 -0
- package/lib/typescript/module/src/index.d.ts +4 -0
- package/lib/typescript/module/src/index.d.ts.map +1 -1
- package/package.json +7 -10
- package/src/acmo/core/helpers/native_methods.ts +43 -0
- package/src/acmo/core/helpers/numeral.ts +14 -0
- package/src/acmo/core/services/localization_service.ts +200 -0
- package/src/acmo/core/storage/storage.ts +14 -0
- package/src/acmo/modules/dashboard/components/active_offers_button.tsx +3 -2
- package/src/acmo/modules/dashboard/components/offer_card.tsx +3 -3
- package/src/acmo/modules/dashboard/components/offer_list_item.tsx +9 -7
- package/src/acmo/modules/dashboard/components/premium_empty_widget.tsx +5 -2
- package/src/acmo/modules/dashboard/components/premium_header.tsx +6 -5
- package/src/acmo/modules/dashboard/components/premium_loading.tsx +2 -8
- package/src/acmo/modules/dashboard/repository.ts +1 -1
- package/src/acmo/modules/dashboard/top_offers.tsx +18 -3
- package/src/acmo/modules/localization/localization_context.tsx +52 -0
- package/src/index.tsx +63 -18
- package/lib/commonjs/i18n.js +0 -112
- package/lib/commonjs/i18n.js.map +0 -1
- package/lib/module/i18n.js +0 -107
- package/lib/module/i18n.js.map +0 -1
- package/lib/typescript/commonjs/src/i18n.d.ts +0 -3
- package/lib/typescript/commonjs/src/i18n.d.ts.map +0 -1
- package/lib/typescript/module/src/i18n.d.ts +0 -3
- package/lib/typescript/module/src/i18n.d.ts.map +0 -1
- package/src/i18n.ts +0 -115
@@ -1,58 +1,160 @@
|
|
1
|
+
//
|
2
|
+
// device_details.swift
|
3
|
+
// Pods
|
4
|
+
//
|
5
|
+
// Created by Basharat Mehdi on 01/08/25.
|
6
|
+
//
|
1
7
|
import UIKit
|
8
|
+
import Foundation
|
2
9
|
|
3
10
|
func getDeviceDetails() -> [String: Any] {
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
11
|
+
var fd = [String: Any]()
|
12
|
+
|
13
|
+
let device = UIDevice.current
|
14
|
+
let bundle = Bundle.main
|
15
|
+
let locale = Locale.current
|
16
|
+
|
17
|
+
// Device basic info
|
18
|
+
fd["deviceId"] = device.identifierForVendor?.uuidString ?? "Unknown"
|
19
|
+
fd["device"] = device.model.lowercased().contains("ipad") ? "iPad" : "iPhone"
|
20
|
+
fd["deviceName"] = device.name
|
21
|
+
fd["brand"] = "Apple"
|
22
|
+
fd["model"] = device.model
|
23
|
+
fd["baseOs"] = device.systemName
|
24
|
+
fd["releaseVersion"] = device.systemVersion
|
25
|
+
fd["modelName"] = getModelName()
|
26
|
+
|
27
|
+
fd["product"] = "Darwin"
|
28
|
+
fd["platform"] = "iOS"
|
29
|
+
fd["apiVersion"] = AcmoConfig.API_VERSION
|
30
|
+
fd["sdkVersion"] = AcmoConfig.SDK_VERSION
|
31
|
+
fd["sdkPlatform"] = AcmoConfig.SDK_PLATFORM
|
32
|
+
|
33
|
+
// App info
|
34
|
+
fd["version"] = bundle.infoDictionary?["CFBundleShortVersionString"] as? String ?? "Unknown"
|
35
|
+
fd["build"] = bundle.infoDictionary?["CFBundleVersion"] as? String ?? "Unknown"
|
36
|
+
fd["package"] = bundle.bundleIdentifier ?? "Unknown"
|
37
|
+
fd["installerStore"] = "App Store"
|
38
|
+
|
39
|
+
fd["osLang"] = locale.languageCode ?? "en"
|
40
|
+
|
41
|
+
// security and virtual check
|
42
|
+
fd["rooted"] = isJailBroken()
|
43
|
+
fd["virtual"] = isRunningOnSimulator()
|
44
|
+
|
45
|
+
return fd
|
32
46
|
}
|
33
47
|
|
34
48
|
func isRunningOnSimulator() -> Bool {
|
35
|
-
|
49
|
+
return ProcessInfo().environment["SIMULATOR_DEVICE_NAME"] != nil
|
36
50
|
}
|
37
51
|
|
38
52
|
func isJailBroken() -> Bool {
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
}
|
53
|
+
#if targetEnvironment(simulator)
|
54
|
+
return false
|
55
|
+
#else
|
56
|
+
let paths = [
|
57
|
+
"/Applications/Cydia.app",
|
58
|
+
"/Library/MobileSubstrate/MobileSubstrate.dylib",
|
59
|
+
"/bin/bash",
|
60
|
+
"/usr/sbin/sshd",
|
61
|
+
"/etc/apt",
|
62
|
+
"/private/var/lib/apt/"
|
63
|
+
]
|
64
|
+
for path in paths {
|
65
|
+
if FileManager.default.fileExists(atPath: path) {
|
66
|
+
return true
|
54
67
|
}
|
55
|
-
|
56
|
-
|
68
|
+
}
|
69
|
+
return false
|
70
|
+
#endif
|
71
|
+
}
|
72
|
+
|
73
|
+
func deviceIdentifier() -> String {
|
74
|
+
#if targetEnvironment(simulator)
|
75
|
+
if let simIdentifier = ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] {
|
76
|
+
return simIdentifier
|
77
|
+
}
|
78
|
+
return "Simulator"
|
79
|
+
#else
|
80
|
+
var systemInfo = utsname()
|
81
|
+
uname(&systemInfo)
|
82
|
+
let machineMirror = Mirror(reflecting: systemInfo.machine)
|
83
|
+
let identifier = machineMirror.children.reduce("") { identifier, element in
|
84
|
+
guard let value = element.value as? Int8, value != 0 else { return identifier }
|
85
|
+
return identifier + String(UnicodeScalar(UInt8(value)))
|
86
|
+
}
|
87
|
+
return identifier
|
88
|
+
#endif
|
57
89
|
}
|
58
90
|
|
91
|
+
|
92
|
+
func getModelName() -> String {
|
93
|
+
let identifier = deviceIdentifier()
|
94
|
+
|
95
|
+
let iphoneModelMapping: [String: String] = [
|
96
|
+
"iPhone1,1": "iPhone",
|
97
|
+
"iPhone1,2": "iPhone 3G",
|
98
|
+
"iPhone2,1": "iPhone 3GS",
|
99
|
+
"iPhone3,1": "iPhone 4",
|
100
|
+
"iPhone3,2": "iPhone 4 GSM Rev A",
|
101
|
+
"iPhone3,3": "iPhone 4 CDMA",
|
102
|
+
"iPhone4,1": "iPhone 4S",
|
103
|
+
"iPhone5,1": "iPhone 5 (GSM)",
|
104
|
+
"iPhone5,2": "iPhone 5 (GSM+CDMA)",
|
105
|
+
"iPhone5,3": "iPhone 5C (GSM)",
|
106
|
+
"iPhone5,4": "iPhone 5C (Global)",
|
107
|
+
"iPhone6,1": "iPhone 5S (GSM)",
|
108
|
+
"iPhone6,2": "iPhone 5S (Global)",
|
109
|
+
"iPhone7,1": "iPhone 6 Plus",
|
110
|
+
"iPhone7,2": "iPhone 6",
|
111
|
+
"iPhone8,1": "iPhone 6s",
|
112
|
+
"iPhone8,2": "iPhone 6s Plus",
|
113
|
+
"iPhone8,4": "iPhone SE (1st Gen)",
|
114
|
+
"iPhone9,1": "iPhone 7",
|
115
|
+
"iPhone9,2": "iPhone 7 Plus",
|
116
|
+
"iPhone9,3": "iPhone 7",
|
117
|
+
"iPhone9,4": "iPhone 7 Plus",
|
118
|
+
"iPhone10,1": "iPhone 8",
|
119
|
+
"iPhone10,2": "iPhone 8 Plus",
|
120
|
+
"iPhone10,3": "iPhone X Global",
|
121
|
+
"iPhone10,4": "iPhone 8",
|
122
|
+
"iPhone10,5": "iPhone 8 Plus",
|
123
|
+
"iPhone10,6": "iPhone X GSM",
|
124
|
+
"iPhone11,2": "iPhone XS",
|
125
|
+
"iPhone11,4": "iPhone XS Max",
|
126
|
+
"iPhone11,6": "iPhone XS Max Global",
|
127
|
+
"iPhone11,8": "iPhone XR",
|
128
|
+
"iPhone12,1": "iPhone 11",
|
129
|
+
"iPhone12,3": "iPhone 11 Pro",
|
130
|
+
"iPhone12,5": "iPhone 11 Pro Max",
|
131
|
+
"iPhone12,8": "iPhone SE (2nd Gen)",
|
132
|
+
"iPhone13,1": "iPhone 12 Mini",
|
133
|
+
"iPhone13,2": "iPhone 12",
|
134
|
+
"iPhone13,3": "iPhone 12 Pro",
|
135
|
+
"iPhone13,4": "iPhone 12 Pro Max",
|
136
|
+
"iPhone14,2": "iPhone 13 Pro",
|
137
|
+
"iPhone14,3": "iPhone 13 Pro Max",
|
138
|
+
"iPhone14,4": "iPhone 13 Mini",
|
139
|
+
"iPhone14,5": "iPhone 13",
|
140
|
+
"iPhone14,6": "iPhone SE (3rd Gen)",
|
141
|
+
"iPhone14,7": "iPhone 14",
|
142
|
+
"iPhone14,8": "iPhone 14 Plus",
|
143
|
+
"iPhone15,2": "iPhone 14 Pro",
|
144
|
+
"iPhone15,3": "iPhone 14 Pro Max",
|
145
|
+
"iPhone15,4": "iPhone 15",
|
146
|
+
"iPhone15,5": "iPhone 15 Plus",
|
147
|
+
"iPhone16,1": "iPhone 15 Pro",
|
148
|
+
"iPhone16,2": "iPhone 15 Pro Max",
|
149
|
+
"iPhone17,1": "iPhone 16 Pro",
|
150
|
+
"iPhone17,2": "iPhone 16 Pro Max",
|
151
|
+
"iPhone17,3": "iPhone 16",
|
152
|
+
"iPhone17,4": "iPhone 16 Plus",
|
153
|
+
"iPhone17,5": "iPhone 16e",
|
154
|
+
"iPhone18,1": "iPhone 17 Pro",
|
155
|
+
"iPhone18,2": "iPhone 17 Pro Max",
|
156
|
+
"iPhone18,3": "iPhone 17",
|
157
|
+
"iPhone18,4": "iPhone Air"
|
158
|
+
]
|
159
|
+
return iphoneModelMapping[identifier] ?? identifier
|
160
|
+
}
|
@@ -0,0 +1,353 @@
|
|
1
|
+
//
|
2
|
+
// AcmoPrivacyPage.swift
|
3
|
+
// Pods
|
4
|
+
//
|
5
|
+
// Created by Basharat Mehdi on 29/09/25.
|
6
|
+
//
|
7
|
+
|
8
|
+
import SwiftUI
|
9
|
+
import UIKit
|
10
|
+
|
11
|
+
struct AcmoPrivacyPolicyPage: View {
|
12
|
+
@Environment(\.presentationMode) var presentationMode
|
13
|
+
|
14
|
+
var localization: LocalizationService = LocalizationService.shared
|
15
|
+
|
16
|
+
var onAccept: (() -> Void)?
|
17
|
+
|
18
|
+
public init(onAccept: (() -> Void)? = nil) {
|
19
|
+
self.onAccept = onAccept
|
20
|
+
}
|
21
|
+
|
22
|
+
var body: some View {
|
23
|
+
ZStack {
|
24
|
+
if #available(iOS 14.0, *) {
|
25
|
+
Color.white.ignoresSafeArea()
|
26
|
+
} else {
|
27
|
+
Color.white
|
28
|
+
}
|
29
|
+
|
30
|
+
VStack {
|
31
|
+
HStack {
|
32
|
+
Spacer()
|
33
|
+
Button(action: {
|
34
|
+
presentationMode.wrappedValue.dismiss()
|
35
|
+
}) {
|
36
|
+
Image(systemName: "xmark")
|
37
|
+
.foregroundColor(Color.gray.opacity(0.6))
|
38
|
+
.font(.system(size: 20))
|
39
|
+
.padding()
|
40
|
+
}
|
41
|
+
}
|
42
|
+
|
43
|
+
BodyView(localization: localization)
|
44
|
+
|
45
|
+
Spacer().frame(height: 24)
|
46
|
+
|
47
|
+
ScrollView {
|
48
|
+
VStack {
|
49
|
+
InfoView(localization: localization)
|
50
|
+
Spacer().frame(height: 155)
|
51
|
+
}
|
52
|
+
.frame(maxWidth: .infinity)
|
53
|
+
}
|
54
|
+
.frame(maxHeight: UIScreen.main.bounds.height / 2)
|
55
|
+
|
56
|
+
Spacer()
|
57
|
+
}
|
58
|
+
.padding(.horizontal, 30)
|
59
|
+
|
60
|
+
VStack {
|
61
|
+
Spacer()
|
62
|
+
VStack(spacing: 16) {
|
63
|
+
Info2View(localization: localization)
|
64
|
+
TwoButtons(
|
65
|
+
onAccept: {
|
66
|
+
UserDefaults.standard.set(true, forKey: "\(AcmoKeyNames.PRIVACY_ACCEPTED_FOR_USER_ID)\(Tyrads.instance.publisherUserID)")
|
67
|
+
presentationMode.wrappedValue.dismiss()
|
68
|
+
onAccept?()
|
69
|
+
},
|
70
|
+
onReject: {
|
71
|
+
presentationMode.wrappedValue.dismiss()
|
72
|
+
},
|
73
|
+
localization: localization
|
74
|
+
)
|
75
|
+
}
|
76
|
+
.frame(width: UIScreen.main.bounds.width, height: 140)
|
77
|
+
.padding(16)
|
78
|
+
.background(Color.white)
|
79
|
+
}
|
80
|
+
}
|
81
|
+
}
|
82
|
+
}
|
83
|
+
|
84
|
+
// MARK: - Body Section
|
85
|
+
struct BodyView: View {
|
86
|
+
let localization: LocalizationService
|
87
|
+
|
88
|
+
func decodeBase64Image(_ base64: String) -> UIImage? {
|
89
|
+
guard let data = Data(base64Encoded: base64) else { return nil }
|
90
|
+
return UIImage(data: data)
|
91
|
+
}
|
92
|
+
|
93
|
+
var body: some View {
|
94
|
+
VStack(spacing: 12) {
|
95
|
+
Text(localization.translate("data.initialization.intro.title"))
|
96
|
+
.font(.system(size: 16, weight: .semibold))
|
97
|
+
.multilineTextAlignment(.center)
|
98
|
+
|
99
|
+
if let image = decodeBase64Image(AcmoAssets.privacyBannerBase64) {
|
100
|
+
Image(uiImage: image)
|
101
|
+
.resizable()
|
102
|
+
.scaledToFit()
|
103
|
+
.frame(height: 160)
|
104
|
+
} else {
|
105
|
+
Text("Image not found")
|
106
|
+
}
|
107
|
+
|
108
|
+
Text(localization.translate("data.initialization.intro.subtitle"))
|
109
|
+
.font(.system(size: 16, weight: .medium))
|
110
|
+
.multilineTextAlignment(.center)
|
111
|
+
.lineSpacing(3)
|
112
|
+
}
|
113
|
+
.frame(maxWidth: UIScreen.main.bounds.width * 0.6)
|
114
|
+
}
|
115
|
+
}
|
116
|
+
|
117
|
+
// MARK: - Info Section (scrollable legal text)
|
118
|
+
struct InfoView: View {
|
119
|
+
let localization: LocalizationService
|
120
|
+
var body: some View {
|
121
|
+
Text(.init(localization.translate("data.initialization.legal.explanation")))
|
122
|
+
.font(.system(size: 14))
|
123
|
+
.padding(10)
|
124
|
+
.foregroundColor(Color.black.opacity(0.65))
|
125
|
+
.multilineTextAlignment(.leading)
|
126
|
+
}
|
127
|
+
func wrapLinks(text: String) -> String {
|
128
|
+
let urlRegexPattern = "(https?:\\/\\/[^\\s]+)"
|
129
|
+
|
130
|
+
do {
|
131
|
+
let regex = try NSRegularExpression(pattern: urlRegexPattern, options: [])
|
132
|
+
let range = NSRange(text.startIndex..., in: text)
|
133
|
+
let replacedText = regex.stringByReplacingMatches(
|
134
|
+
in: text,
|
135
|
+
range: range,
|
136
|
+
withTemplate: "https://tyrads.com/tyrsdk-privacy-policy"
|
137
|
+
)
|
138
|
+
|
139
|
+
return replacedText
|
140
|
+
|
141
|
+
} catch {
|
142
|
+
print("Error creating regex: \(error.localizedDescription)")
|
143
|
+
return text
|
144
|
+
}
|
145
|
+
}
|
146
|
+
}
|
147
|
+
|
148
|
+
// MARK: - Info2 Section
|
149
|
+
struct Info2View: View {
|
150
|
+
let localization: LocalizationService
|
151
|
+
var body: some View {
|
152
|
+
StyledTextView(text: localization.translate("data.initialization.intro.label.iHaveRead"))
|
153
|
+
.padding(10)
|
154
|
+
.multilineTextAlignment(.leading)
|
155
|
+
.frame(width: UIScreen.main.bounds.width * 0.88)
|
156
|
+
}
|
157
|
+
}
|
158
|
+
|
159
|
+
|
160
|
+
// MARK: - Buttons
|
161
|
+
struct TwoButtons: View {
|
162
|
+
var onAccept: () -> Void
|
163
|
+
var onReject: () -> Void
|
164
|
+
let localization: LocalizationService
|
165
|
+
|
166
|
+
var body: some View {
|
167
|
+
VStack(spacing: 8) {
|
168
|
+
Button(action: onAccept) {
|
169
|
+
Text(localization
|
170
|
+
.translate("data.initialization.intro.cta.accept"))
|
171
|
+
.font(.system(size: 15, weight: .medium))
|
172
|
+
.foregroundColor(.white)
|
173
|
+
.frame(width: 160, height: 35)
|
174
|
+
.background(Color(hex: Tyrads.instance.mainColor ?? "#000000"))
|
175
|
+
.cornerRadius(5)
|
176
|
+
}
|
177
|
+
|
178
|
+
Button(action: onReject) {
|
179
|
+
Text(localization
|
180
|
+
.translate("data.initialization.intro.cta.reject"))
|
181
|
+
.font(.system(size: 15, weight: .medium))
|
182
|
+
.foregroundColor(Color(red: 179/255, green: 44/255, blue: 44/255))
|
183
|
+
.frame(width: 150, height: 35)
|
184
|
+
}
|
185
|
+
}
|
186
|
+
}
|
187
|
+
}
|
188
|
+
|
189
|
+
// MARK: StyledText View
|
190
|
+
struct StyledTextView: View {
|
191
|
+
let text: String
|
192
|
+
let fontSize: CGFloat = 14
|
193
|
+
let fontWeight: Font.Weight = .regular
|
194
|
+
let textColor: Color = .black
|
195
|
+
let linkColor: Color = Color(hex: Tyrads.instance.mainColor ?? "#000000")
|
196
|
+
|
197
|
+
var body: some View {
|
198
|
+
WrappingHStack(components: parseTextComponents(), fontSize: fontSize, fontWeight: fontWeight, textColor: textColor, linkColor: linkColor)
|
199
|
+
.font(.system(size: fontSize, weight: fontWeight))
|
200
|
+
}
|
201
|
+
|
202
|
+
private func parseTextComponents() -> [TextComponent] {
|
203
|
+
var components: [TextComponent] = []
|
204
|
+
let pattern = "<([a-z]+)>(.*?)</\\1>"
|
205
|
+
|
206
|
+
do {
|
207
|
+
let regex = try NSRegularExpression(pattern: pattern, options: [])
|
208
|
+
let nsString = text as NSString
|
209
|
+
let matches = regex.matches(in: text, options: [], range: NSRange(location: 0, length: nsString.length))
|
210
|
+
|
211
|
+
if matches.isEmpty {
|
212
|
+
return [TextComponent(text: text, isLink: false)]
|
213
|
+
}
|
214
|
+
|
215
|
+
var lastIndex = 0
|
216
|
+
|
217
|
+
for match in matches {
|
218
|
+
if match.range.location > lastIndex {
|
219
|
+
let beforeRange = NSRange(location: lastIndex, length: match.range.location - lastIndex)
|
220
|
+
let beforeText = nsString.substring(with: beforeRange)
|
221
|
+
components.append(TextComponent(text: beforeText, isLink: false))
|
222
|
+
}
|
223
|
+
|
224
|
+
let tagNameRange = match.range(at: 1)
|
225
|
+
let contentRange = match.range(at: 2)
|
226
|
+
|
227
|
+
let tagName = nsString.substring(with: tagNameRange)
|
228
|
+
let content = nsString.substring(with: contentRange)
|
229
|
+
|
230
|
+
components.append(TextComponent(text: content, isLink: true, tag: tagName))
|
231
|
+
lastIndex = match.range.location + match.range.length
|
232
|
+
}
|
233
|
+
|
234
|
+
if lastIndex < nsString.length {
|
235
|
+
let remainingText = nsString.substring(from: lastIndex)
|
236
|
+
components.append(TextComponent(text: remainingText, isLink: false))
|
237
|
+
}
|
238
|
+
|
239
|
+
} catch {
|
240
|
+
components = [TextComponent(text: text, isLink: false)]
|
241
|
+
}
|
242
|
+
|
243
|
+
return components
|
244
|
+
}
|
245
|
+
|
246
|
+
private func handleLinkTap(for tag: String?) {
|
247
|
+
guard let tag = tag else { return }
|
248
|
+
|
249
|
+
switch tag {
|
250
|
+
case "tos":
|
251
|
+
if let url = URL(string: "https://tyrads.com/tyrsdk-terms-of-service/") {
|
252
|
+
UIApplication.shared.open(url)
|
253
|
+
}
|
254
|
+
case "pp":
|
255
|
+
if let url = URL(string: "https://tyrads.com/tyrsdk-privacy-policy/") {
|
256
|
+
UIApplication.shared.open(url)
|
257
|
+
}
|
258
|
+
default:
|
259
|
+
break
|
260
|
+
}
|
261
|
+
}
|
262
|
+
}
|
263
|
+
|
264
|
+
struct WrappingHStack: View {
|
265
|
+
let components: [TextComponent]
|
266
|
+
let fontSize: CGFloat
|
267
|
+
let fontWeight: Font.Weight
|
268
|
+
let textColor: Color
|
269
|
+
let linkColor: Color
|
270
|
+
|
271
|
+
var body: some View {
|
272
|
+
GeometryReader { geometry in
|
273
|
+
self.generateContent(in: geometry)
|
274
|
+
}
|
275
|
+
}
|
276
|
+
|
277
|
+
private func generateContent(in geometry: GeometryProxy) -> some View {
|
278
|
+
var width = CGFloat.zero
|
279
|
+
var height = CGFloat.zero
|
280
|
+
|
281
|
+
return ZStack(alignment: .topLeading) {
|
282
|
+
ForEach(components, id: \.id) { component in
|
283
|
+
self.item(for: component)
|
284
|
+
.alignmentGuide(.leading) { dimension in
|
285
|
+
if abs(width - dimension.width) > geometry.size.width {
|
286
|
+
width = 0
|
287
|
+
height -= dimension.height
|
288
|
+
}
|
289
|
+
let result = width
|
290
|
+
if component.id == components.last?.id {
|
291
|
+
width = 0
|
292
|
+
} else {
|
293
|
+
width -= dimension.width
|
294
|
+
}
|
295
|
+
return result
|
296
|
+
}
|
297
|
+
.alignmentGuide(.top) { dimension in
|
298
|
+
let result = height
|
299
|
+
if component.id == components.last?.id {
|
300
|
+
height = 0
|
301
|
+
}
|
302
|
+
return result
|
303
|
+
}
|
304
|
+
}
|
305
|
+
}
|
306
|
+
}
|
307
|
+
|
308
|
+
@ViewBuilder
|
309
|
+
private func item(for component: TextComponent) -> some View {
|
310
|
+
if component.isLink {
|
311
|
+
Text(component.text)
|
312
|
+
.font(.system(size: fontSize, weight: fontWeight))
|
313
|
+
.foregroundColor(linkColor)
|
314
|
+
.onTapGesture {
|
315
|
+
handleLinkTap(for: component.tag)
|
316
|
+
}
|
317
|
+
} else {
|
318
|
+
Text(component.text)
|
319
|
+
.font(.system(size: fontSize, weight: fontWeight))
|
320
|
+
.foregroundColor(textColor)
|
321
|
+
}
|
322
|
+
}
|
323
|
+
|
324
|
+
private func handleLinkTap(for tag: String?) {
|
325
|
+
guard let tag = tag else { return }
|
326
|
+
|
327
|
+
switch tag {
|
328
|
+
case "tos":
|
329
|
+
if let url = URL(string: "https://tyrads.com/tyrsdk-terms-of-service/") {
|
330
|
+
UIApplication.shared.open(url)
|
331
|
+
}
|
332
|
+
case "pp":
|
333
|
+
if let url = URL(string: "https://tyrads.com/tyrsdk-privacy-policy/") {
|
334
|
+
UIApplication.shared.open(url)
|
335
|
+
}
|
336
|
+
default:
|
337
|
+
break
|
338
|
+
}
|
339
|
+
}
|
340
|
+
}
|
341
|
+
|
342
|
+
struct TextComponent: Identifiable {
|
343
|
+
let id = UUID()
|
344
|
+
let text: String
|
345
|
+
let isLink: Bool
|
346
|
+
let tag: String?
|
347
|
+
|
348
|
+
init(text: String, isLink: Bool, tag: String? = nil) {
|
349
|
+
self.text = text
|
350
|
+
self.isLink = isLink
|
351
|
+
self.tag = tag
|
352
|
+
}
|
353
|
+
}
|
@@ -0,0 +1,31 @@
|
|
1
|
+
//
|
2
|
+
// AcmoPrivacyPolicyController.swift
|
3
|
+
// Pods
|
4
|
+
//
|
5
|
+
// Created by Basharat Mehdi on 29/09/25.
|
6
|
+
//
|
7
|
+
|
8
|
+
import UIKit
|
9
|
+
import SwiftUI
|
10
|
+
|
11
|
+
public typealias PolicyCompletion = () -> Void
|
12
|
+
|
13
|
+
class AcmoPrivacyPolicyController: UIHostingController<AcmoPrivacyPolicyPage> {
|
14
|
+
|
15
|
+
init(onAccept: PolicyCompletion? = nil) {
|
16
|
+
|
17
|
+
let rootView = AcmoPrivacyPolicyPage(onAccept: onAccept)
|
18
|
+
|
19
|
+
super.init(rootView: rootView)
|
20
|
+
|
21
|
+
self.modalPresentationStyle = .fullScreen
|
22
|
+
}
|
23
|
+
|
24
|
+
@MainActor required dynamic init?(coder aDecoder: NSCoder) {
|
25
|
+
fatalError("init(coder:) has not been implemented")
|
26
|
+
}
|
27
|
+
|
28
|
+
override func viewWillDisappear(_ animated: Bool) {
|
29
|
+
super.viewWillDisappear(animated)
|
30
|
+
}
|
31
|
+
}
|