From ab720ddae73151d127e9eec9895ec8bbee1e087b Mon Sep 17 00:00:00 2001 From: tapframe Date: Wed, 24 Dec 2025 19:21:53 +0530 Subject: [PATCH] responsiveness for sdui modal --- src/components/promotions/CampaignManager.tsx | 138 ++++++++++-------- src/components/promotions/PosterModal.tsx | 91 +++++++----- src/services/campaignService.ts | 2 +- 3 files changed, 131 insertions(+), 100 deletions(-) diff --git a/src/components/promotions/CampaignManager.tsx b/src/components/promotions/CampaignManager.tsx index c5e1b864..3b09d8f9 100644 --- a/src/components/promotions/CampaignManager.tsx +++ b/src/components/promotions/CampaignManager.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState, useCallback } from 'react'; -import { View, StyleSheet, Text, TouchableOpacity, Image, Linking, Dimensions } from 'react-native'; +import { View, StyleSheet, Text, TouchableOpacity, Image, Linking, useWindowDimensions } from 'react-native'; import Animated, { FadeIn, FadeOut, SlideInDown, SlideOutDown, SlideInUp, SlideOutUp } from 'react-native-reanimated'; import { BlurView } from 'expo-blur'; import { Ionicons } from '@expo/vector-icons'; @@ -9,9 +9,6 @@ import { useNavigation } from '@react-navigation/native'; import { useAccount } from '../../contexts/AccountContext'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; -const { width: SCREEN_WIDTH } = Dimensions.get('window'); - -// --- Banner Component --- interface BannerProps { campaign: Campaign; onDismiss: () => void; @@ -20,7 +17,10 @@ interface BannerProps { const BannerCampaign: React.FC = ({ campaign, onDismiss, onAction }) => { const insets = useSafeAreaInsets(); + const { width } = useWindowDimensions(); const { content } = campaign; + const isTablet = width >= 768; + const bannerMaxWidth = isTablet ? 600 : width - 24; const handlePress = () => { if (content.primaryAction) { @@ -41,41 +41,51 @@ const BannerCampaign: React.FC = ({ campaign, onDismiss, onAction } style={[styles.bannerContainer, { paddingTop: insets.top + 8 }]} > {content.imageUrl && ( - + )} {content.title && ( - + {content.title} )} {content.message && ( - + {content.message} )} {content.primaryAction?.label && ( - - + + {content.primaryAction.label} )} - + ); }; -// --- Bottom Sheet Component --- interface BottomSheetProps { campaign: Campaign; onDismiss: () => void; @@ -84,7 +94,13 @@ interface BottomSheetProps { const BottomSheetCampaign: React.FC = ({ campaign, onDismiss, onAction }) => { const insets = useSafeAreaInsets(); + const { width, height } = useWindowDimensions(); const { content } = campaign; + const isTablet = width >= 768; + const isLandscape = width > height; + + const sheetMaxWidth = isTablet ? 500 : width; + const imageMaxHeight = isLandscape ? height * 0.35 : height * 0.3; const handlePrimaryAction = () => { if (content.primaryAction) { @@ -113,30 +129,47 @@ const BottomSheetCampaign: React.FC = ({ campaign, onDismiss, - + {content.imageUrl && ( )} {content.title && ( - + {content.title} )} {content.message && ( - + {content.message} )} @@ -144,11 +177,11 @@ const BottomSheetCampaign: React.FC = ({ campaign, onDismiss, {content.primaryAction && ( - + {content.primaryAction.label} @@ -158,7 +191,6 @@ const BottomSheetCampaign: React.FC = ({ campaign, onDismiss, ); }; -// --- Campaign Manager --- export const CampaignManager: React.FC = () => { const [activeCampaign, setActiveCampaign] = useState(null); const [isVisible, setIsVisible] = useState(false); @@ -190,7 +222,6 @@ export const CampaignManager: React.FC = () => { const handleDismiss = useCallback(() => { setIsVisible(false); - // After animation completes, check for next campaign setTimeout(() => { const nextCampaign = campaignService.getNextCampaign(); console.log('[CampaignManager] Next campaign:', nextCampaign?.id, nextCampaign?.type); @@ -202,7 +233,7 @@ export const CampaignManager: React.FC = () => { } else { setActiveCampaign(null); } - }, 350); // Wait for exit animation + }, 350); }, []); const handleAction = useCallback((action: CampaignAction) => { @@ -252,7 +283,6 @@ export const CampaignManager: React.FC = () => { }; const styles = StyleSheet.create({ - // Banner styles bannerContainer: { position: 'absolute', top: 0, @@ -264,48 +294,40 @@ const styles = StyleSheet.create({ banner: { flexDirection: 'row', alignItems: 'center', - padding: 12, - borderRadius: 12, + padding: 14, + borderRadius: 14, shadowColor: '#000', - shadowOffset: { width: 0, height: 2 }, - shadowOpacity: 0.2, - shadowRadius: 6, - elevation: 6, + shadowOffset: { width: 0, height: 4 }, + shadowOpacity: 0.25, + shadowRadius: 8, + elevation: 8, }, bannerImage: { - width: 44, - height: 44, - borderRadius: 8, + borderRadius: 10, marginRight: 12, }, bannerContent: { flex: 1, }, bannerTitle: { - fontSize: 14, fontWeight: '600', marginBottom: 2, }, bannerMessage: { - fontSize: 12, opacity: 0.8, }, bannerCta: { - paddingHorizontal: 12, - paddingVertical: 6, - borderRadius: 14, - marginLeft: 8, + paddingVertical: 8, + borderRadius: 16, + marginLeft: 10, }, bannerCtaText: { - fontSize: 12, fontWeight: '600', }, bannerClose: { - padding: 4, + padding: 6, marginLeft: 8, }, - - // Bottom sheet styles backdrop: { ...StyleSheet.absoluteFillObject, backgroundColor: 'rgba(0,0,0,0.5)', @@ -316,53 +338,49 @@ const styles = StyleSheet.create({ left: 0, right: 0, backgroundColor: '#1a1a1a', - borderTopLeftRadius: 20, - borderTopRightRadius: 20, - paddingHorizontal: 20, - paddingTop: 12, + borderTopLeftRadius: 24, + borderTopRightRadius: 24, + paddingHorizontal: 24, + paddingTop: 14, }, bottomSheetHandle: { - width: 36, + width: 40, height: 4, backgroundColor: 'rgba(255,255,255,0.2)', borderRadius: 2, alignSelf: 'center', - marginBottom: 16, + marginBottom: 18, }, bottomSheetClose: { position: 'absolute', - top: 16, - right: 16, + top: 18, + right: 18, zIndex: 10, padding: 4, }, bottomSheetImage: { width: '100%', - borderRadius: 10, - marginBottom: 16, + borderRadius: 12, + marginBottom: 18, }, bottomSheetContent: { - marginBottom: 20, + marginBottom: 22, }, bottomSheetTitle: { - fontSize: 20, fontWeight: '600', - marginBottom: 8, + marginBottom: 10, textAlign: 'center', }, bottomSheetMessage: { - fontSize: 14, opacity: 0.8, textAlign: 'center', - lineHeight: 20, + lineHeight: 22, }, bottomSheetButton: { - paddingVertical: 14, - borderRadius: 24, + borderRadius: 26, alignItems: 'center', }, bottomSheetButtonText: { - fontSize: 15, fontWeight: '600', }, }); diff --git a/src/components/promotions/PosterModal.tsx b/src/components/promotions/PosterModal.tsx index 61cc4c7e..9126fa98 100644 --- a/src/components/promotions/PosterModal.tsx +++ b/src/components/promotions/PosterModal.tsx @@ -4,14 +4,11 @@ import { Text, StyleSheet, TouchableOpacity, - Dimensions, Image, Linking, + useWindowDimensions, } from 'react-native'; -import Animated, { - FadeIn, - FadeOut, -} from 'react-native-reanimated'; +import Animated, { FadeIn, FadeOut } from 'react-native-reanimated'; import { BlurView } from 'expo-blur'; import { Ionicons } from '@expo/vector-icons'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; @@ -23,17 +20,27 @@ interface PosterModalProps { onAction: (action: any) => void; } -const { width: SCREEN_WIDTH } = Dimensions.get('window'); - export const PosterModal: React.FC = ({ campaign, onDismiss, onAction, }) => { const insets = useSafeAreaInsets(); + const { width, height } = useWindowDimensions(); const { content } = campaign; const isPosterOnly = !content.title && !content.message; + const isTablet = width >= 768; + const isLandscape = width > height; + + const modalWidth = isTablet + ? Math.min(width * 0.5, 420) + : isLandscape + ? Math.min(width * 0.45, 360) + : Math.min(width * 0.85, 340); + + const maxImageHeight = isLandscape ? height * 0.6 : height * 0.5; + const handleAction = () => { if (content.primaryAction) { if (content.primaryAction.type === 'link' && content.primaryAction.value) { @@ -50,17 +57,12 @@ export const PosterModal: React.FC = ({ return ( - {/* Backdrop */} - + = ({ /> - {/* Modal Container */} = ({ ]} pointerEvents="box-none" > - - {/* Close Button */} + = ({ - {/* Main Image */} {content.imageUrl && ( = ({ )} - {/* Text Content */} {!isPosterOnly && ( {content.title && ( - + {content.title} )} {content.message && ( - + {content.message} )} )} - {/* Primary Action Button */} {content.primaryAction && ( = ({ { backgroundColor: content.textColor || '#fff', marginTop: isPosterOnly ? 16 : 0, + paddingVertical: isTablet ? 16 : 14, + minWidth: isTablet ? 220 : 180, } ]} onPress={handleAction} @@ -142,7 +159,10 @@ export const PosterModal: React.FC = ({ > {content.primaryAction.label} @@ -167,7 +187,6 @@ const styles = StyleSheet.create({ zIndex: 999, }, contentWrapper: { - width: Math.min(SCREEN_WIDTH * 0.85, 340), alignItems: 'center', }, closeButton: { @@ -177,16 +196,16 @@ const styles = StyleSheet.create({ zIndex: 1000, }, closeButtonBg: { - width: 32, - height: 32, - borderRadius: 16, + width: 36, + height: 36, + borderRadius: 18, backgroundColor: 'rgba(0,0,0,0.5)', alignItems: 'center', justifyContent: 'center', }, imageContainer: { width: '100%', - borderRadius: 12, + borderRadius: 14, overflow: 'hidden', backgroundColor: '#222', }, @@ -196,33 +215,27 @@ const styles = StyleSheet.create({ }, textContainer: { width: '100%', - padding: 20, - borderBottomLeftRadius: 12, - borderBottomRightRadius: 12, + borderBottomLeftRadius: 14, + borderBottomRightRadius: 14, marginTop: -2, }, title: { - fontSize: 20, fontWeight: '600', marginBottom: 6, textAlign: 'center', }, message: { - fontSize: 14, - lineHeight: 20, + lineHeight: 22, textAlign: 'center', opacity: 0.85, }, actionButton: { - paddingVertical: 14, paddingHorizontal: 32, borderRadius: 24, marginTop: 16, - minWidth: 180, alignItems: 'center', }, actionButtonText: { - fontSize: 15, fontWeight: '600', }, }); diff --git a/src/services/campaignService.ts b/src/services/campaignService.ts index 47a90b75..aad80bb7 100644 --- a/src/services/campaignService.ts +++ b/src/services/campaignService.ts @@ -1,7 +1,7 @@ import { mmkvStorage } from './mmkvStorage'; import { Platform } from 'react-native'; -const DEV_URL = 'http://192.168.1.5:3000'; +const DEV_URL = 'https://campaign.nuvioapp.space/'; const PROD_URL = process.env.EXPO_PUBLIC_CAMPAIGN_API_URL || ''; const CAMPAIGN_API_URL = __DEV__ ? DEV_URL : PROD_URL;