{
    "name": "zafer-skills",
    "version": "1.0.0",
    "description": "Expo React Native mobile app development with RevenueCat payments, AdMob ads, i18n localization, onboarding flow, paywall, and NativeTabs navigation",
    "system_prompt": "name zafer-skills description Expo React Native mobile app development with RevenueCat payments, AdMob ads, i18n localization, onboarding flow, paywall, and NativeTabs navigation Expo Mobile Application Development Guide IMPORTANT : This is a SKILL file, NOT a project. NEVER run npm/bun install in this folder. NEVER create code files here. When creating a new project, ALWAYS ask the user for the project path first or create it in a separate directory (e.g., ~/Projects/app-name ). This guide is created to provide context when working with Expo projects using Claude Code. MANDATORY REQUIREMENTS When creating a new Expo project, you MUST include ALL of the following: Required Screens (ALWAYS CREATE) src/app/onboarding.tsx - Swipe-based onboarding with fullscreen background video and gradient overlay src/app/paywall.tsx - RevenueCat paywall screen (shown after onboarding) src/app/settings.tsx - Settings screen with language, theme, notifications, and reset onboarding options Onboarding Video Implementation (REQUIRED) The onboarding screen MUST have a fullscreen background video. Use a URL, not a local file: import { useVideoPlayer, VideoView } from \"expo-video\" ; const VIDEO_URL = \"https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4\" ; const player = useVideoPlayer ( VIDEO_URL , ( player ) => { player. loop = true ; player. muted = true ; player. play (); }); // In render: < VideoView player = {player} style = {StyleSheet.absoluteFill} contentFit = \"cover\" nativeControls = {false} /> ; Do NOT just import expo-video without actually using the VideoView component. Required Navigation (ALWAYS USE) Use NativeTabs from expo-router/unstable-native-tabs for tab navigation - NEVER use @react-navigation/bottom-tabs or Tabs from expo-router Required Context Providers (ALWAYS WRAP) import { ThemeProvider } from \"@/context/theme-context\" ; import { DarkTheme , DefaultTheme , ThemeProvider as NavigationThemeProvider , } from \"@react-navigation/native\" ; < ThemeProvider > < OnboardingProvider > < AdsProvider > < NavigationThemeProvider value = {colorScheme === \"dark\" ? DarkTheme : DefaultTheme } > < Stack /> </ NavigationThemeProvider > </ AdsProvider > </ OnboardingProvider > </ ThemeProvider > ; Required Libraries (ALWAYS INSTALL) Use npx expo install to install libraries (NOT npm/yarn/bun install): npx expo install react-native-purchases react-native-google-mobile-ads expo-notifications i18next react-i18next expo-localization react-native-reanimated expo-video expo-audio expo-sqlite expo-linear-gradient Libraries: react-native-purchases (RevenueCat) react-native-google-mobile-ads (AdMob) expo-notifications i18next + react-i18next + expo-localization react-native-reanimated expo-video + expo-audio expo-sqlite (for localStorage) expo-linear-gradient (for gradient overlays) AdMob Configuration (REQUIRED in app.json) You MUST add this to app.json for AdMob to work: { \"expo\" : { \"plugins\" : [ [ \"react-native-google-mobile-ads\" , { \"androidAppId\" : \"ca-app-pub-xxxxxxxxxxxxxxxx~yyyyyyyyyy\" , \"iosAppId\" : \"ca-app-pub-xxxxxxxxxxxxxxxx~yyyyyyyyyy\" } ] ] } } For development/testing, use test App IDs: iOS: ca-app-pub-3940256099942544~1458002511 Android: ca-app-pub-3940256099942544~3347511713 Do NOT skip this configuration or the app will crash with GADInvalidInitializationException . Banner Ad Implementation (REQUIRED) You MUST implement banner ads in the Tab layout. Use this pattern: import { View , StyleSheet } from 'react-native' ; import { NativeTabs } from 'expo-router/unstable-native-tabs' ; import { useTranslation } from 'react-i18next' ; import { BannerAd , BannerAdSize , TestIds } from 'react-native-google-mobile-ads' ; import { useAds } from '@/context/ads-context' ; const adUnitId = __DEV__ ? TestIds . BANNER : 'ca-app-pub-xxxxxxxxxxxxxxxx/yyyyyyyyyy' ; export default function TabLayout ( ) { const { t } = useTranslation (); const { shouldShowAds } = useAds (); return ( < View style = {styles.container} > < NativeTabs > < NativeTabs.Trigger name = \"index\" > < NativeTabs.Trigger.Label > {t('tabs.home')} </ NativeTabs.Trigger.Label > < NativeTabs.Trigger.Icon sf = \"house.fill\" md = \"home\" /> </ NativeTabs.Trigger > < NativeTabs.Trigger name = \"settings\" > < NativeTabs.Trigger.Label > {t('tabs.settings')} </ NativeTabs.Trigger.Label > < NativeTabs.Trigger.Icon sf = \"gear\" md = \"settings\" /> </ NativeTabs.Trigger > </ NativeTabs > {shouldShowAds && ( < View style = {styles.adContainer} > < BannerAd unitId = {adUnitId} size = {BannerAdSize.ANCHORED_ADAPTIVE_BANNER} requestOptions = {{ requestNonPersonalizedAdsOnly: true , }} /> </ View > )} </ View > ); } const styles = StyleSheet . create ({ container : { flex : 1 , }, adContainer : { alignItems : 'center' , paddingBottom : 10 , }, }); ALWAYS use TestIds.BANNER in development Banner ad is placed below NativeTabs in the Tab layout Use useAds context to check shouldShowAds (hides for premium users) TURKISH LOCALIZATION (IMPORTANT) When writing tr.json , you MUST use correct Turkish characters: ı (lowercase dotless i) - NOT i İ (uppercase dotted I) - NOT I ü, Ü, ö, Ö, ç, Ç, ş, Ş, ğ, Ğ Example: ✅ \"Ayarlar\", \"Giriş\", \"Çıkış\", \"Başla\", \"İleri\", \"Güncelle\" ❌ \"Ayarlar\", \"Giris\", \"Cikis\", \"Basla\", \"Ileri\", \"Guncelle\" FORBIDDEN (NEVER USE) ❌ AsyncStorage - Use expo-sqlite/localStorage/install instead ❌ lineHeight style - Use padding/margin instead ❌ Tabs from expo-router - Use NativeTabs instead ❌ @react-navigation/bottom-tabs - Use NativeTabs instead ❌ expo-av - Use expo-video for video, expo-audio for audio instead ❌ expo-ads-admob - Use react-native-google-mobile-ads instead ❌ Any other ads library - ONLY use react-native-google-mobile-ads ❌ Reanimated hooks inside callbacks - Call at component top level Reanimated Usage (IMPORTANT) NEVER call useAnimatedStyle , useSharedValue , or other reanimated hooks inside callbacks, loops, or conditions. ❌ WRONG: const renderItem = ( ) => { const animatedStyle = useAnimatedStyle ( () => ({ opacity : 1 })); // ERROR! return < Animated.View style = {animatedStyle} /> ; }; ✅ CORRECT: function MyComponent ( ) { const animatedStyle = useAnimatedStyle ( () => ({ opacity : 1 })); // Top level return < Animated.View style = {animatedStyle} /> ; } For lists, create a separate component for each item: function AnimatedItem ( { item } ) { const animatedStyle = useAnimatedStyle ( () => ({ opacity : 1 })); return < Animated.View style = {animatedStyle} > {item.name} </ Animated.View > ; } // In FlatList: renderItem={ ( { item } ) => < AnimatedItem item = {item} /> } POST-CREATION CLEANUP (ALWAYS DO) After creating a new Expo project, you MUST: If using (tabs) folder, DELETE src/app/index.tsx to avoid route conflicts: rm src/app/index.tsx Check and remove lineHeight from these files: src/components/themed-text.tsx (comes with lineHeight by default - REMOVE IT) Any other component using lineHeight Search and remove all lineHeight occurrences: grep -r \"lineHeight\" src/ Replace with padding or margin instead. AFTER COMPLETING CODE (ALWAYS RUN) When you finish writing/modifying code, you MUST run these commands in order: npx expo install --fix npx expo prebuild --clean install --fix fixes dependency version mismatches prebuild --clean recreates ios and android folders Do NOT skip these steps. Project Creation When user asks to create an app, you MUST: FIRST ask for the bundle ID (e.g., \"What is the bundle ID? Example: com.company.appname\") Create the project in the CURRENT directory using: bunx create-expo -t default@next app-name Update app.json with the bundle ID: { \"expo\" : { \"ios\" : { \"bundleIdentifier\" : \"com.company.appname\" } , \"android\" : { \"package\" : \"com.company.appname\" } } } Then cd into the project and start implementing all required screens Do NOT ask for project path - always use current directory Technology Stack Framework : Expo, React Native Navigation : Expo Router (file-based routing), NativeTabs State Management : React Context API Translations : i18next, react-i18next Purchases : RevenueCat (react-native-purchases) Advertisements : Google AdMob (react-native-google-mobile-ads) Notifications : expo-notifications Animations : react-native-reanimated Storage : localStorage via expo-sqlite polyfill WARNING : DO NOT USE AsyncStorage! Use expo-sqlite polyfill instead. Example usage import \"expo-sqlite/localStorage/install\" ; globalThis. localStorage . setItem ( \"key\" , \"value\" ); console . log (globalThis. localStorage . getItem ( \"key\" )); // 'value' WARNING : NEVER USE lineHeight ! It causes layout issues in React Native. Use padding or margin instead. Project Structure project-root/ ├── src/ │ ├── app/ │ │ ├── _layout.tsx │ │ ├── index.tsx │ │ ├── explore.tsx │ │ ├── settings.tsx │ │ ├── paywall.tsx │ │ └── onboarding.tsx │ ├── components/ │ │ ├── ui/ │ │ ├── themed-text.tsx │ │ └── themed-view.tsx │ ├── constants/ │ │ ├── theme.ts │ │ └── [data-files].ts │ ├── context/ │ │ ├── onboarding-context.tsx │ │ └── ads-context.tsx │ ├── hooks/ │ │ ├── use-notifications.ts │ │ └── use-color-scheme.ts │ ├── lib/ │ │ ├── notifications.ts │ │ ├── purchases.ts │ │ ├── ads.ts │ │ └── i18n.ts │ └── locales/ │ ├── tr.json │ └── en.json ├── assets/ │ └── images/ ├── ios/ ├── android/ ├── app.json ├── eas.json ├── package.json └── tsconfig.json Tab Navigation (NativeTabs) Expo Router uses NativeTabs for native tab navigation: import { NativeTabs } from \"expo-router/unstable-native-tabs\" ; export default function TabLayout ( ) { return ( < NativeTabs > < NativeTabs.Trigger name = \"index\" > < NativeTabs.Trigger.Label > Home </ NativeTabs.Trigger.Label > < NativeTabs.Trigger.Icon sf = \"house.fill\" md = \"home\" /> </ NativeTabs.Trigger > < NativeTabs.Trigger name = \"explore\" > < NativeTabs.Trigger.Label > Explore </ NativeTabs.Trigger.Label > < NativeTabs.Trigger.Icon sf = \"compass.fill\" md = \"explore\" /> </ NativeTabs.Trigger > < NativeTabs.Trigger name = \"settings\" > < NativeTabs.Trigger.Label > Settings </ NativeTabs.Trigger.Label > < NativeTabs.Trigger.Icon sf = \"gear\" md = \"settings\" /> </ NativeTabs.Trigger > </ NativeTabs > ); } NativeTabs Properties sf : SF Symbols icon name (iOS) md : Material Design icon name (Android) name : Route file name Tab order follows trigger order Common Icons Purpose SF Symbol Material Icon Home house.fill home Explore compass.fill explore Settings gear settings Profile person.fill person Search magnifyingglass search Favorites heart.fill favorite Notifications bell.fill notifications Development Commands bun install bun start bun ios bun android bun lint npx expo install --fix npx expo prebuild --clean EAS Build Commands eas build --profile development --platform ios eas build --profile development --platform android eas build --profile production --platform ios eas build --profile production --platform android",
    "model_config": {
        "provider": "deepseek",
        "model": "deepseek-chat",
        "temperature": 0.7,
        "max_tokens": 4096,
        "top_p": 0.9
    },
    "trigger_words": [],
    "source": "DeepseekModel",
    "source_url": "https://deepseekmodel.com/skill?id=zaferayan-skills-skill-md"
}