1import { getSDKVersionFromRuntimeVersion } from '@expo/sdk-runtime-versions'; 2import { ChevronDownIcon } from '@expo/styleguide-native'; 3import { useNavigation } from '@react-navigation/native'; 4import { StackNavigationProp } from '@react-navigation/stack'; 5import { Divider, Row, View, Text, useExpoTheme } from 'expo-dev-client-components'; 6import React, { Fragment } from 'react'; 7import { TouchableOpacity } from 'react-native-gesture-handler'; 8import semver from 'semver'; 9 10import { BranchListItem } from '../../components/BranchListItem'; 11import { SectionHeader } from '../../components/SectionHeader'; 12import { WebContainerProjectPage_Query } from '../../graphql/types'; 13import { HomeStackRoutes } from '../../navigation/Navigation.types'; 14 15type ProjectPageApp = WebContainerProjectPage_Query['app']['byId']; 16type ProjectUpdateBranch = WebContainerProjectPage_Query['app']['byId']['updateBranches'][0]; 17 18function truthy<TValue>(value: TValue | null | undefined): value is TValue { 19 return !!value; 20} 21 22export function getSDKMajorVersionForEASUpdateBranch(branch: ProjectUpdateBranch): number | null { 23 const updates = branch.updates; 24 if (updates.length === 0) { 25 return null; 26 } 27 28 return ( 29 updates 30 .map((update) => { 31 const potentialSDKVersion = getSDKVersionFromRuntimeVersion(update.runtimeVersion); 32 return potentialSDKVersion ? semver.major(potentialSDKVersion) : null; 33 }) 34 .filter(truthy) 35 .sort((a, b) => b - a)[0] ?? null 36 ); 37} 38 39export function EASUpdateLaunchSection({ app }: { app: ProjectPageApp }) { 40 const branchesToRender = app.updateBranches.filter( 41 (updateBranch) => updateBranch.updates.length > 0 42 ); 43 44 const branchManifests = branchesToRender.slice(0, 3).map((branch) => ({ 45 name: branch.name, 46 id: branch.id, 47 latestUpdate: branch.updates[0], 48 sdkVersion: getSDKMajorVersionForEASUpdateBranch(branch), 49 })); 50 51 const theme = useExpoTheme(); 52 const navigation = useNavigation<StackNavigationProp<HomeStackRoutes>>(); 53 54 function onSeeAllBranchesPress() { 55 navigation.navigate('Branches', { appId: app.id }); 56 } 57 58 return ( 59 <View> 60 <SectionHeader header="Branches" style={{ paddingTop: 0 }} /> 61 {branchManifests.map((branch, i) => { 62 return ( 63 <Fragment key={branch.id}> 64 <BranchListItem 65 first={i === 0} 66 last={i === branchesToRender.length - 1} 67 appId={app.id} 68 name={branch.name} 69 latestUpdate={branch.latestUpdate} 70 /> 71 {i < branchManifests.length - 1 && <Divider style={{ height: 1 }} />} 72 </Fragment> 73 ); 74 })} 75 {branchesToRender.length > 3 && ( 76 <View border="default" roundedBottom="large"> 77 <TouchableOpacity onPress={onSeeAllBranchesPress}> 78 <View padding="medium" bg="default" roundedBottom="large"> 79 <Row align="center" justify="between"> 80 <Text type="InterRegular">See all branches</Text> 81 <ChevronDownIcon 82 style={{ transform: [{ rotate: '-90deg' }] }} 83 color={theme.icon.secondary} 84 /> 85 </Row> 86 </View> 87 </TouchableOpacity> 88 </View> 89 )} 90 </View> 91 ); 92} 93