@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.
Files changed (141) hide show
  1. package/android/build.gradle +1 -1
  2. package/android/src/main/java/com/tyradssdk/TyradsSdkModule.kt +130 -46
  3. package/ios/Tyrads/AcmoAssets.swift +14 -0
  4. package/ios/Tyrads/ApiHeaders.swift +1 -0
  5. package/ios/Tyrads/Tyrads.swift +184 -57
  6. package/ios/Tyrads/WebViewController.swift +27 -3
  7. package/ios/Tyrads/core/utils/AcmoKeyNames.swift +29 -0
  8. package/ios/Tyrads/core/utils/ColorExtension.swift +55 -0
  9. package/ios/Tyrads/core/utils/Services/LocalizationService.swift +175 -0
  10. package/ios/Tyrads/helpers/device_details.swift +148 -46
  11. package/ios/Tyrads/legal/AcmoPrivacyPage.swift +353 -0
  12. package/ios/Tyrads/legal/PrivacyPageController.swift +31 -0
  13. package/ios/Tyrads/user/AcmoUserUpdatePage.swift +302 -0
  14. package/ios/Tyrads/user/AcmoUsersUpdateController.swift +26 -0
  15. package/ios/Tyrads/user/Repository.swift +89 -0
  16. package/ios/TyradsSdk.mm +15 -3
  17. package/ios/TyradsSdk.swift +101 -46
  18. package/lib/commonjs/acmo/core/helpers/native_methods.js +37 -0
  19. package/lib/commonjs/acmo/core/helpers/native_methods.js.map +1 -0
  20. package/lib/commonjs/acmo/core/helpers/numeral.js +19 -0
  21. package/lib/commonjs/acmo/core/helpers/numeral.js.map +1 -0
  22. package/lib/commonjs/acmo/core/services/localization_service.js +164 -0
  23. package/lib/commonjs/acmo/core/services/localization_service.js.map +1 -0
  24. package/lib/commonjs/acmo/core/storage/storage.js +15 -1
  25. package/lib/commonjs/acmo/core/storage/storage.js.map +1 -1
  26. package/lib/commonjs/acmo/modules/dashboard/components/active_offers_button.js +5 -2
  27. package/lib/commonjs/acmo/modules/dashboard/components/active_offers_button.js.map +1 -1
  28. package/lib/commonjs/acmo/modules/dashboard/components/custom_scroller.js +1 -2
  29. package/lib/commonjs/acmo/modules/dashboard/components/custom_scroller.js.map +1 -1
  30. package/lib/commonjs/acmo/modules/dashboard/components/custom_shimmer.js +1 -2
  31. package/lib/commonjs/acmo/modules/dashboard/components/custom_shimmer.js.map +1 -1
  32. package/lib/commonjs/acmo/modules/dashboard/components/offer_card.js +4 -6
  33. package/lib/commonjs/acmo/modules/dashboard/components/offer_card.js.map +1 -1
  34. package/lib/commonjs/acmo/modules/dashboard/components/offer_list_item.js +14 -9
  35. package/lib/commonjs/acmo/modules/dashboard/components/offer_list_item.js.map +1 -1
  36. package/lib/commonjs/acmo/modules/dashboard/components/premium_empty_widget.js +6 -2
  37. package/lib/commonjs/acmo/modules/dashboard/components/premium_empty_widget.js.map +1 -1
  38. package/lib/commonjs/acmo/modules/dashboard/components/premium_header.js +4 -4
  39. package/lib/commonjs/acmo/modules/dashboard/components/premium_header.js.map +1 -1
  40. package/lib/commonjs/acmo/modules/dashboard/components/premium_loading.js +4 -11
  41. package/lib/commonjs/acmo/modules/dashboard/components/premium_loading.js.map +1 -1
  42. package/lib/commonjs/acmo/modules/dashboard/repository.js +1 -1
  43. package/lib/commonjs/acmo/modules/dashboard/top_offers.js +16 -2
  44. package/lib/commonjs/acmo/modules/dashboard/top_offers.js.map +1 -1
  45. package/lib/commonjs/acmo/modules/localization/localization_context.js +55 -0
  46. package/lib/commonjs/acmo/modules/localization/localization_context.js.map +1 -0
  47. package/lib/commonjs/index.js +38 -5
  48. package/lib/commonjs/index.js.map +1 -1
  49. package/lib/module/acmo/core/helpers/native_methods.js +33 -0
  50. package/lib/module/acmo/core/helpers/native_methods.js.map +1 -0
  51. package/lib/module/acmo/core/helpers/numeral.js +14 -0
  52. package/lib/module/acmo/core/helpers/numeral.js.map +1 -0
  53. package/lib/module/acmo/core/services/localization_service.js +159 -0
  54. package/lib/module/acmo/core/services/localization_service.js.map +1 -0
  55. package/lib/module/acmo/core/storage/storage.js +13 -0
  56. package/lib/module/acmo/core/storage/storage.js.map +1 -1
  57. package/lib/module/acmo/modules/dashboard/components/active_offers_button.js +5 -2
  58. package/lib/module/acmo/modules/dashboard/components/active_offers_button.js.map +1 -1
  59. package/lib/module/acmo/modules/dashboard/components/offer_card.js +3 -3
  60. package/lib/module/acmo/modules/dashboard/components/offer_card.js.map +1 -1
  61. package/lib/module/acmo/modules/dashboard/components/offer_list_item.js +14 -9
  62. package/lib/module/acmo/modules/dashboard/components/offer_list_item.js.map +1 -1
  63. package/lib/module/acmo/modules/dashboard/components/premium_empty_widget.js +6 -2
  64. package/lib/module/acmo/modules/dashboard/components/premium_empty_widget.js.map +1 -1
  65. package/lib/module/acmo/modules/dashboard/components/premium_header.js +4 -4
  66. package/lib/module/acmo/modules/dashboard/components/premium_header.js.map +1 -1
  67. package/lib/module/acmo/modules/dashboard/components/premium_loading.js +5 -12
  68. package/lib/module/acmo/modules/dashboard/components/premium_loading.js.map +1 -1
  69. package/lib/module/acmo/modules/dashboard/repository.js +1 -1
  70. package/lib/module/acmo/modules/dashboard/top_offers.js +15 -0
  71. package/lib/module/acmo/modules/dashboard/top_offers.js.map +1 -1
  72. package/lib/module/acmo/modules/localization/localization_context.js +45 -0
  73. package/lib/module/acmo/modules/localization/localization_context.js.map +1 -0
  74. package/lib/module/index.js +38 -6
  75. package/lib/module/index.js.map +1 -1
  76. package/lib/typescript/commonjs/src/acmo/core/helpers/native_methods.d.ts +6 -0
  77. package/lib/typescript/commonjs/src/acmo/core/helpers/native_methods.d.ts.map +1 -0
  78. package/lib/typescript/commonjs/src/acmo/core/helpers/numeral.d.ts +2 -0
  79. package/lib/typescript/commonjs/src/acmo/core/helpers/numeral.d.ts.map +1 -0
  80. package/lib/typescript/commonjs/src/acmo/core/services/localization_service.d.ts +18 -0
  81. package/lib/typescript/commonjs/src/acmo/core/services/localization_service.d.ts.map +1 -0
  82. package/lib/typescript/commonjs/src/acmo/core/storage/storage.d.ts +1 -0
  83. package/lib/typescript/commonjs/src/acmo/core/storage/storage.d.ts.map +1 -1
  84. package/lib/typescript/commonjs/src/acmo/modules/dashboard/components/active_offers_button.d.ts.map +1 -1
  85. package/lib/typescript/commonjs/src/acmo/modules/dashboard/components/offer_card.d.ts.map +1 -1
  86. package/lib/typescript/commonjs/src/acmo/modules/dashboard/components/offer_list_item.d.ts.map +1 -1
  87. package/lib/typescript/commonjs/src/acmo/modules/dashboard/components/premium_empty_widget.d.ts.map +1 -1
  88. package/lib/typescript/commonjs/src/acmo/modules/dashboard/components/premium_header.d.ts.map +1 -1
  89. package/lib/typescript/commonjs/src/acmo/modules/dashboard/components/premium_loading.d.ts +0 -1
  90. package/lib/typescript/commonjs/src/acmo/modules/dashboard/components/premium_loading.d.ts.map +1 -1
  91. package/lib/typescript/commonjs/src/acmo/modules/dashboard/repository.d.ts.map +1 -1
  92. package/lib/typescript/commonjs/src/acmo/modules/dashboard/top_offers.d.ts.map +1 -1
  93. package/lib/typescript/commonjs/src/acmo/modules/localization/localization_context.d.ts +14 -0
  94. package/lib/typescript/commonjs/src/acmo/modules/localization/localization_context.d.ts.map +1 -0
  95. package/lib/typescript/commonjs/src/index.d.ts +4 -0
  96. package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
  97. package/lib/typescript/module/src/acmo/core/helpers/native_methods.d.ts +6 -0
  98. package/lib/typescript/module/src/acmo/core/helpers/native_methods.d.ts.map +1 -0
  99. package/lib/typescript/module/src/acmo/core/helpers/numeral.d.ts +2 -0
  100. package/lib/typescript/module/src/acmo/core/helpers/numeral.d.ts.map +1 -0
  101. package/lib/typescript/module/src/acmo/core/services/localization_service.d.ts +18 -0
  102. package/lib/typescript/module/src/acmo/core/services/localization_service.d.ts.map +1 -0
  103. package/lib/typescript/module/src/acmo/core/storage/storage.d.ts +1 -0
  104. package/lib/typescript/module/src/acmo/core/storage/storage.d.ts.map +1 -1
  105. package/lib/typescript/module/src/acmo/modules/dashboard/components/active_offers_button.d.ts.map +1 -1
  106. package/lib/typescript/module/src/acmo/modules/dashboard/components/offer_card.d.ts.map +1 -1
  107. package/lib/typescript/module/src/acmo/modules/dashboard/components/offer_list_item.d.ts.map +1 -1
  108. package/lib/typescript/module/src/acmo/modules/dashboard/components/premium_empty_widget.d.ts.map +1 -1
  109. package/lib/typescript/module/src/acmo/modules/dashboard/components/premium_header.d.ts.map +1 -1
  110. package/lib/typescript/module/src/acmo/modules/dashboard/components/premium_loading.d.ts +0 -1
  111. package/lib/typescript/module/src/acmo/modules/dashboard/components/premium_loading.d.ts.map +1 -1
  112. package/lib/typescript/module/src/acmo/modules/dashboard/repository.d.ts.map +1 -1
  113. package/lib/typescript/module/src/acmo/modules/dashboard/top_offers.d.ts.map +1 -1
  114. package/lib/typescript/module/src/acmo/modules/localization/localization_context.d.ts +14 -0
  115. package/lib/typescript/module/src/acmo/modules/localization/localization_context.d.ts.map +1 -0
  116. package/lib/typescript/module/src/index.d.ts +4 -0
  117. package/lib/typescript/module/src/index.d.ts.map +1 -1
  118. package/package.json +7 -10
  119. package/src/acmo/core/helpers/native_methods.ts +43 -0
  120. package/src/acmo/core/helpers/numeral.ts +14 -0
  121. package/src/acmo/core/services/localization_service.ts +200 -0
  122. package/src/acmo/core/storage/storage.ts +14 -0
  123. package/src/acmo/modules/dashboard/components/active_offers_button.tsx +3 -2
  124. package/src/acmo/modules/dashboard/components/offer_card.tsx +3 -3
  125. package/src/acmo/modules/dashboard/components/offer_list_item.tsx +9 -7
  126. package/src/acmo/modules/dashboard/components/premium_empty_widget.tsx +5 -2
  127. package/src/acmo/modules/dashboard/components/premium_header.tsx +6 -5
  128. package/src/acmo/modules/dashboard/components/premium_loading.tsx +2 -8
  129. package/src/acmo/modules/dashboard/repository.ts +1 -1
  130. package/src/acmo/modules/dashboard/top_offers.tsx +18 -3
  131. package/src/acmo/modules/localization/localization_context.tsx +52 -0
  132. package/src/index.tsx +63 -18
  133. package/lib/commonjs/i18n.js +0 -112
  134. package/lib/commonjs/i18n.js.map +0 -1
  135. package/lib/module/i18n.js +0 -107
  136. package/lib/module/i18n.js.map +0 -1
  137. package/lib/typescript/commonjs/src/i18n.d.ts +0 -3
  138. package/lib/typescript/commonjs/src/i18n.d.ts.map +0 -1
  139. package/lib/typescript/module/src/i18n.d.ts +0 -3
  140. package/lib/typescript/module/src/i18n.d.ts.map +0 -1
  141. 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
- // const { t } = useTranslation();
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 }]}>Active Offers</Text>
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).format("0.00a").toUpperCase()}
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).format("0.00a").toUpperCase()}
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 numeral from 'numeral';
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
- {bonusMultiplier}x BONUS
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).format('0.00a')}
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
- ).format('0.00a')}
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
- Play
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: 10,
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
- {'Keep Playing!\nExciting Rewards Await!'}
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
- Continue Playing
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 { useTranslation } from 'react-i18next';
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 } = useTranslation();
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('dashboard.suggested_offers')}
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('dashboard.more_offers')}
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: undefined
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
+ };