1import { spacing } from '@expo/styleguide-native'; 2import { StackScreenProps } from '@react-navigation/stack'; 3import dedent from 'dedent'; 4import { Divider, Spacer, Text, useExpoTheme, View } from 'expo-dev-client-components'; 5import * as React from 'react'; 6import { ActivityIndicator } from 'react-native'; 7 8import { ConstantItem } from '../../components/ConstantItem'; 9import ScrollView from '../../components/NavigationScrollView'; 10import ShareProjectButton from '../../components/ShareProjectButton'; 11import { WebContainerProjectPage_Query } from '../../graphql/types'; 12import { HomeStackRoutes } from '../../navigation/Navigation.types'; 13import { EASUpdateLaunchSection } from './EASUpdateLaunchSection'; 14import { EmptySection } from './EmptySection'; 15import { LegacyLaunchSection } from './LegacyLaunchSection'; 16import { ProjectHeader } from './ProjectHeader'; 17 18const ERROR_TEXT = dedent` 19 An unexpected error has occurred. 20 Sorry about this. We will resolve the issue as soon as possible. 21`; 22 23type Props = { 24 loading: boolean; 25 error?: Error; 26 data?: WebContainerProjectPage_Query; 27} & StackScreenProps<HomeStackRoutes, 'ProjectDetails'>; 28 29type ProjectPageApp = WebContainerProjectPage_Query['app']['byId']; 30 31export function ProjectView({ loading, error, data, navigation }: Props) { 32 const theme = useExpoTheme(); 33 34 let contents; 35 if (error && !data?.app?.byId) { 36 console.log(error); 37 contents = ( 38 <Text 39 align="center" 40 style={{ marginBottom: spacing[4], marginHorizontal: spacing[4] }} 41 type="InterRegular"> 42 {ERROR_TEXT} 43 </Text> 44 ); 45 } else if (loading || !data?.app?.byId) { 46 contents = ( 47 <View flex="1" align="centered"> 48 <ActivityIndicator size="large" color={theme.highlight.accent} /> 49 </View> 50 ); 51 } else { 52 const app = data.app.byId; 53 54 contents = ( 55 <ScrollView style={{ flex: 1 }}> 56 <ProjectHeader app={app} /> 57 <View padding="medium"> 58 {appHasLegacyUpdate(app) && <LegacyLaunchSection app={app} />} 59 {appHasEASUpdates(app) && <EASUpdateLaunchSection app={app} />} 60 {!appHasLegacyUpdate(app) && !appHasEASUpdates(app) && <EmptySection />} 61 <Spacer.Vertical size="xl" /> 62 <View bg="default" border="default" overflow="hidden" rounded="large"> 63 <ConstantItem title="Owner" value={app.username} /> 64 {app.sdkVersion !== '0.0.0' && ( 65 <> 66 <Divider style={{ height: 1 }} /> 67 <ConstantItem title="SDK Version" value={app.sdkVersion} /> 68 </> 69 )} 70 {app.latestReleaseForReleaseChannel?.runtimeVersion && ( 71 <> 72 <Divider style={{ height: 1 }} /> 73 <ConstantItem 74 title="Runtime Version" 75 value={app.latestReleaseForReleaseChannel?.runtimeVersion} 76 /> 77 </> 78 )} 79 </View> 80 </View> 81 </ScrollView> 82 ); 83 } 84 85 React.useEffect(() => { 86 if (data?.app?.byId) { 87 const fullName = data?.app.byId.fullName; 88 const title = data?.app.byId.name ?? fullName; 89 navigation.setOptions({ 90 title, 91 headerRight: () => <ShareProjectButton fullName={fullName} />, 92 }); 93 } 94 }, [navigation, data?.app?.byId]); 95 96 return <View flex="1">{contents}</View>; 97} 98 99function appHasLegacyUpdate(app: ProjectPageApp): boolean { 100 return app.published; 101} 102 103function appHasEASUpdates(app: ProjectPageApp): boolean { 104 return app.updateBranches.some((branch) => branch.updates.length > 0); 105} 106