@loyalytics/swan-react-native-sdk 2.1.3-beta.3 → 2.1.3

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.
@@ -8,5 +8,5 @@ exports.SDK_VERSION = void 0;
8
8
  // This file is generated from package.json version during build.
9
9
  // See scripts/generate-version.js
10
10
 
11
- const SDK_VERSION = exports.SDK_VERSION = '2.1.3-beta.3';
11
+ const SDK_VERSION = exports.SDK_VERSION = '2.1.3';
12
12
  //# sourceMappingURL=version.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["SDK_VERSION","exports"],"sourceRoot":"../../src","sources":["version.ts"],"mappings":";;;;;;AAAA;AACA;AACA;;AAEO,MAAMA,WAAW,GAAAC,OAAA,CAAAD,WAAA,GAAG,cAAc","ignoreList":[]}
1
+ {"version":3,"names":["SDK_VERSION","exports"],"sourceRoot":"../../src","sources":["version.ts"],"mappings":";;;;;;AAAA;AACA;AACA;;AAEO,MAAMA,WAAW,GAAAC,OAAA,CAAAD,WAAA,GAAG,OAAO","ignoreList":[]}
@@ -4,5 +4,5 @@
4
4
  // This file is generated from package.json version during build.
5
5
  // See scripts/generate-version.js
6
6
 
7
- export const SDK_VERSION = '2.1.3-beta.3';
7
+ export const SDK_VERSION = '2.1.3';
8
8
  //# sourceMappingURL=version.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["SDK_VERSION"],"sourceRoot":"../../src","sources":["version.ts"],"mappings":";;AAAA;AACA;AACA;;AAEA,OAAO,MAAMA,WAAW,GAAG,cAAc","ignoreList":[]}
1
+ {"version":3,"names":["SDK_VERSION"],"sourceRoot":"../../src","sources":["version.ts"],"mappings":";;AAAA;AACA;AACA;;AAEA,OAAO,MAAMA,WAAW,GAAG,OAAO","ignoreList":[]}
@@ -1,2 +1,2 @@
1
- export declare const SDK_VERSION = "2.1.3-beta.3";
1
+ export declare const SDK_VERSION = "2.1.3";
2
2
  //# sourceMappingURL=version.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../../../src/version.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,WAAW,iBAAiB,CAAC"}
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../../../src/version.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,WAAW,UAAU,CAAC"}
@@ -1,2 +1,2 @@
1
- export declare const SDK_VERSION = "2.1.3-beta.3";
1
+ export declare const SDK_VERSION = "2.1.3";
2
2
  //# sourceMappingURL=version.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../../../src/version.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,WAAW,iBAAiB,CAAC"}
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../../../src/version.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,WAAW,UAAU,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loyalytics/swan-react-native-sdk",
3
- "version": "2.1.3-beta.3",
3
+ "version": "2.1.3",
4
4
  "description": "React Native SDK for Swan",
5
5
  "source": "./src/index.tsx",
6
6
  "main": "./lib/commonjs/index.js",
@@ -34,7 +34,9 @@
34
34
  "!**/__tests__",
35
35
  "!**/__fixtures__",
36
36
  "!**/__mocks__",
37
- "!**/.*"
37
+ "!**/.*",
38
+ "!android/src/test",
39
+ "!scripts/test-carousel-push.js"
38
40
  ],
39
41
  "scripts": {
40
42
  "test": "jest",
@@ -1,125 +0,0 @@
1
- package com.loyalytics.swan.templates.carousel
2
-
3
- import org.junit.Assert.*
4
- import org.junit.Test
5
-
6
- class CarouselTemplateTest {
7
-
8
- // --- wrapIndex ---
9
-
10
- @Test
11
- fun `wrapIndex forward at end wraps to first`() {
12
- assertEquals(0, wrapIndex(3, 3))
13
- }
14
-
15
- @Test
16
- fun `wrapIndex backward at start wraps to last`() {
17
- assertEquals(2, wrapIndex(-1, 3))
18
- }
19
-
20
- @Test
21
- fun `wrapIndex normal forward`() {
22
- assertEquals(1, wrapIndex(1, 3))
23
- assertEquals(2, wrapIndex(2, 3))
24
- }
25
-
26
- @Test
27
- fun `wrapIndex normal backward`() {
28
- assertEquals(1, wrapIndex(1, 3))
29
- }
30
-
31
- @Test
32
- fun `wrapIndex with single item always returns 0`() {
33
- assertEquals(0, wrapIndex(0, 1))
34
- assertEquals(0, wrapIndex(1, 1))
35
- assertEquals(0, wrapIndex(-1, 1))
36
- }
37
-
38
- @Test
39
- fun `wrapIndex with size 0 returns 0`() {
40
- assertEquals(0, wrapIndex(0, 0))
41
- assertEquals(0, wrapIndex(5, 0))
42
- }
43
-
44
- @Test
45
- fun `wrapIndex large negative wraps correctly`() {
46
- // -5 with size 3: -5 mod 3 = -2, + 3 = 1
47
- assertEquals(1, wrapIndex(-5, 3))
48
- }
49
-
50
- @Test
51
- fun `wrapIndex large positive wraps correctly`() {
52
- // 7 with size 3: 7 mod 3 = 1
53
- assertEquals(1, wrapIndex(7, 3))
54
- }
55
-
56
- @Test
57
- fun `wrapIndex full cycle returns to start`() {
58
- // Going forward through all items returns to 0
59
- val size = 5
60
- for (i in 0 until size) {
61
- assertEquals(i, wrapIndex(i, size))
62
- }
63
- assertEquals(0, wrapIndex(size, size))
64
- }
65
-
66
- @Test
67
- fun `wrapIndex two items alternates correctly`() {
68
- assertEquals(0, wrapIndex(0, 2))
69
- assertEquals(1, wrapIndex(1, 2))
70
- assertEquals(0, wrapIndex(2, 2))
71
- assertEquals(1, wrapIndex(-1, 2))
72
- }
73
-
74
- @Test
75
- fun `wrapIndex filmstrip prev-current-next for 3 items`() {
76
- val size = 3
77
- // At index 0: prev=2, current=0, next=1
78
- assertEquals(2, wrapIndex(0 - 1, size))
79
- assertEquals(0, wrapIndex(0, size))
80
- assertEquals(1, wrapIndex(0 + 1, size))
81
-
82
- // At index 2: prev=1, current=2, next=0
83
- assertEquals(1, wrapIndex(2 - 1, size))
84
- assertEquals(2, wrapIndex(2, size))
85
- assertEquals(0, wrapIndex(2 + 1, size))
86
- }
87
-
88
- // --- canHandle ---
89
-
90
- @Test
91
- fun `canHandle returns true for carousel type`() {
92
- val template = CarouselTemplate()
93
- assertTrue(template.canHandle("carousel"))
94
- }
95
-
96
- @Test
97
- fun `canHandle returns false for other types`() {
98
- val template = CarouselTemplate()
99
- assertFalse(template.canHandle("timer"))
100
- assertFalse(template.canHandle("cta"))
101
- assertFalse(template.canHandle(""))
102
- assertFalse(template.canHandle("CAROUSEL"))
103
- assertFalse(template.canHandle("Carousel"))
104
- }
105
-
106
- // --- CarouselAutoRemoteViews.MAX_FLIPPER_IMAGES cap ---
107
- // (Cannot directly test private const, but we verify the build method
108
- // is accessible and the type is correct)
109
-
110
- @Test
111
- fun `CarouselAutoRemoteViews is a singleton object`() {
112
- // Verify the object exists and is accessible
113
- assertNotNull(CarouselAutoRemoteViews)
114
- }
115
-
116
- @Test
117
- fun `CarouselFilmstripRemoteViews is a singleton object`() {
118
- assertNotNull(CarouselFilmstripRemoteViews)
119
- }
120
-
121
- @Test
122
- fun `CarouselRemoteViews is a singleton object`() {
123
- assertNotNull(CarouselRemoteViews)
124
- }
125
- }
@@ -1,266 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Swan SDK - Carousel Push Notification Test Script
5
- *
6
- * Sends test carousel push notifications via Firebase Admin SDK.
7
- * Requires firebase-admin to be installed (already in devDependencies).
8
- *
9
- * Usage:
10
- * node scripts/test-carousel-push.js --token <FCM_TOKEN> [--mode manual|auto] [--scenario <name>]
11
- *
12
- * Environment:
13
- * GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account-key.json
14
- *
15
- * Scenarios:
16
- * manual-3 — Manual carousel with 3 products (default)
17
- * auto-5 — Auto carousel with 5 products, 2s interval (ViewFlipper)
18
- * filmstrip-4 — Filmstrip variant with 4 products (3-image preview strip)
19
- * single-item — Single item carousel (should hide arrows)
20
- * invalid-json — Invalid carouselItems JSON (tests fallback)
21
- * no-images — Items without imageUrl (tests filtering)
22
- */
23
-
24
- const admin = require('firebase-admin');
25
-
26
- // Parse CLI args
27
- const args = process.argv.slice(2);
28
- const getArg = (name) => {
29
- const idx = args.indexOf(`--${name}`);
30
- return idx !== -1 ? args[idx + 1] : undefined;
31
- };
32
-
33
- const fcmToken = getArg('token');
34
- const mode = getArg('mode') || 'manual';
35
- const scenarioName = getArg('scenario') || 'manual-3';
36
-
37
- if (!fcmToken) {
38
- console.error(
39
- 'Usage: node scripts/test-carousel-push.js --token <FCM_TOKEN> [--mode manual|auto] [--scenario <name>]'
40
- );
41
- console.error(
42
- '\nAvailable scenarios: manual-3, auto-5, filmstrip-4, single-item, invalid-json, no-images'
43
- );
44
- process.exit(1);
45
- }
46
-
47
- // Initialize Firebase Admin
48
- if (!admin.apps.length) {
49
- admin.initializeApp({
50
- credential: admin.credential.applicationDefault(),
51
- });
52
- }
53
-
54
- const SAMPLE_IMAGES = [
55
- 'https://picsum.photos/seed/swan1/400/200',
56
- 'https://picsum.photos/seed/swan2/400/200',
57
- 'https://picsum.photos/seed/swan3/400/200',
58
- 'https://picsum.photos/seed/swan4/400/200',
59
- 'https://picsum.photos/seed/swan5/400/200',
60
- ];
61
-
62
- const scenarios = {
63
- 'manual-3': {
64
- name: 'Manual carousel — 3 products',
65
- data: {
66
- notificationType: 'carousel',
67
- carouselMode: 'manual',
68
- title: 'Flash Sale - Top Picks!',
69
- body: 'Swipe to see our best deals',
70
- channelId: 'swan_promotional',
71
- carouselItems: JSON.stringify([
72
- {
73
- imageUrl: SAMPLE_IMAGES[0],
74
- title: 'Wireless Earbuds',
75
- body: '50% off - $29.99',
76
- route: '/product/101',
77
- },
78
- {
79
- imageUrl: SAMPLE_IMAGES[1],
80
- title: 'Smart Watch',
81
- body: 'New arrival',
82
- route: '/product/102',
83
- },
84
- {
85
- imageUrl: SAMPLE_IMAGES[2],
86
- title: 'Phone Case',
87
- body: 'Best seller',
88
- route: '/product/103',
89
- },
90
- ]),
91
- },
92
- },
93
- 'auto-5': {
94
- name: 'Auto carousel — 5 products, 2s interval (ViewFlipper)',
95
- data: {
96
- notificationType: 'carousel',
97
- carouselMode: 'auto',
98
- carouselInterval: '2000',
99
- title: 'Recommended for You',
100
- body: 'Based on your recent browsing',
101
- channelId: 'swan_general',
102
- defaultRoute: '/recommended',
103
- carouselItems: JSON.stringify([
104
- {
105
- imageUrl: SAMPLE_IMAGES[0],
106
- title: 'Product A',
107
- body: '$19.99',
108
- route: '/product/201',
109
- },
110
- {
111
- imageUrl: SAMPLE_IMAGES[1],
112
- title: 'Product B',
113
- body: '$24.99',
114
- route: '/product/202',
115
- },
116
- {
117
- imageUrl: SAMPLE_IMAGES[2],
118
- title: 'Product C',
119
- body: '$14.99',
120
- route: '/product/203',
121
- },
122
- {
123
- imageUrl: SAMPLE_IMAGES[3],
124
- title: 'Product D',
125
- body: '$39.99',
126
- route: '/product/204',
127
- },
128
- {
129
- imageUrl: SAMPLE_IMAGES[4],
130
- title: 'Product E',
131
- body: '$9.99',
132
- route: '/product/205',
133
- },
134
- ]),
135
- },
136
- },
137
- 'filmstrip-4': {
138
- name: 'Filmstrip carousel — 4 products with side previews',
139
- data: {
140
- notificationType: 'carousel',
141
- carouselMode: 'manual',
142
- carouselVariant: 'filmstrip',
143
- title: 'Summer Collection',
144
- body: 'Tap the side images to browse',
145
- channelId: 'swan_promotional',
146
- carouselItems: JSON.stringify([
147
- {
148
- imageUrl: SAMPLE_IMAGES[0],
149
- title: 'Summer Dress',
150
- body: '$49.99',
151
- route: '/product/401',
152
- },
153
- {
154
- imageUrl: SAMPLE_IMAGES[1],
155
- title: 'Beach Sandals',
156
- body: '$29.99',
157
- route: '/product/402',
158
- },
159
- {
160
- imageUrl: SAMPLE_IMAGES[2],
161
- title: 'Sun Hat',
162
- body: '$19.99',
163
- route: '/product/403',
164
- },
165
- {
166
- imageUrl: SAMPLE_IMAGES[3],
167
- title: 'Sunglasses',
168
- body: '$34.99',
169
- route: '/product/404',
170
- },
171
- ]),
172
- },
173
- },
174
- 'single-item': {
175
- name: 'Single item carousel (should look like normal image notification)',
176
- data: {
177
- notificationType: 'carousel',
178
- carouselMode: 'manual',
179
- title: 'Just for You',
180
- body: 'Check this out',
181
- channelId: 'swan_promotional',
182
- carouselItems: JSON.stringify([
183
- {
184
- imageUrl: SAMPLE_IMAGES[0],
185
- title: 'Featured Product',
186
- body: 'Limited time offer',
187
- route: '/product/301',
188
- },
189
- ]),
190
- },
191
- },
192
- 'invalid-json': {
193
- name: 'Invalid carouselItems JSON (should fall back to standard notification)',
194
- data: {
195
- notificationType: 'carousel',
196
- carouselMode: 'manual',
197
- title: 'Bad Payload Test',
198
- body: 'Should show as standard notification',
199
- channelId: 'swan_general',
200
- carouselItems: 'this is not valid json {{{',
201
- },
202
- },
203
- 'no-images': {
204
- name: 'Items without imageUrl (should filter and fall back)',
205
- data: {
206
- notificationType: 'carousel',
207
- carouselMode: 'manual',
208
- title: 'No Images Test',
209
- body: 'Items lack imageUrl',
210
- channelId: 'swan_general',
211
- carouselItems: JSON.stringify([
212
- { title: 'No image 1', body: 'Missing imageUrl' },
213
- { title: 'No image 2', body: 'Also missing' },
214
- ]),
215
- },
216
- },
217
- };
218
-
219
- async function sendPush(scenario) {
220
- console.log(`\nSending: ${scenario.name}`);
221
- console.log(`Mode: ${scenario.data.carouselMode || 'manual'}`);
222
- console.log(`Token: ${fcmToken.substring(0, 20)}...`);
223
-
224
- const message = {
225
- token: fcmToken,
226
- data: scenario.data,
227
- android: {
228
- priority: 'high',
229
- },
230
- apns: {
231
- payload: {
232
- aps: {
233
- 'mutable-content': 1,
234
- 'category': 'swan_carousel',
235
- },
236
- },
237
- },
238
- };
239
-
240
- try {
241
- const response = await admin.messaging().send(message);
242
- console.log(`Sent successfully! messageId: ${response}`);
243
- return response;
244
- } catch (error) {
245
- console.error(`Failed to send: ${error.message}`);
246
- return null;
247
- }
248
- }
249
-
250
- async function main() {
251
- const scenario = scenarios[scenarioName];
252
- if (!scenario) {
253
- console.error(`Unknown scenario: ${scenarioName}`);
254
- console.error(`Available: ${Object.keys(scenarios).join(', ')}`);
255
- process.exit(1);
256
- }
257
-
258
- // Override mode if --mode flag is provided and scenario supports it
259
- if (mode && scenario.data.carouselMode !== undefined) {
260
- scenario.data.carouselMode = mode;
261
- }
262
-
263
- await sendPush(scenario);
264
- }
265
-
266
- main().catch(console.error);