react-native-pangleads 0.1.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 (95) hide show
  1. package/LICENSE +20 -0
  2. package/NitroPangleAds.podspec +26 -0
  3. package/README.md +80 -0
  4. package/android/CMakeLists.txt +20 -0
  5. package/android/build.gradle +129 -0
  6. package/android/consumer-rules.pro +2 -0
  7. package/android/fix-prefab.gradle +46 -0
  8. package/android/gradle.properties +5 -0
  9. package/android/src/main/AndroidManifest.xml +1 -0
  10. package/android/src/main/cpp/cpp-adapter.cpp +7 -0
  11. package/android/src/main/java/com/margelo/nitro/pangleads/HybridPangleAds.kt +123 -0
  12. package/android/src/main/java/com/margelo/nitro/pangleads/HybridPangleInterstitialAd.kt +106 -0
  13. package/android/src/main/java/com/margelo/nitro/pangleads/HybridPangleRewardedAd.kt +139 -0
  14. package/android/src/main/java/com/margelo/nitro/pangleads/NitroPangleAdsPackage.java +27 -0
  15. package/android/src/main/java/com/margelo/nitro/pangleads/PangleAdEventEmitter.kt +41 -0
  16. package/android/src/main/java/com/margelo/nitro/pangleads/PangleAndroidUtils.kt +32 -0
  17. package/ios/HybridPangleAds.swift +120 -0
  18. package/ios/HybridPangleInterstitialAd.swift +110 -0
  19. package/ios/HybridPangleRewardedAd.swift +143 -0
  20. package/ios/PangleSupport.swift +129 -0
  21. package/lib/module/index.js +172 -0
  22. package/lib/module/index.js.map +1 -0
  23. package/lib/module/index.test.js +201 -0
  24. package/lib/module/index.test.js.map +1 -0
  25. package/lib/module/package.json +1 -0
  26. package/lib/module/specs/PangleAds.nitro.js +4 -0
  27. package/lib/module/specs/PangleAds.nitro.js.map +1 -0
  28. package/lib/typescript/package.json +1 -0
  29. package/lib/typescript/src/index.d.ts +68 -0
  30. package/lib/typescript/src/index.d.ts.map +1 -0
  31. package/lib/typescript/src/index.test.d.ts +2 -0
  32. package/lib/typescript/src/index.test.d.ts.map +1 -0
  33. package/lib/typescript/src/specs/PangleAds.nitro.d.ts +46 -0
  34. package/lib/typescript/src/specs/PangleAds.nitro.d.ts.map +1 -0
  35. package/nitro.json +18 -0
  36. package/nitrogen/generated/android/NitroPangleAds+autolinking.cmake +85 -0
  37. package/nitrogen/generated/android/NitroPangleAds+autolinking.gradle +27 -0
  38. package/nitrogen/generated/android/NitroPangleAdsOnLoad.cpp +50 -0
  39. package/nitrogen/generated/android/NitroPangleAdsOnLoad.hpp +25 -0
  40. package/nitrogen/generated/android/c++/JFunc_void_std__optional_PangleAdEventPayload_.hpp +78 -0
  41. package/nitrogen/generated/android/c++/JHybridPangleAdsSpec.cpp +88 -0
  42. package/nitrogen/generated/android/c++/JHybridPangleAdsSpec.hpp +69 -0
  43. package/nitrogen/generated/android/c++/JHybridPangleInterstitialAdSpec.cpp +85 -0
  44. package/nitrogen/generated/android/c++/JHybridPangleInterstitialAdSpec.hpp +68 -0
  45. package/nitrogen/generated/android/c++/JHybridPangleRewardedAdSpec.cpp +89 -0
  46. package/nitrogen/generated/android/c++/JHybridPangleRewardedAdSpec.hpp +68 -0
  47. package/nitrogen/generated/android/c++/JPangleAdEventPayload.hpp +58 -0
  48. package/nitrogen/generated/android/c++/JPangleAdEventType.hpp +74 -0
  49. package/nitrogen/generated/android/c++/JPangleAdVerificationOptions.hpp +58 -0
  50. package/nitrogen/generated/android/kotlin/com/margelo/nitro/pangleads/Func_void_std__optional_PangleAdEventPayload_.kt +81 -0
  51. package/nitrogen/generated/android/kotlin/com/margelo/nitro/pangleads/HybridPangleAdsSpec.kt +72 -0
  52. package/nitrogen/generated/android/kotlin/com/margelo/nitro/pangleads/HybridPangleInterstitialAdSpec.kt +73 -0
  53. package/nitrogen/generated/android/kotlin/com/margelo/nitro/pangleads/HybridPangleRewardedAdSpec.kt +73 -0
  54. package/nitrogen/generated/android/kotlin/com/margelo/nitro/pangleads/NitroPangleAdsOnLoad.kt +35 -0
  55. package/nitrogen/generated/android/kotlin/com/margelo/nitro/pangleads/PangleAdEventPayload.kt +32 -0
  56. package/nitrogen/generated/android/kotlin/com/margelo/nitro/pangleads/PangleAdEventType.kt +26 -0
  57. package/nitrogen/generated/android/kotlin/com/margelo/nitro/pangleads/PangleAdVerificationOptions.kt +32 -0
  58. package/nitrogen/generated/ios/NitroPangleAds+autolinking.rb +60 -0
  59. package/nitrogen/generated/ios/NitroPangleAds-Swift-Cxx-Bridge.cpp +90 -0
  60. package/nitrogen/generated/ios/NitroPangleAds-Swift-Cxx-Bridge.hpp +270 -0
  61. package/nitrogen/generated/ios/NitroPangleAds-Swift-Cxx-Umbrella.hpp +66 -0
  62. package/nitrogen/generated/ios/NitroPangleAdsAutolinking.mm +33 -0
  63. package/nitrogen/generated/ios/NitroPangleAdsAutolinking.swift +25 -0
  64. package/nitrogen/generated/ios/c++/HybridPangleAdsSpecSwift.cpp +11 -0
  65. package/nitrogen/generated/ios/c++/HybridPangleAdsSpecSwift.hpp +114 -0
  66. package/nitrogen/generated/ios/c++/HybridPangleInterstitialAdSpecSwift.cpp +11 -0
  67. package/nitrogen/generated/ios/c++/HybridPangleInterstitialAdSpecSwift.hpp +107 -0
  68. package/nitrogen/generated/ios/c++/HybridPangleRewardedAdSpecSwift.cpp +11 -0
  69. package/nitrogen/generated/ios/c++/HybridPangleRewardedAdSpecSwift.hpp +110 -0
  70. package/nitrogen/generated/ios/swift/Func_void.swift +47 -0
  71. package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +47 -0
  72. package/nitrogen/generated/ios/swift/Func_void_std__optional_PangleAdEventPayload_.swift +54 -0
  73. package/nitrogen/generated/ios/swift/HybridPangleAdsSpec.swift +54 -0
  74. package/nitrogen/generated/ios/swift/HybridPangleAdsSpec_cxx.swift +193 -0
  75. package/nitrogen/generated/ios/swift/HybridPangleInterstitialAdSpec.swift +53 -0
  76. package/nitrogen/generated/ios/swift/HybridPangleInterstitialAdSpec_cxx.swift +186 -0
  77. package/nitrogen/generated/ios/swift/HybridPangleRewardedAdSpec.swift +53 -0
  78. package/nitrogen/generated/ios/swift/HybridPangleRewardedAdSpec_cxx.swift +193 -0
  79. package/nitrogen/generated/ios/swift/PangleAdEventPayload.swift +77 -0
  80. package/nitrogen/generated/ios/swift/PangleAdEventType.swift +60 -0
  81. package/nitrogen/generated/ios/swift/PangleAdVerificationOptions.swift +84 -0
  82. package/nitrogen/generated/shared/c++/HybridPangleAdsSpec.cpp +26 -0
  83. package/nitrogen/generated/shared/c++/HybridPangleAdsSpec.hpp +74 -0
  84. package/nitrogen/generated/shared/c++/HybridPangleInterstitialAdSpec.cpp +25 -0
  85. package/nitrogen/generated/shared/c++/HybridPangleInterstitialAdSpec.hpp +73 -0
  86. package/nitrogen/generated/shared/c++/HybridPangleRewardedAdSpec.cpp +25 -0
  87. package/nitrogen/generated/shared/c++/HybridPangleRewardedAdSpec.hpp +76 -0
  88. package/nitrogen/generated/shared/c++/PangleAdEventPayload.hpp +72 -0
  89. package/nitrogen/generated/shared/c++/PangleAdEventType.hpp +96 -0
  90. package/nitrogen/generated/shared/c++/PangleAdVerificationOptions.hpp +72 -0
  91. package/package.json +177 -0
  92. package/react-native.config.js +8 -0
  93. package/src/index.test.ts +255 -0
  94. package/src/index.ts +305 -0
  95. package/src/specs/PangleAds.nitro.ts +66 -0
@@ -0,0 +1,96 @@
1
+ ///
2
+ /// PangleAdEventType.hpp
3
+ /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4
+ /// https://github.com/mrousavy/nitro
5
+ /// Copyright © 2026 Marc Rousavy @ Margelo
6
+ ///
7
+
8
+ #pragma once
9
+
10
+ #if __has_include(<NitroModules/NitroHash.hpp>)
11
+ #include <NitroModules/NitroHash.hpp>
12
+ #else
13
+ #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
14
+ #endif
15
+ #if __has_include(<NitroModules/JSIConverter.hpp>)
16
+ #include <NitroModules/JSIConverter.hpp>
17
+ #else
18
+ #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
19
+ #endif
20
+ #if __has_include(<NitroModules/NitroDefines.hpp>)
21
+ #include <NitroModules/NitroDefines.hpp>
22
+ #else
23
+ #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
24
+ #endif
25
+
26
+ namespace margelo::nitro::pangleads {
27
+
28
+ /**
29
+ * An enum which can be represented as a JavaScript union (PangleAdEventType).
30
+ */
31
+ enum class PangleAdEventType {
32
+ LOADED SWIFT_NAME(loaded) = 0,
33
+ ERROR SWIFT_NAME(error) = 1,
34
+ OPENED SWIFT_NAME(opened) = 2,
35
+ CLICKED SWIFT_NAME(clicked) = 3,
36
+ CLOSED SWIFT_NAME(closed) = 4,
37
+ REWARDED_LOADED SWIFT_NAME(rewardedLoaded) = 5,
38
+ REWARDED_EARNED_REWARD SWIFT_NAME(rewardedEarnedReward) = 6,
39
+ } CLOSED_ENUM;
40
+
41
+ } // namespace margelo::nitro::pangleads
42
+
43
+ namespace margelo::nitro {
44
+
45
+ // C++ PangleAdEventType <> JS PangleAdEventType (union)
46
+ template <>
47
+ struct JSIConverter<margelo::nitro::pangleads::PangleAdEventType> final {
48
+ static inline margelo::nitro::pangleads::PangleAdEventType fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
49
+ std::string unionValue = JSIConverter<std::string>::fromJSI(runtime, arg);
50
+ switch (hashString(unionValue.c_str(), unionValue.size())) {
51
+ case hashString("loaded"): return margelo::nitro::pangleads::PangleAdEventType::LOADED;
52
+ case hashString("error"): return margelo::nitro::pangleads::PangleAdEventType::ERROR;
53
+ case hashString("opened"): return margelo::nitro::pangleads::PangleAdEventType::OPENED;
54
+ case hashString("clicked"): return margelo::nitro::pangleads::PangleAdEventType::CLICKED;
55
+ case hashString("closed"): return margelo::nitro::pangleads::PangleAdEventType::CLOSED;
56
+ case hashString("rewarded_loaded"): return margelo::nitro::pangleads::PangleAdEventType::REWARDED_LOADED;
57
+ case hashString("rewarded_earned_reward"): return margelo::nitro::pangleads::PangleAdEventType::REWARDED_EARNED_REWARD;
58
+ default: [[unlikely]]
59
+ throw std::invalid_argument("Cannot convert \"" + unionValue + "\" to enum PangleAdEventType - invalid value!");
60
+ }
61
+ }
62
+ static inline jsi::Value toJSI(jsi::Runtime& runtime, margelo::nitro::pangleads::PangleAdEventType arg) {
63
+ switch (arg) {
64
+ case margelo::nitro::pangleads::PangleAdEventType::LOADED: return JSIConverter<std::string>::toJSI(runtime, "loaded");
65
+ case margelo::nitro::pangleads::PangleAdEventType::ERROR: return JSIConverter<std::string>::toJSI(runtime, "error");
66
+ case margelo::nitro::pangleads::PangleAdEventType::OPENED: return JSIConverter<std::string>::toJSI(runtime, "opened");
67
+ case margelo::nitro::pangleads::PangleAdEventType::CLICKED: return JSIConverter<std::string>::toJSI(runtime, "clicked");
68
+ case margelo::nitro::pangleads::PangleAdEventType::CLOSED: return JSIConverter<std::string>::toJSI(runtime, "closed");
69
+ case margelo::nitro::pangleads::PangleAdEventType::REWARDED_LOADED: return JSIConverter<std::string>::toJSI(runtime, "rewarded_loaded");
70
+ case margelo::nitro::pangleads::PangleAdEventType::REWARDED_EARNED_REWARD: return JSIConverter<std::string>::toJSI(runtime, "rewarded_earned_reward");
71
+ default: [[unlikely]]
72
+ throw std::invalid_argument("Cannot convert PangleAdEventType to JS - invalid value: "
73
+ + std::to_string(static_cast<int>(arg)) + "!");
74
+ }
75
+ }
76
+ static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
77
+ if (!value.isString()) {
78
+ return false;
79
+ }
80
+ std::string unionValue = JSIConverter<std::string>::fromJSI(runtime, value);
81
+ switch (hashString(unionValue.c_str(), unionValue.size())) {
82
+ case hashString("loaded"):
83
+ case hashString("error"):
84
+ case hashString("opened"):
85
+ case hashString("clicked"):
86
+ case hashString("closed"):
87
+ case hashString("rewarded_loaded"):
88
+ case hashString("rewarded_earned_reward"):
89
+ return true;
90
+ default:
91
+ return false;
92
+ }
93
+ }
94
+ };
95
+
96
+ } // namespace margelo::nitro
@@ -0,0 +1,72 @@
1
+ ///
2
+ /// PangleAdVerificationOptions.hpp
3
+ /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4
+ /// https://github.com/mrousavy/nitro
5
+ /// Copyright © 2026 Marc Rousavy @ Margelo
6
+ ///
7
+
8
+ #pragma once
9
+
10
+ #if __has_include(<NitroModules/JSIConverter.hpp>)
11
+ #include <NitroModules/JSIConverter.hpp>
12
+ #else
13
+ #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
14
+ #endif
15
+ #if __has_include(<NitroModules/NitroDefines.hpp>)
16
+ #include <NitroModules/NitroDefines.hpp>
17
+ #else
18
+ #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
19
+ #endif
20
+
21
+
22
+
23
+ #include <string>
24
+ #include <optional>
25
+
26
+ namespace margelo::nitro::pangleads {
27
+
28
+ /**
29
+ * A struct which can be represented as a JavaScript object (PangleAdVerificationOptions).
30
+ */
31
+ struct PangleAdVerificationOptions {
32
+ public:
33
+ std::optional<std::string> userId SWIFT_PRIVATE;
34
+ std::optional<std::string> customData SWIFT_PRIVATE;
35
+
36
+ public:
37
+ PangleAdVerificationOptions() = default;
38
+ explicit PangleAdVerificationOptions(std::optional<std::string> userId, std::optional<std::string> customData): userId(userId), customData(customData) {}
39
+ };
40
+
41
+ } // namespace margelo::nitro::pangleads
42
+
43
+ namespace margelo::nitro {
44
+
45
+ // C++ PangleAdVerificationOptions <> JS PangleAdVerificationOptions (object)
46
+ template <>
47
+ struct JSIConverter<margelo::nitro::pangleads::PangleAdVerificationOptions> final {
48
+ static inline margelo::nitro::pangleads::PangleAdVerificationOptions fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
49
+ jsi::Object obj = arg.asObject(runtime);
50
+ return margelo::nitro::pangleads::PangleAdVerificationOptions(
51
+ JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, "userId")),
52
+ JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, "customData"))
53
+ );
54
+ }
55
+ static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::pangleads::PangleAdVerificationOptions& arg) {
56
+ jsi::Object obj(runtime);
57
+ obj.setProperty(runtime, "userId", JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.userId));
58
+ obj.setProperty(runtime, "customData", JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.customData));
59
+ return obj;
60
+ }
61
+ static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
62
+ if (!value.isObject()) {
63
+ return false;
64
+ }
65
+ jsi::Object obj = value.getObject(runtime);
66
+ if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, "userId"))) return false;
67
+ if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, "customData"))) return false;
68
+ return true;
69
+ }
70
+ };
71
+
72
+ } // namespace margelo::nitro
package/package.json ADDED
@@ -0,0 +1,177 @@
1
+ {
2
+ "name": "react-native-pangleads",
3
+ "version": "0.1.0",
4
+ "description": "Nitro Modules bridge for the Pangle Ads SDK",
5
+ "main": "./lib/module/index.js",
6
+ "types": "./lib/typescript/src/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "react-native-pangleads-source": "./src/index.ts",
10
+ "types": "./lib/typescript/src/index.d.ts",
11
+ "default": "./lib/module/index.js"
12
+ },
13
+ "./package.json": "./package.json"
14
+ },
15
+ "files": [
16
+ "src",
17
+ "lib",
18
+ "android",
19
+ "ios",
20
+ "cpp",
21
+ "nitrogen",
22
+ "nitro.json",
23
+ "*.podspec",
24
+ "react-native.config.js",
25
+ "!ios/build",
26
+ "!android/build",
27
+ "!android/gradle",
28
+ "!android/gradlew",
29
+ "!android/gradlew.bat",
30
+ "!android/local.properties",
31
+ "!**/__tests__",
32
+ "!**/__fixtures__",
33
+ "!**/__mocks__",
34
+ "!**/.*"
35
+ ],
36
+ "scripts": {
37
+ "example": "yarn workspace react-native-pangleads-example",
38
+ "clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib",
39
+ "prepare": "bob build",
40
+ "nitrogen": "nitrogen",
41
+ "typecheck": "tsc",
42
+ "lint": "eslint \"**/*.{js,ts,tsx}\"",
43
+ "test": "jest",
44
+ "release": "release-it --only-version"
45
+ },
46
+ "keywords": [
47
+ "react-native",
48
+ "ios",
49
+ "android"
50
+ ],
51
+ "repository": {
52
+ "type": "git",
53
+ "url": "git+https://github.com/exzos28/react-native-pangleads.git"
54
+ },
55
+ "author": "exzos <oleksandr.kurinnyi.work@gmail.com> (https://github.com/exzos28)",
56
+ "license": "MIT",
57
+ "bugs": {
58
+ "url": "https://github.com/exzos28/react-native-pangleads/issues"
59
+ },
60
+ "homepage": "https://github.com/exzos28/react-native-pangleads#readme",
61
+ "publishConfig": {
62
+ "registry": "https://registry.npmjs.org/"
63
+ },
64
+ "devDependencies": {
65
+ "@commitlint/config-conventional": "^21.0.2",
66
+ "@eslint/compat": "^2.1.0",
67
+ "@eslint/eslintrc": "^3.3.5",
68
+ "@eslint/js": "^10.0.1",
69
+ "@jest/globals": "^29.7.0",
70
+ "@react-native/babel-preset": "0.85.0",
71
+ "@react-native/eslint-config": "0.85.0",
72
+ "@react-native/jest-preset": "0.85.0",
73
+ "@release-it/conventional-changelog": "^11.0.1",
74
+ "@types/jest": "29.5.14",
75
+ "@types/react": "^19.2.0",
76
+ "commitlint": "^21.0.2",
77
+ "del-cli": "^7.0.0",
78
+ "eslint": "^9.39.4",
79
+ "eslint-config-prettier": "^10.1.8",
80
+ "eslint-plugin-ft-flow": "^3.0.11",
81
+ "eslint-plugin-prettier": "^5.5.6",
82
+ "expo": "*",
83
+ "jest": "^29.7.0",
84
+ "lefthook": "^2.1.9",
85
+ "nitrogen": "0.29.6",
86
+ "prettier": "^3.8.3",
87
+ "react": "19.2.3",
88
+ "react-native": "0.85.0",
89
+ "react-native-builder-bob": "^0.43.0",
90
+ "react-native-nitro-modules": "^0.29.6",
91
+ "release-it": "^20.2.0",
92
+ "turbo": "^2.9.16",
93
+ "typescript": "^6.0.3"
94
+ },
95
+ "peerDependencies": {
96
+ "expo": "*",
97
+ "react": "*",
98
+ "react-native": "*",
99
+ "react-native-nitro-modules": "^0.29.6"
100
+ },
101
+ "peerDependenciesMeta": {
102
+ "expo": {
103
+ "optional": true
104
+ }
105
+ },
106
+ "workspaces": [
107
+ "example"
108
+ ],
109
+ "packageManager": "yarn@4.11.0",
110
+ "react-native-builder-bob": {
111
+ "source": "src",
112
+ "output": "lib",
113
+ "targets": [
114
+ [
115
+ "custom",
116
+ {
117
+ "script": "nitrogen",
118
+ "clean": "nitrogen/"
119
+ }
120
+ ],
121
+ [
122
+ "module",
123
+ {
124
+ "esm": true
125
+ }
126
+ ],
127
+ [
128
+ "typescript",
129
+ {
130
+ "project": "tsconfig.build.json"
131
+ }
132
+ ]
133
+ ]
134
+ },
135
+ "prettier": {
136
+ "quoteProps": "consistent",
137
+ "singleQuote": true,
138
+ "tabWidth": 2,
139
+ "trailingComma": "es5",
140
+ "useTabs": false
141
+ },
142
+ "commitlint": {
143
+ "extends": [
144
+ "@commitlint/config-conventional"
145
+ ]
146
+ },
147
+ "release-it": {
148
+ "git": {
149
+ "commitMessage": "chore: release ${version}",
150
+ "tagName": "v${version}"
151
+ },
152
+ "npm": {
153
+ "publish": true
154
+ },
155
+ "github": {
156
+ "release": true
157
+ },
158
+ "plugins": {
159
+ "@release-it/conventional-changelog": {
160
+ "preset": {
161
+ "name": "angular"
162
+ }
163
+ }
164
+ }
165
+ },
166
+ "create-react-native-library": {
167
+ "type": "nitro-module",
168
+ "languages": "kotlin-swift",
169
+ "tools": [
170
+ "eslint",
171
+ "jest",
172
+ "lefthook",
173
+ "release-it"
174
+ ],
175
+ "version": "0.63.0"
176
+ }
177
+ }
@@ -0,0 +1,8 @@
1
+ module.exports = {
2
+ dependency: {
3
+ platforms: {
4
+ ios: {},
5
+ android: {},
6
+ },
7
+ },
8
+ };
@@ -0,0 +1,255 @@
1
+ import type {
2
+ PangleAdEventPayload,
3
+ PangleAdEventType,
4
+ } from './specs/PangleAds.nitro';
5
+
6
+ type Listener = (payload?: PangleAdEventPayload) => void;
7
+
8
+ class MockNativeAd {
9
+ load = jest.fn();
10
+ show = jest.fn(async () => undefined);
11
+ removeAllListeners = jest.fn(() => {
12
+ this.listeners.clear();
13
+ });
14
+
15
+ private listeners = new Map<PangleAdEventType, Set<Listener>>();
16
+ private nextId = 1;
17
+ private idToEntry = new Map<
18
+ number,
19
+ { type: PangleAdEventType; listener: Listener }
20
+ >();
21
+
22
+ addAdEventListener = jest.fn(
23
+ (eventType: PangleAdEventType, listener: Listener) => {
24
+ const set = this.listeners.get(eventType) ?? new Set<Listener>();
25
+ set.add(listener);
26
+ this.listeners.set(eventType, set);
27
+ const id = this.nextId++;
28
+ this.idToEntry.set(id, { type: eventType, listener });
29
+ return id;
30
+ }
31
+ );
32
+
33
+ removeAdEventListener = jest.fn((subscriptionId: number) => {
34
+ const entry = this.idToEntry.get(subscriptionId);
35
+ if (!entry) {
36
+ return;
37
+ }
38
+ this.listeners.get(entry.type)?.delete(entry.listener);
39
+ this.idToEntry.delete(subscriptionId);
40
+ });
41
+
42
+ emit(eventType: PangleAdEventType, payload?: PangleAdEventPayload): void {
43
+ for (const listener of this.listeners.get(eventType) ?? []) {
44
+ listener(payload);
45
+ }
46
+ }
47
+ }
48
+
49
+ const mockCreatedInterstitials: MockNativeAd[] = [];
50
+ const mockCreatedRewarded: MockNativeAd[] = [];
51
+
52
+ jest.mock('react-native-nitro-modules', () => ({
53
+ NitroModules: {
54
+ createHybridObject: () => ({
55
+ initialize: jest.fn(async () => undefined),
56
+ setGDPRConsent: jest.fn(),
57
+ setCCPAConsent: jest.fn(),
58
+ setCOPPA: jest.fn(),
59
+ createInterstitialAd: jest.fn(() => {
60
+ const ad = new MockNativeAd();
61
+ mockCreatedInterstitials.push(ad);
62
+ return ad;
63
+ }),
64
+ createRewardedAd: jest.fn(() => {
65
+ const ad = new MockNativeAd();
66
+ mockCreatedRewarded.push(ad);
67
+ return ad;
68
+ }),
69
+ }),
70
+ },
71
+ }));
72
+
73
+ import {
74
+ AdEventType,
75
+ InterstitialAd,
76
+ MobileAds,
77
+ RewardedAd,
78
+ RewardedAdEventType,
79
+ } from './index';
80
+
81
+ beforeEach(() => {
82
+ mockCreatedInterstitials.length = 0;
83
+ mockCreatedRewarded.length = 0;
84
+ });
85
+
86
+ describe('InterstitialAd.createForAdRequest', () => {
87
+ it('throws for an empty ad unit id', () => {
88
+ expect(() => InterstitialAd.createForAdRequest('')).toThrow(
89
+ /expected a non-empty string value/
90
+ );
91
+ });
92
+
93
+ it('creates a native ad for a valid ad unit id', () => {
94
+ InterstitialAd.createForAdRequest('unit-1');
95
+ expect(mockCreatedInterstitials).toHaveLength(1);
96
+ });
97
+ });
98
+
99
+ describe('InterstitialAd load/show lifecycle', () => {
100
+ it('is not loaded until the native LOADED event fires', () => {
101
+ const ad = InterstitialAd.createForAdRequest('unit-1');
102
+ expect(ad.loaded).toBe(false);
103
+
104
+ ad.load();
105
+ expect(mockCreatedInterstitials[0]!.load).toHaveBeenCalledTimes(1);
106
+ expect(ad.loaded).toBe(false);
107
+
108
+ mockCreatedInterstitials[0]!.emit('loaded');
109
+ expect(ad.loaded).toBe(true);
110
+ });
111
+
112
+ it('does not call native load twice while a load is pending', () => {
113
+ const ad = InterstitialAd.createForAdRequest('unit-1');
114
+ ad.load();
115
+ ad.load();
116
+ expect(mockCreatedInterstitials[0]!.load).toHaveBeenCalledTimes(1);
117
+ });
118
+
119
+ it('throws synchronously when show() is called before the ad has loaded', () => {
120
+ const ad = InterstitialAd.createForAdRequest('unit-1');
121
+ expect(() => ad.show()).toThrow(/has not loaded and could not be shown/);
122
+ });
123
+
124
+ it('delegates to the native ad once loaded', async () => {
125
+ const ad = InterstitialAd.createForAdRequest('unit-1');
126
+ mockCreatedInterstitials[0]!.emit('loaded');
127
+
128
+ await ad.show();
129
+ expect(mockCreatedInterstitials[0]!.show).toHaveBeenCalledTimes(1);
130
+ });
131
+
132
+ it('resets loaded/load state on CLOSED so the ad can be reloaded', () => {
133
+ const ad = InterstitialAd.createForAdRequest('unit-1');
134
+ ad.load();
135
+ mockCreatedInterstitials[0]!.emit('loaded');
136
+ mockCreatedInterstitials[0]!.emit('closed');
137
+
138
+ expect(ad.loaded).toBe(false);
139
+ ad.load();
140
+ expect(mockCreatedInterstitials[0]!.load).toHaveBeenCalledTimes(2);
141
+ });
142
+
143
+ it('resets loaded/load state on ERROR so the ad can be retried', () => {
144
+ const ad = InterstitialAd.createForAdRequest('unit-1');
145
+ ad.load();
146
+ mockCreatedInterstitials[0]!.emit('error', { errorMessage: 'boom' });
147
+
148
+ expect(ad.loaded).toBe(false);
149
+ ad.load();
150
+ expect(mockCreatedInterstitials[0]!.load).toHaveBeenCalledTimes(2);
151
+ });
152
+ });
153
+
154
+ describe('InterstitialAd error payload', () => {
155
+ it('maps the native error payload to a JS Error', () => {
156
+ const ad = InterstitialAd.createForAdRequest('unit-1');
157
+ const handler = jest.fn();
158
+ ad.addAdEventListener(AdEventType.ERROR, handler);
159
+
160
+ mockCreatedInterstitials[0]!.emit('error', {
161
+ errorCode: 42,
162
+ errorMessage: 'no fill',
163
+ });
164
+
165
+ expect(handler).toHaveBeenCalledTimes(1);
166
+ const error = handler.mock.calls[0][0] as Error;
167
+ expect(error.message).toBe('no fill');
168
+ expect((error as Error & { code: string }).code).toBe('42');
169
+ });
170
+ });
171
+
172
+ describe('InterstitialAd listener subscriptions', () => {
173
+ it('stops notifying a listener after it unsubscribes', () => {
174
+ const ad = InterstitialAd.createForAdRequest('unit-1');
175
+ const handler = jest.fn();
176
+ const unsubscribe = ad.addAdEventListener(AdEventType.OPENED, handler);
177
+
178
+ mockCreatedInterstitials[0]!.emit('opened');
179
+ unsubscribe();
180
+ mockCreatedInterstitials[0]!.emit('opened');
181
+
182
+ expect(handler).toHaveBeenCalledTimes(1);
183
+ });
184
+
185
+ it('removeAllListeners clears listeners and forwards to the native ad', () => {
186
+ const ad = InterstitialAd.createForAdRequest('unit-1');
187
+ const handler = jest.fn();
188
+ ad.addAdEventListener(AdEventType.OPENED, handler);
189
+
190
+ ad.removeAllListeners();
191
+ expect(
192
+ mockCreatedInterstitials[0]!.removeAllListeners
193
+ ).toHaveBeenCalledTimes(1);
194
+
195
+ mockCreatedInterstitials[0]!.emit('opened');
196
+ expect(handler).not.toHaveBeenCalled();
197
+ });
198
+
199
+ it('addAdEventsListener receives every event type for the ad', () => {
200
+ const ad = InterstitialAd.createForAdRequest('unit-1');
201
+ const handler = jest.fn();
202
+ ad.addAdEventsListener(handler);
203
+
204
+ mockCreatedInterstitials[0]!.emit('opened');
205
+ mockCreatedInterstitials[0]!.emit('clicked');
206
+
207
+ expect(handler).toHaveBeenCalledWith({
208
+ type: 'opened',
209
+ payload: undefined,
210
+ });
211
+ expect(handler).toHaveBeenCalledWith({
212
+ type: 'clicked',
213
+ payload: undefined,
214
+ });
215
+ });
216
+ });
217
+
218
+ describe('RewardedAd', () => {
219
+ it('rejects AdEventType.LOADED in favor of RewardedAdEventType.LOADED', () => {
220
+ const ad = RewardedAd.createForAdRequest('unit-1');
221
+ expect(() =>
222
+ ad.addAdEventListener(AdEventType.LOADED as never, jest.fn())
223
+ ).toThrow(/use RewardedAdEventType.LOADED/);
224
+ });
225
+
226
+ it('does not fabricate reward data for LOADED or EARNED_REWARD events', () => {
227
+ const ad = RewardedAd.createForAdRequest('unit-1');
228
+ const onLoaded = jest.fn();
229
+ const onEarned = jest.fn();
230
+ ad.addAdEventListener(RewardedAdEventType.LOADED, onLoaded);
231
+ ad.addAdEventListener(RewardedAdEventType.EARNED_REWARD, onEarned);
232
+
233
+ mockCreatedRewarded[0]!.emit('rewarded_loaded');
234
+ mockCreatedRewarded[0]!.emit('rewarded_earned_reward');
235
+
236
+ expect(onLoaded).toHaveBeenCalledWith(undefined);
237
+ expect(onEarned).toHaveBeenCalledWith(undefined);
238
+ });
239
+
240
+ it('marks the ad as loaded on RewardedAdEventType.LOADED', () => {
241
+ const ad = RewardedAd.createForAdRequest('unit-1');
242
+ expect(ad.loaded).toBe(false);
243
+ mockCreatedRewarded[0]!.emit('rewarded_loaded');
244
+ expect(ad.loaded).toBe(true);
245
+ });
246
+ });
247
+
248
+ describe('MobileAds()', () => {
249
+ it('reports a ready adapter status after initialize resolves', async () => {
250
+ const statuses = await MobileAds().initialize('app-id');
251
+ expect(statuses).toEqual([
252
+ expect.objectContaining({ name: 'Pangle', state: 1 }),
253
+ ]);
254
+ });
255
+ });