Skip to content

Commit 417eee2

Browse files
author
Michael Kuczera
committed
chore: fix support modal, apply build
1 parent ccc4eae commit 417eee2

2 files changed

Lines changed: 53 additions & 20 deletions

File tree

example/ios/HapticFeedbackExample.xcodeproj/project.pbxproj

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -221,10 +221,14 @@
221221
inputFileListPaths = (
222222
"${PODS_ROOT}/Target Support Files/Pods-HapticFeedbackExample/Pods-HapticFeedbackExample-frameworks-${CONFIGURATION}-input-files.xcfilelist",
223223
);
224+
inputPaths = (
225+
);
224226
name = "[CP] Embed Pods Frameworks";
225227
outputFileListPaths = (
226228
"${PODS_ROOT}/Target Support Files/Pods-HapticFeedbackExample/Pods-HapticFeedbackExample-frameworks-${CONFIGURATION}-output-files.xcfilelist",
227229
);
230+
outputPaths = (
231+
);
228232
runOnlyForDeploymentPostprocessing = 0;
229233
shellPath = /bin/sh;
230234
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-HapticFeedbackExample/Pods-HapticFeedbackExample-frameworks.sh\"\n";
@@ -260,10 +264,14 @@
260264
inputFileListPaths = (
261265
"${PODS_ROOT}/Target Support Files/Pods-HapticFeedbackExample/Pods-HapticFeedbackExample-resources-${CONFIGURATION}-input-files.xcfilelist",
262266
);
267+
inputPaths = (
268+
);
263269
name = "[CP] Copy Pods Resources";
264270
outputFileListPaths = (
265271
"${PODS_ROOT}/Target Support Files/Pods-HapticFeedbackExample/Pods-HapticFeedbackExample-resources-${CONFIGURATION}-output-files.xcfilelist",
266272
);
273+
outputPaths = (
274+
);
267275
runOnlyForDeploymentPostprocessing = 0;
268276
shellPath = /bin/sh;
269277
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-HapticFeedbackExample/Pods-HapticFeedbackExample-resources.sh\"\n";
@@ -289,7 +297,7 @@
289297
buildSettings = {
290298
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
291299
CLANG_ENABLE_MODULES = YES;
292-
CURRENT_PROJECT_VERSION = 3;
300+
CURRENT_PROJECT_VERSION = 6;
293301
DEVELOPMENT_TEAM = MX783T4VSP;
294302
ENABLE_BITCODE = NO;
295303
INFOPLIST_FILE = HapticFeedbackExample/Info.plist;
@@ -300,7 +308,7 @@
300308
"$(inherited)",
301309
"@executable_path/Frameworks",
302310
);
303-
MARKETING_VERSION = 1.2;
311+
MARKETING_VERSION = 1.1;
304312
OTHER_LDFLAGS = (
305313
"$(inherited)",
306314
"-ObjC",
@@ -322,7 +330,7 @@
322330
buildSettings = {
323331
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
324332
CLANG_ENABLE_MODULES = YES;
325-
CURRENT_PROJECT_VERSION = 3;
333+
CURRENT_PROJECT_VERSION = 6;
326334
DEVELOPMENT_TEAM = MX783T4VSP;
327335
INFOPLIST_FILE = HapticFeedbackExample/Info.plist;
328336
INFOPLIST_KEY_CFBundleDisplayName = "Haptic feedback demo";
@@ -332,7 +340,7 @@
332340
"$(inherited)",
333341
"@executable_path/Frameworks",
334342
);
335-
MARKETING_VERSION = 1.2;
343+
MARKETING_VERSION = 1.1;
336344
OTHER_LDFLAGS = (
337345
"$(inherited)",
338346
"-ObjC",

example/src/SupportModal.tsx

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useEffect, useCallback } from 'react';
1+
import React, { useEffect, useCallback, useState } from 'react';
22
import {
33
ActivityIndicator,
44
Alert,
@@ -31,7 +31,7 @@ export function higherTier(a: SupportTier, b: SupportTier): SupportTier {
3131

3232
const TIERS = [
3333
{
34-
productId: 'haptic_support_bronze',
34+
productId: 'support_0',
3535
tier: 'support_0' as const,
3636
fallbackPrice: '$0.99',
3737
label: 'Supporter',
@@ -40,7 +40,7 @@ const TIERS = [
4040
color: '#b87333',
4141
},
4242
{
43-
productId: 'haptic_support_silver',
43+
productId: 'support_1',
4444
tier: 'support_1' as const,
4545
fallbackPrice: '$2.99',
4646
label: 'Super Supporter',
@@ -49,7 +49,7 @@ const TIERS = [
4949
color: '#9ca3af',
5050
},
5151
{
52-
productId: 'haptic_support_gold',
52+
productId: 'support_2',
5353
tier: 'support_2' as const,
5454
fallbackPrice: '$4.99',
5555
label: 'Best Supporter',
@@ -82,13 +82,18 @@ export default function SupportModal({
8282
currentTier,
8383
onTierChange,
8484
}: Props) {
85+
// ── Purchase in-progress state ───────────────────────────────────────────────
86+
87+
const [purchasingId, setPurchasingId] = useState<string | null>(null);
88+
8589
// ── useIAP hook ──────────────────────────────────────────────────────────────
8690
// The hook manages initConnection on mount and listener cleanup on unmount.
8791
// onPurchaseSuccess / onPurchaseError fire for every purchase event while
8892
// this component is mounted.
8993

9094
const onPurchaseSuccess = useCallback(
9195
async (purchase: Purchase) => {
96+
setPurchasingId(null);
9297
const newTier = tierForProduct(purchase.productId);
9398
const best = higherTier(newTier, currentTier);
9499
await AsyncStorage.setItem(SUPPORT_STORAGE_KEY, best ?? '');
@@ -98,15 +103,20 @@ export default function SupportModal({
98103
'Thank you! 🙏',
99104
`You are now a ${cfg?.label ?? 'Supporter'}! ${cfg?.emoji ?? ''}`,
100105
);
106+
onClose();
101107
},
102-
[currentTier, onTierChange],
108+
[currentTier, onTierChange, onClose, setPurchasingId],
103109
);
104110

105-
const onPurchaseError = useCallback((error: PurchaseError) => {
106-
if (error.code !== ErrorCode.UserCancelled) {
107-
Alert.alert('Purchase failed', error.message);
108-
}
109-
}, []);
111+
const onPurchaseError = useCallback(
112+
(error: PurchaseError) => {
113+
setPurchasingId(null);
114+
if (error.code !== ErrorCode.UserCancelled) {
115+
Alert.alert('Purchase failed', error.message);
116+
}
117+
},
118+
[setPurchasingId],
119+
);
110120

111121
const {
112122
connected,
@@ -147,6 +157,7 @@ export default function SupportModal({
147157

148158
const purchase = useCallback(
149159
async (productId: string) => {
160+
setPurchasingId(productId);
150161
try {
151162
await requestPurchase({
152163
type: 'in-app',
@@ -157,6 +168,7 @@ export default function SupportModal({
157168
});
158169
} catch (e: any) {
159170
// Non-cancellation errors surface via the onPurchaseError callback
171+
setPurchasingId(null);
160172
}
161173
},
162174
[requestPurchase, finishTransaction], // eslint-disable-line react-hooks/exhaustive-deps
@@ -214,6 +226,8 @@ export default function SupportModal({
214226
const product = products.find(p => p.id === cfg.productId);
215227
const owned = tierRank >= TIER_RANK[cfg.tier];
216228
const isGold = cfg.tier === 'support_2';
229+
const isPurchasing = purchasingId === cfg.productId;
230+
const isDisabled = owned || purchasingId !== null;
217231

218232
return (
219233
<Pressable
@@ -226,14 +240,16 @@ export default function SupportModal({
226240
backgroundColor: cfg.color + '1a',
227241
borderColor: cfg.color,
228242
},
243+
purchasingId !== null &&
244+
!isPurchasing && { opacity: 0.45 },
229245
pressed &&
230-
!owned && {
246+
!isDisabled && {
231247
opacity: 0.72,
232248
transform: [{ scale: 0.99 }],
233249
},
234250
]}
235-
onPress={() => !owned && purchase(cfg.productId)}
236-
disabled={owned}
251+
onPress={() => !isDisabled && purchase(cfg.productId)}
252+
disabled={isDisabled}
237253
>
238254
<Text style={styles.tierEmoji}>{cfg.emoji}</Text>
239255
<View style={styles.tierInfo}>
@@ -262,9 +278,17 @@ export default function SupportModal({
262278
{ backgroundColor: cfg.color },
263279
]}
264280
>
265-
<Text style={styles.priceBtnText}>
266-
{product?.displayPrice ?? cfg.fallbackPrice}
267-
</Text>
281+
{isPurchasing ? (
282+
<ActivityIndicator
283+
size="small"
284+
color="#fff"
285+
style={styles.priceBtnSpinner}
286+
/>
287+
) : (
288+
<Text style={styles.priceBtnText}>
289+
{product?.displayPrice ?? cfg.fallbackPrice}
290+
</Text>
291+
)}
268292
</View>
269293
)}
270294
</View>
@@ -364,6 +388,7 @@ const styles = StyleSheet.create({
364388
paddingVertical: 8,
365389
},
366390
priceBtnText: { color: '#fff', fontWeight: '700', fontSize: 14 },
391+
priceBtnSpinner: { width: 20, height: 20 },
367392
ownedLabel: { fontWeight: '700', fontSize: 13 },
368393
legal: { fontSize: 11, textAlign: 'center', lineHeight: 16 },
369394
closeBtn: { alignItems: 'center', paddingVertical: 10 },

0 commit comments

Comments
 (0)