From 5ef532bdb5d791bc882a9d8849fb8457bad41d94 Mon Sep 17 00:00:00 2001 From: boztopuz Date: Mon, 10 Mar 2025 13:27:41 +0300 Subject: [PATCH 01/36] Test for new server --- App.tsx | 2 +- ios/RNCodepushTest/Info.plist | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/App.tsx b/App.tsx index c945896..0cfd99a 100644 --- a/App.tsx +++ b/App.tsx @@ -54,7 +54,7 @@ const App: React.FC = () => { alignContent: 'center', justifyContent: 'center', }}> - {`Rollback öncesi codepush testNew.\n${currentVersion}\n`} + {`CodePush New Server Test.\n${currentVersion}\n`} UIViewControllerBasedStatusBarAppearance CodePushDeploymentKey - eO--yguuyKlipG5b1ooFfio6zCmIdgVZF6orGs + dk_0t0JjZN4IfN04ojJM6K4ZA2N1ri4pcohs2oam9fjugobzww61yke + CodePushServerURL + https://codepush.cenkce.com From 7834761b57bd9fc2b2cd112c13a0dc4f10ac785a Mon Sep 17 00:00:00 2001 From: boztopuz Date: Thu, 20 Mar 2025 11:06:30 +0300 Subject: [PATCH 02/36] =?UTF-8?q?Release=20testleri=20i=C3=A7in=20yeni=20c?= =?UTF-8?q?ommit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/App.tsx b/App.tsx index 0cfd99a..76d5cf4 100644 --- a/App.tsx +++ b/App.tsx @@ -54,7 +54,7 @@ const App: React.FC = () => { alignContent: 'center', justifyContent: 'center', }}> - {`CodePush New Server Test.\n${currentVersion}\n`} + {`20.03.25 tarihindeki codepush test için build.\n${currentVersion}\n`} Date: Tue, 15 Apr 2025 16:12:45 +0300 Subject: [PATCH 03/36] Added new screens for tests --- App.tsx | 112 +++-- ios/RNCodepushTest.xcodeproj/project.pbxproj | 10 +- package.json | 9 +- src/components/buttons/Button.tsx | 143 ++++++ src/components/cards/Card.tsx | 101 ++++ src/navigation/index.tsx | 144 ++++++ src/navigation/types.ts | 17 + src/screens/FeedScreen.tsx | 261 ++++++++++ src/screens/HomeScreen.tsx | 295 ++++++++++++ src/screens/NotificationsScreen.tsx | 305 ++++++++++++ src/screens/ProfileScreen.tsx | 480 +++++++++++++++++++ src/theme/colors.ts | 19 + src/theme/index.ts | 9 + src/theme/spacing.ts | 8 + src/theme/typography.ts | 17 + src/utils/analytics.ts | 105 ++++ src/utils/cache.ts | 87 ++++ src/utils/featureFlags.ts | 73 +++ src/utils/mockData.ts | 293 +++++++++++ 19 files changed, 2437 insertions(+), 51 deletions(-) create mode 100644 src/components/buttons/Button.tsx create mode 100644 src/components/cards/Card.tsx create mode 100644 src/navigation/index.tsx create mode 100644 src/navigation/types.ts create mode 100644 src/screens/FeedScreen.tsx create mode 100644 src/screens/HomeScreen.tsx create mode 100644 src/screens/NotificationsScreen.tsx create mode 100644 src/screens/ProfileScreen.tsx create mode 100644 src/theme/colors.ts create mode 100644 src/theme/index.ts create mode 100644 src/theme/spacing.ts create mode 100644 src/theme/typography.ts create mode 100644 src/utils/analytics.ts create mode 100644 src/utils/cache.ts create mode 100644 src/utils/featureFlags.ts create mode 100644 src/utils/mockData.ts diff --git a/App.tsx b/App.tsx index 76d5cf4..245d85f 100644 --- a/App.tsx +++ b/App.tsx @@ -1,73 +1,91 @@ -import React, {useEffect, useState} from 'react'; -import {View, Text, Alert, BackHandler} from 'react-native'; -// import CodePush from '@chlee1001/react-native-code-push'; -import {version as currentVersion} from './package.json'; +import React, { useEffect, useState } from 'react'; +import { SafeAreaView, StatusBar, View, StyleSheet, LogBox } from 'react-native'; import CodePush from '@chlee1001/react-native-code-push'; +import Navigation from './src/navigation'; +import { colors } from './src/theme/colors'; import Snackbar from './src/components/common/snackbar'; -// import Snackbar from './src/components/common/snackbar.tsx'; -// import CodePush from '@chlee1001/react-native-code-push'; -// import { useCodePush } from './src/hooks/useCodePush.ts'; +import { analyticsService } from './src/utils/analytics'; +import { featureFlagsService } from './src/utils/featureFlags'; + +// Ignore specific LogBox warnings +LogBox.ignoreLogs([ + 'VirtualizedLists should never be nested', + 'Warning: componentWillReceiveProps has been renamed', +]); const App: React.FC = () => { - const [snackbarVisible, setSnackbarVisible] = useState(false); - // const {isUpdateDownloaded} = useCodePush(); + const [snackbarVisible, setSnackbarVisible] = useState(false); + const [updateMessage, setUpdateMessage] = useState( + 'The app has been updated. Please restart to apply changes.' + ); useEffect(() => { + // Initialize analytics + analyticsService.startNewSession(); + analyticsService.setEnabled(featureFlagsService.isEnabled('enableAnalytics')); + analyticsService.trackEvent('app_launched'); + + // Check for CodePush updates CodePush.sync( { installMode: CodePush.InstallMode.ON_NEXT_RESTART, }, (syncStatus) => { - // Güncelleme yüklendiyse snackbar'ı göster if (syncStatus === CodePush.SyncStatus.UPDATE_INSTALLED) { + setUpdateMessage('The app has been updated. Please restart to apply changes.'); setSnackbarVisible(true); } } ); - }, []); - // Press the back button to exit the app - useEffect(() => { - const backAction = () => { - Alert.alert('알림', '앱 종료', [ - { - text: '취소', - onPress: () => null, - style: 'cancel', - }, - {text: '확인', onPress: () => BackHandler.exitApp()}, - ]); - return true; + // Check update status + const checkUpdateStatus = async () => { + try { + const update = await CodePush.getUpdateMetadata(); + if (update) { + analyticsService.trackEvent('codepush_update_status', { + label: update.label, + description: update.description, + isFirstRun: update.isFirstRun, + }); + } + } catch (error) { + console.error('Error checking update status:', error); + } }; - const backHandler = BackHandler.addEventListener( - 'hardwareBackPress', - backAction, - ); - return () => backHandler.remove(); + + checkUpdateStatus(); }, []); return ( - - {`20.03.25 tarihindeki codepush test için build.\n${currentVersion}\n`} - + + + + setSnackbarVisible(false)} - actionLabel="Restart" - onActionPress={() => CodePush.restartApp()} - autoHide={false} - swipeToDismiss + visible={snackbarVisible} + message={updateMessage} + onDismiss={() => setSnackbarVisible(false)} + actionLabel="Restart" + onActionPress={() => CodePush.restartApp()} + autoHide={false} + swipeToDismiss /> - + ); }; -export default CodePush({checkFrequency: CodePush.CheckFrequency.MANUAL})(App); -// export default App; +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: colors.background, + }, +}); + +export default CodePush({ + checkFrequency: CodePush.CheckFrequency.ON_APP_START, + installMode: CodePush.InstallMode.ON_NEXT_RESTART, +})(App); diff --git a/ios/RNCodepushTest.xcodeproj/project.pbxproj b/ios/RNCodepushTest.xcodeproj/project.pbxproj index 825c2a5..5aa9e78 100644 --- a/ios/RNCodepushTest.xcodeproj/project.pbxproj +++ b/ios/RNCodepushTest.xcodeproj/project.pbxproj @@ -386,7 +386,10 @@ "-DFOLLY_CFG_NO_COROUTINES=1", "-DFOLLY_HAVE_CLOCK_GETTIME=1", ); - OTHER_LDFLAGS = "$(inherited) "; + OTHER_LDFLAGS = ( + "$(inherited)", + " ", + ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG"; @@ -455,7 +458,10 @@ "-DFOLLY_CFG_NO_COROUTINES=1", "-DFOLLY_HAVE_CLOCK_GETTIME=1", ); - OTHER_LDFLAGS = "$(inherited) "; + OTHER_LDFLAGS = ( + "$(inherited)", + " ", + ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; USE_HERMES = true; diff --git a/package.json b/package.json index 90443bd..88a6301 100644 --- a/package.json +++ b/package.json @@ -13,8 +13,14 @@ }, "dependencies": { "@chlee1001/react-native-code-push": "^10.0.3", + "@react-navigation/bottom-tabs": "^6.5.14", + "@react-navigation/native": "^6.1.12", + "@react-navigation/native-stack": "^6.9.20", "react": "18.3.1", - "react-native": "~0.76.7" + "react-native": "~0.76.7", + "react-native-iphone-screen-helper": "2.1.2", + "react-native-safe-area-context": "^4.9.0", + "react-native-screens": "^3.29.0" }, "devDependencies": { "@babel/core": "^7.25.2", @@ -33,7 +39,6 @@ "eslint": "^8.19.0", "jest": "^29.6.3", "prettier": "2.8.8", - "react-native-iphone-screen-helper": "2.1.2", "react-test-renderer": "18.3.1", "typescript": "5.0.4" }, diff --git a/src/components/buttons/Button.tsx b/src/components/buttons/Button.tsx new file mode 100644 index 0000000..327d4c7 --- /dev/null +++ b/src/components/buttons/Button.tsx @@ -0,0 +1,143 @@ +import React from 'react'; +import { + StyleSheet, + TouchableOpacity, + Text, + ActivityIndicator, + ViewStyle, + TextStyle, + TouchableOpacityProps, +} from 'react-native'; +import { colors } from '../../theme/colors'; +import { spacing } from '../../theme/spacing'; +import { typography } from '../../theme/typography'; + +interface ButtonProps extends TouchableOpacityProps { + title: string; + variant?: 'primary' | 'secondary' | 'outline' | 'text'; + size?: 'small' | 'medium' | 'large'; + isLoading?: boolean; + disabled?: boolean; + fullWidth?: boolean; + style?: ViewStyle; + textStyle?: TextStyle; +} + +const Button: React.FC = ({ + title, + variant = 'primary', + size = 'medium', + isLoading = false, + disabled = false, + fullWidth = false, + style, + textStyle, + ...props +}) => { + const getButtonStyle = (): ViewStyle => { + const baseStyle: ViewStyle = { + borderRadius: 8, + justifyContent: 'center', + alignItems: 'center', + flexDirection: 'row', + }; + + // Size variations + switch (size) { + case 'small': + baseStyle.paddingVertical = spacing.xs; + baseStyle.paddingHorizontal = spacing.md; + break; + case 'medium': + baseStyle.paddingVertical = spacing.sm; + baseStyle.paddingHorizontal = spacing.lg; + break; + case 'large': + baseStyle.paddingVertical = spacing.md; + baseStyle.paddingHorizontal = spacing.xl; + break; + } + + // Variant styles + switch (variant) { + case 'primary': + baseStyle.backgroundColor = colors.primary; + break; + case 'secondary': + baseStyle.backgroundColor = colors.secondary; + break; + case 'outline': + baseStyle.backgroundColor = colors.transparent; + baseStyle.borderWidth = 1; + baseStyle.borderColor = colors.primary; + break; + case 'text': + baseStyle.backgroundColor = colors.transparent; + break; + } + + // Full width + if (fullWidth) { + baseStyle.width = '100%'; + } + + // Disabled state + if (disabled) { + baseStyle.opacity = 0.5; + } + + return baseStyle; + }; + + const getTextStyle = (): TextStyle => { + const baseStyle: TextStyle = { + fontWeight: typography.fontWeights.medium as TextStyle['fontWeight'], + }; + + // Text size based on button size + switch (size) { + case 'small': + baseStyle.fontSize = typography.fontSizes.sm; + break; + case 'medium': + baseStyle.fontSize = typography.fontSizes.md; + break; + case 'large': + baseStyle.fontSize = typography.fontSizes.lg; + break; + } + + // Text color based on variant + switch (variant) { + case 'primary': + case 'secondary': + baseStyle.color = colors.white; + break; + case 'outline': + case 'text': + baseStyle.color = colors.primary; + break; + } + + return baseStyle; + }; + + return ( + + {isLoading ? ( + + ) : null} + {title} + + ); +}; + +export default Button; diff --git a/src/components/cards/Card.tsx b/src/components/cards/Card.tsx new file mode 100644 index 0000000..191a8de --- /dev/null +++ b/src/components/cards/Card.tsx @@ -0,0 +1,101 @@ +import React from 'react'; +import { + View, + Text, + StyleSheet, + ViewStyle, + TextStyle, + TouchableOpacity, +} from 'react-native'; +import { colors } from '../../theme/colors'; +import { spacing } from '../../theme/spacing'; +import { typography } from '../../theme/typography'; + +interface CardProps { + title?: string; + subtitle?: string; + children?: React.ReactNode; + style?: ViewStyle; + titleStyle?: TextStyle; + subtitleStyle?: TextStyle; + onPress?: () => void; + elevation?: number; + bordered?: boolean; +} + +const Card: React.FC = ({ + title, + subtitle, + children, + style, + titleStyle, + subtitleStyle, + onPress, + elevation = 2, + bordered = false, +}) => { + const renderContent = () => ( + + {(title || subtitle) && ( + + {title && {title}} + {subtitle && ( + {subtitle} + )} + + )} + {children && {children}} + + ); + + if (onPress) { + return ( + + {renderContent()} + + ); + } + + return renderContent(); +}; + +const styles = StyleSheet.create({ + container: { + backgroundColor: colors.white, + borderRadius: 12, + borderColor: colors.grayLight, + marginVertical: spacing.sm, + shadowColor: colors.black, + shadowOffset: { width: 0, height: 2 }, + shadowRadius: 6, + }, + header: { + padding: spacing.md, + borderBottomWidth: 1, + borderBottomColor: colors.light, + }, + title: { + fontSize: typography.fontSizes.lg, + fontWeight: typography.fontWeights.semiBold as TextStyle['fontWeight'], + color: colors.dark, + marginBottom: title => (title ? spacing.xs : 0), + }, + subtitle: { + fontSize: typography.fontSizes.md, + color: colors.gray, + }, + content: { + padding: spacing.md, + }, +}); + +export default Card; diff --git a/src/navigation/index.tsx b/src/navigation/index.tsx new file mode 100644 index 0000000..8c88ef7 --- /dev/null +++ b/src/navigation/index.tsx @@ -0,0 +1,144 @@ +import React from 'react'; +import { NavigationContainer } from '@react-navigation/native'; +import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; +import { createNativeStackNavigator } from '@react-navigation/native-stack'; +import { Text, View, StyleSheet } from 'react-native'; +import { RootStackParamList, MainTabParamList } from './types'; +import { colors } from '../theme/colors'; +import { typography } from '../theme/typography'; + +// Screens +import HomeScreen from '../screens/HomeScreen'; +import FeedScreen from '../screens/FeedScreen'; +import NotificationsScreen from '../screens/NotificationsScreen'; +import ProfileScreen from '../screens/ProfileScreen'; + +// Create navigators +const Stack = createNativeStackNavigator(); +const Tab = createBottomTabNavigator(); + +// Tab Bar Icon component +const TabBarIcon = ({ focused, name }: { focused: boolean; name: string }) => { + const getIcon = () => { + switch (name) { + case 'Home': + return '🏠'; + case 'Feed': + return '📱'; + case 'Profile': + return '👤'; + case 'Notifications': + return '🔔'; + default: + return '📎'; + } + }; + + return ( + + {getIcon()} + + ); +}; + +// Tab Navigator +const MainTabNavigator = () => { + return ( + + ( + + ), + }} + /> + ( + + ), + }} + /> + ( + + ), + tabBarBadge: 3, // Hardcoded for simplicity, would normally be dynamic + tabBarBadgeStyle: { + backgroundColor: colors.danger, + }, + }} + /> + ( + + ), + }} + /> + + ); +}; + +// Main Navigation +const Navigation: React.FC = () => { + return ( + + + + + + ); +}; + +const styles = StyleSheet.create({ + iconContainer: { + width: 30, + height: 30, + borderRadius: 15, + justifyContent: 'center', + alignItems: 'center', + }, + iconText: { + fontSize: 18, + }, +}); + +export default Navigation; diff --git a/src/navigation/types.ts b/src/navigation/types.ts new file mode 100644 index 0000000..b13350d --- /dev/null +++ b/src/navigation/types.ts @@ -0,0 +1,17 @@ +import { NavigatorScreenParams } from '@react-navigation/native'; + +export type MainTabParamList = { + Home: undefined; + Feed: undefined; + Profile: undefined; + Settings: undefined; +}; + +export type RootStackParamList = { + MainTabs: NavigatorScreenParams; + PostDetail: { postId: string }; + UserProfile: { userId: string }; + Notifications: undefined; + Settings: undefined; + About: undefined; +}; diff --git a/src/screens/FeedScreen.tsx b/src/screens/FeedScreen.tsx new file mode 100644 index 0000000..4ef003f --- /dev/null +++ b/src/screens/FeedScreen.tsx @@ -0,0 +1,261 @@ +import React, { useState, useEffect } from 'react'; +import { + View, + Text, + FlatList, + StyleSheet, + Image, + TouchableOpacity, + ActivityIndicator, + RefreshControl, +} from 'react-native'; +import { mockPosts, Post } from '../utils/mockData'; +import { colors } from '../theme/colors'; +import { spacing } from '../theme/spacing'; +import { typography } from '../theme/typography'; +import Card from '../components/cards/Card'; +import { analyticsService } from '../utils/analytics'; + +const FeedScreen: React.FC = () => { + const [posts, setPosts] = useState([]); + const [loading, setLoading] = useState(true); + const [refreshing, setRefreshing] = useState(false); + + const loadPosts = () => { + // Simulate API call with delay + setLoading(true); + setTimeout(() => { + setPosts(mockPosts); + setLoading(false); + }, 1000); + }; + + const onRefresh = () => { + // Simulate pull-to-refresh + setRefreshing(true); + setTimeout(() => { + // Shuffle the posts to simulate new content + const shuffled = [...mockPosts].sort(() => 0.5 - Math.random()); + setPosts(shuffled); + setRefreshing(false); + }, 1500); + }; + + useEffect(() => { + loadPosts(); + analyticsService.trackScreenView('Feed'); + }, []); + + const renderPost = ({ item }: { item: Post }) => { + const formattedDate = new Date(item.createdAt).toLocaleDateString('en-US', { + year: 'numeric', + month: 'short', + day: 'numeric', + }); + + return ( + { + // Handle post tap + analyticsService.trackEvent('post_tap', { post_id: item.id }); + }}> + + + + {item.author.name} + {formattedDate} + + + + {item.imageUrl && ( + + )} + + + {item.title} + + {item.body} + + + + + { + // Track Like event + analyticsService.trackEvent('post_like', { post_id: item.id }); + }}> + ❤️ {item.likes} + + { + // Track Comment event + analyticsService.trackEvent('post_comment', { post_id: item.id }); + }}> + 💬 {item.comments} + + { + // Track Share event + analyticsService.trackEvent('post_share', { post_id: item.id }); + }}> + 🔄 Share + + + + ); + }; + + if (loading && !refreshing) { + return ( + + + Loading posts... + + ); + } + + return ( + + item.id} + contentContainerStyle={styles.flatListContent} + showsVerticalScrollIndicator={false} + refreshControl={ + + } + ListHeaderComponent={ + + Your Feed + + Stay updated with the latest posts + + + } + ListEmptyComponent={ + + No posts found + + } + /> + + ); +}; + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: colors.background, + }, + loadingContainer: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + backgroundColor: colors.background, + }, + loadingText: { + marginTop: spacing.md, + fontSize: typography.fontSizes.md, + color: colors.gray, + }, + flatListContent: { + paddingHorizontal: spacing.md, + paddingBottom: spacing.xxl, + }, + card: { + marginVertical: spacing.sm, + }, + header: { + marginVertical: spacing.lg, + }, + headerTitle: { + fontSize: typography.fontSizes.xxl, + fontWeight: typography.fontWeights.bold as any, + color: colors.dark, + }, + headerSubtitle: { + fontSize: typography.fontSizes.md, + color: colors.gray, + marginTop: spacing.xs, + }, + postHeader: { + flexDirection: 'row', + alignItems: 'center', + marginBottom: spacing.md, + }, + avatar: { + width: 40, + height: 40, + borderRadius: 20, + }, + authorContainer: { + marginLeft: spacing.sm, + }, + authorName: { + fontSize: typography.fontSizes.md, + fontWeight: typography.fontWeights.semiBold as any, + color: colors.dark, + }, + postDate: { + fontSize: typography.fontSizes.xs, + color: colors.gray, + }, + postImage: { + height: 200, + borderRadius: 8, + marginBottom: spacing.md, + }, + postContent: { + marginBottom: spacing.md, + }, + postTitle: { + fontSize: typography.fontSizes.lg, + fontWeight: typography.fontWeights.semiBold as any, + color: colors.dark, + marginBottom: spacing.xs, + }, + postBody: { + fontSize: typography.fontSizes.md, + color: colors.grayDark, + lineHeight: 22, + }, + postFooter: { + flexDirection: 'row', + borderTopWidth: 1, + borderTopColor: colors.light, + paddingTop: spacing.sm, + }, + footerButton: { + flex: 1, + flexDirection: 'row', + justifyContent: 'center', + alignItems: 'center', + paddingVertical: spacing.xs, + }, + footerText: { + fontSize: typography.fontSizes.sm, + color: colors.gray, + }, + emptyContainer: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + padding: spacing.xl, + }, + emptyText: { + fontSize: typography.fontSizes.lg, + color: colors.gray, + textAlign: 'center', + }, +}); + +export default FeedScreen; diff --git a/src/screens/HomeScreen.tsx b/src/screens/HomeScreen.tsx new file mode 100644 index 0000000..e240eee --- /dev/null +++ b/src/screens/HomeScreen.tsx @@ -0,0 +1,295 @@ +import React, { useEffect, useState } from 'react'; +import { + View, + Text, + StyleSheet, + TouchableOpacity, + ScrollView, + Image, + Alert, +} from 'react-native'; +import { colors } from '../theme/colors'; +import { spacing } from '../theme/spacing'; +import { typography } from '../theme/typography'; +import Button from '../components/buttons/Button'; +import Card from '../components/cards/Card'; +import { analyticsService } from '../utils/analytics'; +import { version as currentVersion } from '../../package.json'; +import CodePush from '@chlee1001/react-native-code-push'; +import Snackbar from '../components/common/snackbar'; +import { featureFlagsService } from '../utils/featureFlags'; + +const HomeScreen: React.FC<{ navigation: any }> = ({ navigation }) => { + const [snackbarVisible, setSnackbarVisible] = useState(false); + const [updateAvailable, setUpdateAvailable] = useState(false); + + useEffect(() => { + // Check for CodePush updates + CodePush.sync( + { + installMode: CodePush.InstallMode.ON_NEXT_RESTART, + }, + (syncStatus) => { + if (syncStatus === CodePush.SyncStatus.UPDATE_INSTALLED) { + setSnackbarVisible(true); + } else if (syncStatus === CodePush.SyncStatus.AVAILABLE_UPDATE) { + setUpdateAvailable(true); + } + } + ); + + analyticsService.trackScreenView('Home'); + + // Show new feature popup if enabled + if (featureFlagsService.isEnabled('showNewFeaturePopup')) { + setTimeout(() => { + Alert.alert( + 'New Features Available!', + 'Check out our new profile and notification screens. Tap on the tabs below to explore.', + [{ text: 'OK', onPress: () => featureFlagsService.setFlag('showNewFeaturePopup', false) }] + ); + }, 1500); + } + }, []); + + const checkForUpdates = () => { + CodePush.sync( + { + installMode: CodePush.InstallMode.ON_NEXT_RESTART, + }, + (syncStatus) => { + switch(syncStatus) { + case CodePush.SyncStatus.CHECKING_FOR_UPDATE: + Alert.alert('Checking for updates...'); + break; + case CodePush.SyncStatus.DOWNLOADING_PACKAGE: + Alert.alert('Downloading update...'); + break; + case CodePush.SyncStatus.INSTALLING_UPDATE: + Alert.alert('Installing update...'); + break; + case CodePush.SyncStatus.UPDATE_INSTALLED: + setSnackbarVisible(true); + break; + case CodePush.SyncStatus.UP_TO_DATE: + Alert.alert('App is up to date!'); + break; + case CodePush.SyncStatus.UNKNOWN_ERROR: + Alert.alert('An error occurred while checking for updates'); + break; + } + } + ); + + analyticsService.trackEvent('check_for_updates'); + }; + + return ( + + + Welcome to CodePush Test App + Current Version: {currentVersion} + + {updateAvailable && ( +