@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
@@ -0,0 +1,43 @@
|
|
1
|
+
import { NativeModules, Platform } from "react-native";
|
2
|
+
|
3
|
+
const LINKING_ERROR =
|
4
|
+
`The package 'tyrads-sdk' doesn't seem to be linked. Make sure: \n\n` +
|
5
|
+
Platform.select({ ios: "- You have run 'pod install'\n", default: '' }) +
|
6
|
+
'- You rebuilt the app after installing the package\n' +
|
7
|
+
'- You are not using Expo Go\n';
|
8
|
+
|
9
|
+
const TyradsSdk = NativeModules.TyradsSdk
|
10
|
+
? NativeModules.TyradsSdk
|
11
|
+
: new Proxy(
|
12
|
+
{},
|
13
|
+
{
|
14
|
+
get() {
|
15
|
+
throw new Error(LINKING_ERROR);
|
16
|
+
},
|
17
|
+
}
|
18
|
+
);
|
19
|
+
|
20
|
+
|
21
|
+
const TyradsNativeMethods = {
|
22
|
+
|
23
|
+
isPrivacyAccepted: async () => {
|
24
|
+
try {
|
25
|
+
return await TyradsSdk.isPrivacyAccepted();
|
26
|
+
} catch (err) {
|
27
|
+
console.error("Error checking privacy acceptance:", err);
|
28
|
+
return false;
|
29
|
+
}
|
30
|
+
},
|
31
|
+
|
32
|
+
checkOnboardingProcess: async () => {
|
33
|
+
try {
|
34
|
+
const result = await TyradsSdk.checkOnboardingProcess();
|
35
|
+
return result === true;
|
36
|
+
} catch (err) {
|
37
|
+
console.error("Error showing privacy flow:", err);
|
38
|
+
return false;
|
39
|
+
}
|
40
|
+
}
|
41
|
+
};
|
42
|
+
|
43
|
+
export default TyradsNativeMethods;
|
@@ -0,0 +1,14 @@
|
|
1
|
+
export const numeral = (value: number, decimals: number = 2): string => {
|
2
|
+
const suffixes = ['', 'K', 'M', 'B', 'T'];
|
3
|
+
let suffixIndex = 0;
|
4
|
+
let dividedValue = value;
|
5
|
+
|
6
|
+
while (dividedValue >= 1000 && suffixIndex < suffixes.length - 1) {
|
7
|
+
dividedValue /= 1000;
|
8
|
+
suffixIndex++;
|
9
|
+
}
|
10
|
+
|
11
|
+
const roundedValue = Math.floor(dividedValue * Math.pow(10, decimals)) / Math.pow(10, decimals);
|
12
|
+
|
13
|
+
return `${roundedValue}${suffixes[suffixIndex]}`;
|
14
|
+
};
|
@@ -0,0 +1,200 @@
|
|
1
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
2
|
+
import axios, { type AxiosInstance } from 'axios';
|
3
|
+
|
4
|
+
const BASE_URL = 'https://api.tyrads.com/v3.0/';
|
5
|
+
|
6
|
+
interface TranslationResponse {
|
7
|
+
data: Array<{
|
8
|
+
code: string;
|
9
|
+
sha256: string;
|
10
|
+
}>;
|
11
|
+
}
|
12
|
+
|
13
|
+
type Translations = {
|
14
|
+
[key: string]: string | Translations;
|
15
|
+
};
|
16
|
+
|
17
|
+
class LocalizationService {
|
18
|
+
private static instance: LocalizationService;
|
19
|
+
private axios: AxiosInstance | null = null;
|
20
|
+
private translations: Translations = {};
|
21
|
+
private supportedLocales: string[] = [];
|
22
|
+
private readonly fallbackLocale: string = 'en';
|
23
|
+
|
24
|
+
private constructor() {
|
25
|
+
}
|
26
|
+
|
27
|
+
public static getInstance(): LocalizationService {
|
28
|
+
if (!LocalizationService.instance) {
|
29
|
+
LocalizationService.instance = new LocalizationService();
|
30
|
+
}
|
31
|
+
return LocalizationService.instance;
|
32
|
+
}
|
33
|
+
|
34
|
+
private static async getHeadersFromStorage(): Promise<Record<string, string>> {
|
35
|
+
try {
|
36
|
+
const data = await AsyncStorage.getItem('credentials');
|
37
|
+
return data ? JSON.parse(data) : {};
|
38
|
+
} catch (error) {
|
39
|
+
console.error('Failed to retrieve headers from AsyncStorage', error);
|
40
|
+
return {};
|
41
|
+
}
|
42
|
+
}
|
43
|
+
|
44
|
+
|
45
|
+
public async init(locale: string): Promise<void> {
|
46
|
+
if (this.axios) {
|
47
|
+
return;
|
48
|
+
}
|
49
|
+
|
50
|
+
const headers = await LocalizationService.getHeadersFromStorage();
|
51
|
+
|
52
|
+
this.axios = axios.create({
|
53
|
+
baseURL: BASE_URL,
|
54
|
+
timeout: 10000,
|
55
|
+
headers: {
|
56
|
+
'Content-Type': 'application/json',
|
57
|
+
Accept: 'application/json',
|
58
|
+
"X-API-Key": headers["X-API-Key"],
|
59
|
+
"X-API-Secret": headers["X-API-Secret"],
|
60
|
+
}
|
61
|
+
});
|
62
|
+
|
63
|
+
this.axios.interceptors.request.use(
|
64
|
+
(config) => {
|
65
|
+
console.log('====================================');
|
66
|
+
console.log(`Request: ${config.method} ${config.baseURL}${config.url}`);
|
67
|
+
console.log('Headers:', config.headers);
|
68
|
+
console.log('====================================');
|
69
|
+
return config;
|
70
|
+
},
|
71
|
+
(error) => {
|
72
|
+
return Promise.reject(error);
|
73
|
+
}
|
74
|
+
);
|
75
|
+
this.axios.interceptors.response.use(
|
76
|
+
(res) => {
|
77
|
+
console.log('====================================');
|
78
|
+
console.log('Response Data:', res.data);
|
79
|
+
console.log('====================================');
|
80
|
+
return res;
|
81
|
+
},
|
82
|
+
(error) => {
|
83
|
+
return Promise.reject(error);
|
84
|
+
}
|
85
|
+
);
|
86
|
+
await this.loadTranslations(locale);
|
87
|
+
}
|
88
|
+
|
89
|
+
private async loadTranslations(locale: string, force = false): Promise<void> {
|
90
|
+
const hasUpdate = await this.checkForUpdate(locale, force);
|
91
|
+
|
92
|
+
if (!hasUpdate) {
|
93
|
+
const cachedData = await AsyncStorage.getItem(`translations_${locale}`);
|
94
|
+
if (cachedData) {
|
95
|
+
try {
|
96
|
+
this.translations = JSON.parse(cachedData);
|
97
|
+
return;
|
98
|
+
} catch (e) {
|
99
|
+
console.error('Failed to parse cached translations', e);
|
100
|
+
}
|
101
|
+
}
|
102
|
+
}
|
103
|
+
|
104
|
+
await this.fetchTranslations(locale, force);
|
105
|
+
}
|
106
|
+
|
107
|
+
private async fetchTranslations(locale: string, force = false): Promise<void> {
|
108
|
+
if (!this.axios) return;
|
109
|
+
try {
|
110
|
+
let effectiveLocale = locale;
|
111
|
+
if (!this.supportedLocales.includes(locale)) {
|
112
|
+
effectiveLocale = this.fallbackLocale;
|
113
|
+
}
|
114
|
+
|
115
|
+
const response = await this.axios.get(
|
116
|
+
`translations/${effectiveLocale}`,
|
117
|
+
{
|
118
|
+
params: {
|
119
|
+
force,
|
120
|
+
format: 'nested',
|
121
|
+
},
|
122
|
+
}
|
123
|
+
);
|
124
|
+
|
125
|
+
if (response.status === 200) {
|
126
|
+
this.translations = response.data as Translations;
|
127
|
+
await AsyncStorage.setItem(`translations_${effectiveLocale}`, JSON.stringify(response.data));
|
128
|
+
} else {
|
129
|
+
console.warn(`Failed to load translations: ${response.status}`);
|
130
|
+
}
|
131
|
+
} catch (e) {
|
132
|
+
if (axios.isAxiosError(e)) {
|
133
|
+
console.error('Network error fetching translations:', e.message);
|
134
|
+
} else {
|
135
|
+
console.error('An unexpected error occurred:', e);
|
136
|
+
}
|
137
|
+
}
|
138
|
+
}
|
139
|
+
|
140
|
+
private async checkForUpdate(locale: string, force = false): Promise<boolean> {
|
141
|
+
if (!this.axios) return false;
|
142
|
+
try {
|
143
|
+
const response = await this.axios.get<TranslationResponse>('translations/version', { params: { force } });
|
144
|
+
if (response.status === 200) {
|
145
|
+
const data = response.data.data;
|
146
|
+
this.supportedLocales = data.map(item => item.code);
|
147
|
+
|
148
|
+
const currentLocaleData = data.find(item => item.code === locale);
|
149
|
+
if (!currentLocaleData) {
|
150
|
+
return false;
|
151
|
+
}
|
152
|
+
|
153
|
+
const currentLocaleSha256 = currentLocaleData.sha256;
|
154
|
+
const cachedVersion = await AsyncStorage.getItem(`cached_version_${locale}`);
|
155
|
+
|
156
|
+
if (currentLocaleSha256 !== cachedVersion) {
|
157
|
+
await AsyncStorage.setItem(`cached_version_${locale}`, currentLocaleSha256);
|
158
|
+
return true;
|
159
|
+
}
|
160
|
+
}
|
161
|
+
} catch (e) {
|
162
|
+
if (axios.isAxiosError(e)) {
|
163
|
+
console.error('Error checking for update:', e.message);
|
164
|
+
} else {
|
165
|
+
console.error('An unexpected error occurred:', e);
|
166
|
+
}
|
167
|
+
}
|
168
|
+
return false;
|
169
|
+
}
|
170
|
+
|
171
|
+
public translate(key: string, args: Record<string, string | number> = {}): string {
|
172
|
+
const keys = key.split('.');
|
173
|
+
let currentMap: any = this.translations;
|
174
|
+
|
175
|
+
for (const k of keys) {
|
176
|
+
if (typeof currentMap === 'object' && currentMap !== null && currentMap.hasOwnProperty(k)) {
|
177
|
+
currentMap = currentMap[k];
|
178
|
+
} else {
|
179
|
+
return key;
|
180
|
+
}
|
181
|
+
}
|
182
|
+
|
183
|
+
if (typeof currentMap === 'string') {
|
184
|
+
let result = currentMap;
|
185
|
+
for (const [argKey, argValue] of Object.entries(args)) {
|
186
|
+
const regex = new RegExp(`{${argKey}}`, 'gi');
|
187
|
+
result = result.replace(regex, String(argValue));
|
188
|
+
}
|
189
|
+
return result;
|
190
|
+
}
|
191
|
+
|
192
|
+
return key;
|
193
|
+
}
|
194
|
+
|
195
|
+
public async changeLanguage(locale: string, force = false): Promise<void> {
|
196
|
+
await this.loadTranslations(locale, force);
|
197
|
+
}
|
198
|
+
}
|
199
|
+
|
200
|
+
export default LocalizationService;
|
@@ -25,4 +25,18 @@ export const getData = async <T>(key: string): Promise<T | null> => {
|
|
25
25
|
}
|
26
26
|
return null;
|
27
27
|
}
|
28
|
+
};
|
29
|
+
|
30
|
+
export const clearData = async <T>(key: string): Promise<T | null> => {
|
31
|
+
try {
|
32
|
+
await AsyncStorage.removeItem(key);
|
33
|
+
return true as T;
|
34
|
+
} catch (e: any) {
|
35
|
+
if (e instanceof Error) {
|
36
|
+
console.error('Error getting object:', e.message);
|
37
|
+
} else {
|
38
|
+
console.error('An unknown error occurred while getting.');
|
39
|
+
}
|
40
|
+
return false as T;
|
41
|
+
}
|
28
42
|
};
|
@@ -1,5 +1,6 @@
|
|
1
1
|
import React from 'react';
|
2
2
|
import { TouchableOpacity, Text, StyleSheet, View, } from 'react-native';
|
3
|
+
import { useLocalization } from '../../localization/localization_context';
|
3
4
|
// import { useTranslation } from 'react-i18next';
|
4
5
|
|
5
6
|
interface ActiveOffersBtnProps {
|
@@ -9,14 +10,14 @@ interface ActiveOffersBtnProps {
|
|
9
10
|
}
|
10
11
|
|
11
12
|
const ActiveOffersButton: React.FC<ActiveOffersBtnProps> = ({ premiumColor, activeCount, onPress }) => {
|
12
|
-
|
13
|
+
const { t } = useLocalization();
|
13
14
|
return (
|
14
15
|
<TouchableOpacity
|
15
16
|
style={[styles.button, { borderColor: premiumColor }]}
|
16
17
|
onPress={() => onPress && onPress('active-offers')}
|
17
18
|
>
|
18
19
|
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
|
19
|
-
<Text style={[styles.buttonText, { color: premiumColor }]}>
|
20
|
+
<Text style={[styles.buttonText, { color: premiumColor }]}>{t("data.offers.button.activeOffers")}</Text>
|
20
21
|
{activeCount > 0 &&
|
21
22
|
<View style={styles.activeCountContainer}>
|
22
23
|
<Text style={styles.activeCountText}>{activeCount > 99 ? '99+' : activeCount}</Text>
|
@@ -1,5 +1,5 @@
|
|
1
|
-
import numeral from 'numeral';
|
2
1
|
import React, { useState } from 'react';
|
2
|
+
import {numeral} from '../../../core/helpers/numeral';
|
3
3
|
import {
|
4
4
|
View,
|
5
5
|
Text,
|
@@ -71,13 +71,13 @@ const AcmoOfferCard: React.FC<Props> = ({
|
|
71
71
|
<View style={styles.payoutSection}>
|
72
72
|
{currencySaleModel?.multiplier && (
|
73
73
|
<Text style={styles.strikePayout}>
|
74
|
-
{numeral(item.campaignPayout.totalPlayablePayoutConverted)
|
74
|
+
{numeral(item.campaignPayout.totalPlayablePayoutConverted)}
|
75
75
|
</Text>
|
76
76
|
)}
|
77
77
|
<View style={styles.payoutRow}>
|
78
78
|
<Image source={{ uri: item.currency.adUnitCurrencyIcon }} style={styles.currencyIcon} />
|
79
79
|
<Text style={styles.payoutText}>
|
80
|
-
{numeral(item.campaignPayout.totalPlayablePayoutConverted * bonusMultiplier)
|
80
|
+
{numeral(item.campaignPayout.totalPlayablePayoutConverted * bonusMultiplier)}
|
81
81
|
</Text>
|
82
82
|
</View>
|
83
83
|
</View>
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import React from 'react';
|
2
|
+
import {numeral} from '../../../core/helpers/numeral';
|
2
3
|
import {
|
3
4
|
View,
|
4
5
|
Text,
|
@@ -7,7 +8,7 @@ import {
|
|
7
8
|
StyleSheet,
|
8
9
|
ActivityIndicator,
|
9
10
|
} from 'react-native';
|
10
|
-
import
|
11
|
+
import { useLocalization } from '../../localization/localization_context';
|
11
12
|
|
12
13
|
|
13
14
|
|
@@ -45,6 +46,7 @@ export const AcmoOfferListItem: React.FC<Props> = ({
|
|
45
46
|
const bonusMultiplier = currencySales?.multiplier ?? 1;
|
46
47
|
const isLoading = loadingIndex === index;
|
47
48
|
const anyLoading = loadingIndex != null;
|
49
|
+
const { t } = useLocalization();
|
48
50
|
|
49
51
|
return (
|
50
52
|
<TouchableOpacity
|
@@ -62,7 +64,8 @@ export const AcmoOfferListItem: React.FC<Props> = ({
|
|
62
64
|
{currencySales && (
|
63
65
|
<View style={[styles.bonusBadge, { backgroundColor: `${colorPremium}20` }]}>
|
64
66
|
<Text style={[styles.bonusText, { color: colorPremium }]}>
|
65
|
-
{
|
67
|
+
{t('data.shared.label.bonusTagCaps',
|
68
|
+
{ 'multiplier': currencySales?.multiplier! },)}
|
66
69
|
</Text>
|
67
70
|
</View>
|
68
71
|
)}
|
@@ -74,7 +77,7 @@ export const AcmoOfferListItem: React.FC<Props> = ({
|
|
74
77
|
<View style={styles.payoutRow}>
|
75
78
|
{currencySales && (
|
76
79
|
<Text style={styles.strikeText}>
|
77
|
-
{numeral(offer.campaignPayout.totalPlayablePayoutConverted)
|
80
|
+
{numeral(offer.campaignPayout.totalPlayablePayoutConverted)}
|
78
81
|
</Text>
|
79
82
|
)}
|
80
83
|
|
@@ -86,7 +89,7 @@ export const AcmoOfferListItem: React.FC<Props> = ({
|
|
86
89
|
<Text style={styles.payoutText}>
|
87
90
|
{numeral(
|
88
91
|
offer.campaignPayout.totalPlayablePayoutConverted * bonusMultiplier
|
89
|
-
)
|
92
|
+
)}
|
90
93
|
</Text>
|
91
94
|
</View>
|
92
95
|
</View>
|
@@ -115,7 +118,7 @@ export const AcmoOfferListItem: React.FC<Props> = ({
|
|
115
118
|
},
|
116
119
|
]}
|
117
120
|
>
|
118
|
-
|
121
|
+
{t("data.widget.button.play")}
|
119
122
|
</Text>
|
120
123
|
</TouchableOpacity>
|
121
124
|
|
@@ -189,7 +192,7 @@ const styles = StyleSheet.create({
|
|
189
192
|
payoutRow: {
|
190
193
|
flexDirection: 'row',
|
191
194
|
alignItems: 'center',
|
192
|
-
gap:
|
195
|
+
gap: 4,
|
193
196
|
},
|
194
197
|
strikeText: {
|
195
198
|
fontSize: 12,
|
@@ -200,7 +203,6 @@ const styles = StyleSheet.create({
|
|
200
203
|
currencyIcon: {
|
201
204
|
width: 14,
|
202
205
|
height: 14,
|
203
|
-
marginHorizontal: 4,
|
204
206
|
},
|
205
207
|
payoutText: {
|
206
208
|
fontSize: 12,
|
@@ -1,8 +1,11 @@
|
|
1
1
|
import React from 'react';
|
2
2
|
import { View, Text, TouchableOpacity, StyleSheet, ImageBackground } from 'react-native';
|
3
|
+
import { useLocalization } from '../../localization/localization_context';
|
3
4
|
|
4
5
|
const PremiumEmptyView: React.FC<{ onContinue?: () => void, colorPremium?: string}> = ({ onContinue, colorPremium}) => {
|
5
6
|
|
7
|
+
const { t } = useLocalization();
|
8
|
+
|
6
9
|
return (
|
7
10
|
<View style={styles.container}>
|
8
11
|
<ImageBackground
|
@@ -12,7 +15,7 @@ const PremiumEmptyView: React.FC<{ onContinue?: () => void, colorPremium?: strin
|
|
12
15
|
>
|
13
16
|
<View style={styles.content}>
|
14
17
|
<Text style={[styles.title, { color: 'white', fontFamily: 'Poppins_600SemiBold' }]}>
|
15
|
-
{'
|
18
|
+
{t('data.widget.empty.noOffers')}
|
16
19
|
</Text>
|
17
20
|
<TouchableOpacity
|
18
21
|
style={[styles.button, { backgroundColor: 'white' }]}
|
@@ -29,7 +32,7 @@ const PremiumEmptyView: React.FC<{ onContinue?: () => void, colorPremium?: strin
|
|
29
32
|
},
|
30
33
|
]}
|
31
34
|
>
|
32
|
-
|
35
|
+
{t('data.widget.button.continuePlaying')}
|
33
36
|
</Text>
|
34
37
|
</TouchableOpacity>
|
35
38
|
</View>
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import React from 'react';
|
2
2
|
import { View, Text, Image, TouchableOpacity, StyleSheet,} from 'react-native';
|
3
|
-
import {
|
3
|
+
import { useLocalization } from '../../localization/localization_context';
|
4
4
|
|
5
5
|
interface PremiumHeaderSectionProps {
|
6
6
|
showMore?: boolean;
|
@@ -9,7 +9,8 @@ interface PremiumHeaderSectionProps {
|
|
9
9
|
}
|
10
10
|
|
11
11
|
const PremiumHeaderSection: React.FC<PremiumHeaderSectionProps> = ({ showMore = true, premiumColor, onShowOffers }) => {
|
12
|
-
const { t } =
|
12
|
+
const { t } = useLocalization();
|
13
|
+
|
13
14
|
return (
|
14
15
|
<View style={styles.headerContainer}>
|
15
16
|
<View style={styles.leftContainer}>
|
@@ -20,13 +21,13 @@ const PremiumHeaderSection: React.FC<PremiumHeaderSectionProps> = ({ showMore =
|
|
20
21
|
/>
|
21
22
|
</View>
|
22
23
|
<Text style={[styles.headerText, { color: premiumColor }]} numberOfLines={1} ellipsizeMode="tail">
|
23
|
-
{t('
|
24
|
+
{t('data.widget.page.title')}
|
24
25
|
</Text>
|
25
26
|
</View>
|
26
27
|
{showMore && (
|
27
28
|
<TouchableOpacity style={styles.rightContainer} onPress={onShowOffers}>
|
28
29
|
<Text style={[styles.moreOffersText, { color: premiumColor }]} numberOfLines={1}>
|
29
|
-
{t('
|
30
|
+
{t('data.widget.button.moreOffers')}
|
30
31
|
</Text>
|
31
32
|
<Image
|
32
33
|
source={require('../../../../assets/images/angle_up.png')}
|
@@ -78,4 +79,4 @@ const styles = StyleSheet.create({
|
|
78
79
|
},
|
79
80
|
});
|
80
81
|
|
81
|
-
export default PremiumHeaderSection;
|
82
|
+
export default PremiumHeaderSection;
|
@@ -6,12 +6,10 @@ import CustomCard from './custom_card';
|
|
6
6
|
|
7
7
|
interface PremiumWidgetsLoadingProps {
|
8
8
|
widgetStyle: PremiumWidgetStyles;
|
9
|
-
itemHeight?: number;
|
10
9
|
}
|
11
10
|
|
12
11
|
const PremiumWidgetsLoading: React.FC<PremiumWidgetsLoadingProps> = ({
|
13
12
|
widgetStyle,
|
14
|
-
itemHeight,
|
15
13
|
}) => {
|
16
14
|
return (
|
17
15
|
<CustomCard style={{
|
@@ -23,7 +21,7 @@ const PremiumWidgetsLoading: React.FC<PremiumWidgetsLoadingProps> = ({
|
|
23
21
|
<Shimmer style={{ width: 105, height: 18, borderRadius: 4 }} />
|
24
22
|
</View>
|
25
23
|
|
26
|
-
{widgetStyle === PremiumWidgetStyles.list
|
24
|
+
{widgetStyle === PremiumWidgetStyles.list && (
|
27
25
|
<View style={styles.listContainer}>
|
28
26
|
{[...Array(4)].map((_, index) => (
|
29
27
|
<View key={index} style={styles.listTile}>
|
@@ -38,14 +36,10 @@ const PremiumWidgetsLoading: React.FC<PremiumWidgetsLoadingProps> = ({
|
|
38
36
|
</View>
|
39
37
|
))}
|
40
38
|
</View>
|
41
|
-
) : (
|
42
|
-
<>
|
43
|
-
<Shimmer style={{ width: '100%', height: itemHeight }} />
|
44
|
-
</>
|
45
39
|
)}
|
46
40
|
|
47
41
|
{widgetStyle === PremiumWidgetStyles.sliderCards && (
|
48
|
-
<Shimmer shimmerHeight={150} style={{ flexDirection: 'row', height: 150, }} />
|
42
|
+
<Shimmer shimmerHeight={150} style={{ flexDirection: 'row', height: 150, marginTop: 16 }} />
|
49
43
|
)}
|
50
44
|
|
51
45
|
<Shimmer shimmerHeight={42} style={{ flexDirection: 'row', height: 42, borderRadius: 21, marginVertical: 16 }} />
|
@@ -63,7 +63,7 @@ export const fetchPremiumOfferDetails = async (
|
|
63
63
|
// "bannerUrl": "",
|
64
64
|
// "dateStart": "2025-03-10T00:00:00.000Z",
|
65
65
|
// "dateEnd": "2025-03-10T23:59:59.000Z",
|
66
|
-
// remainingTimeSeconds:
|
66
|
+
// remainingTimeSeconds: 3090
|
67
67
|
// };
|
68
68
|
|
69
69
|
setCampaigns(hotOffers);
|
@@ -13,6 +13,7 @@ import AcmoOfferCard from './components/offer_card';
|
|
13
13
|
import AcmoScrollPager from './components/custom_scroller';
|
14
14
|
import PremiumEmptyView from './components/premium_empty_widget';
|
15
15
|
import PremiumWidgetsLoading from './components/premium_loading';
|
16
|
+
import TyradsNativeMethods from '../../core/helpers/native_methods';
|
16
17
|
|
17
18
|
export const enum PremiumWidgetStyles {
|
18
19
|
list,
|
@@ -36,7 +37,6 @@ const PremiumWidgets: React.FC<PremiumWidgetProps> = ({
|
|
36
37
|
const [activeCount, setActiveCount] = useState<number>(0);
|
37
38
|
const [loadingIndex, setLoadingIndex] = useState<number | null>(null);
|
38
39
|
|
39
|
-
|
40
40
|
useEffect(() => {
|
41
41
|
fetchPremiumOfferDetails(
|
42
42
|
setPremiumColor,
|
@@ -44,8 +44,9 @@ const PremiumWidgets: React.FC<PremiumWidgetProps> = ({
|
|
44
44
|
setCurrencySale,
|
45
45
|
setActiveCount,
|
46
46
|
setError,
|
47
|
-
setIsLoading
|
47
|
+
setIsLoading,
|
48
48
|
);
|
49
|
+
|
49
50
|
}, []);
|
50
51
|
|
51
52
|
const handleShowOffers = () => {
|
@@ -60,6 +61,20 @@ const PremiumWidgets: React.FC<PremiumWidgetProps> = ({
|
|
60
61
|
};
|
61
62
|
|
62
63
|
const handleButtonPress = async (campaign: Campaign) => {
|
64
|
+
let isReady = await TyradsNativeMethods.isPrivacyAccepted()
|
65
|
+
if (!isReady) {
|
66
|
+
try {
|
67
|
+
const result = await TyradsNativeMethods.checkOnboardingProcess();
|
68
|
+
console.log("Privacy flow result:", result);
|
69
|
+
isReady = result === true;
|
70
|
+
} catch (err) {
|
71
|
+
console.error("Privacy flow error:", err);
|
72
|
+
isReady = false;
|
73
|
+
}
|
74
|
+
}
|
75
|
+
if (!isReady) {
|
76
|
+
return
|
77
|
+
}
|
63
78
|
await openOffer(campaign);
|
64
79
|
await fetchPremiumOfferDetails(
|
65
80
|
setPremiumColor,
|
@@ -67,7 +82,7 @@ const PremiumWidgets: React.FC<PremiumWidgetProps> = ({
|
|
67
82
|
setCurrencySale,
|
68
83
|
setActiveCount,
|
69
84
|
setError,
|
70
|
-
setIsLoading
|
85
|
+
setIsLoading,
|
71
86
|
);
|
72
87
|
}
|
73
88
|
|
@@ -0,0 +1,52 @@
|
|
1
|
+
import React, { createContext, useContext, useState, useEffect } from 'react';
|
2
|
+
import LocalizationService from '../../core/services/localization_service';
|
3
|
+
|
4
|
+
interface LocalizationContextProps {
|
5
|
+
t: (key: string, args?: Record<string, string | number>) => string;
|
6
|
+
changeLanguage: (lang: string) => Promise<void>;
|
7
|
+
currentLanguage: string;
|
8
|
+
}
|
9
|
+
|
10
|
+
const LocalizationContext = createContext<LocalizationContextProps>({
|
11
|
+
t: (key) => key,
|
12
|
+
changeLanguage: async () => {},
|
13
|
+
currentLanguage: 'en',
|
14
|
+
});
|
15
|
+
|
16
|
+
let _updateLanguage: ((lang: string) => void) | null = null;
|
17
|
+
|
18
|
+
export const LocalizationProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
19
|
+
const [currentLanguage, setCurrentLanguage] = useState('en');
|
20
|
+
const service = LocalizationService.getInstance();
|
21
|
+
|
22
|
+
useEffect(() => {
|
23
|
+
_updateLanguage = setCurrentLanguage;
|
24
|
+
}, []);
|
25
|
+
|
26
|
+
const changeLanguage = async (lang: string) => {
|
27
|
+
await service.changeLanguage(lang);
|
28
|
+
setCurrentLanguage(lang);
|
29
|
+
};
|
30
|
+
|
31
|
+
const t = (key: string, args?: Record<string, string | number>) => service.translate(key, args);
|
32
|
+
|
33
|
+
return (
|
34
|
+
<LocalizationContext.Provider value={{ t, changeLanguage, currentLanguage }}>
|
35
|
+
{children}
|
36
|
+
</LocalizationContext.Provider>
|
37
|
+
);
|
38
|
+
};
|
39
|
+
|
40
|
+
export const useLocalization = () => useContext(LocalizationContext);
|
41
|
+
|
42
|
+
export const updateProviderLanguage = async (lang: string) => {
|
43
|
+
const service = LocalizationService.getInstance();
|
44
|
+
await service.init(lang);
|
45
|
+
_updateLanguage?.(lang);
|
46
|
+
};
|
47
|
+
|
48
|
+
export const changeProviderLanguage = async (lang: string) => {
|
49
|
+
const service = LocalizationService.getInstance();
|
50
|
+
await service.changeLanguage(lang);
|
51
|
+
_updateLanguage?.(lang);
|
52
|
+
};
|