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 { EASUpdateLaunchSection } from './EASUpdateLaunchSection'; 9import { EmptySection } from './EmptySection'; 10import { LegacyLaunchSection } from './LegacyLaunchSection'; 11import { ProjectHeader } from './ProjectHeader'; 12import { ConstantItem } from '../../components/ConstantItem'; 13import ScrollView from '../../components/NavigationScrollView'; 14import ShareProjectButton from '../../components/ShareProjectButton'; 15import { WebContainerProjectPage_Query } from '../../graphql/types'; 16import { HomeStackRoutes } from '../../navigation/Navigation.types'; 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 {appHasEASUpdates(app) && ( 59 <> 60 <EASUpdateLaunchSection app={app} /> 61 <Spacer.Vertical size="xl" /> 62 </> 63 )} 64 {appHasLegacyUpdate(app) && ( 65 <> 66 <LegacyLaunchSection app={app} /> 67 <Spacer.Vertical size="xl" /> 68 </> 69 )} 70 {!appHasLegacyUpdate(app) && !appHasEASUpdates(app) && ( 71 <> 72 <EmptySection /> 73 <Spacer.Vertical size="xl" /> 74 </> 75 )} 76 <View bg="default" border="default" overflow="hidden" rounded="large"> 77 <ConstantItem title="Owner" value={app.username} /> 78 {app.sdkVersion !== '0.0.0' && ( 79 <> 80 <Divider style={{ height: 1 }} /> 81 <ConstantItem title="SDK Version" value={app.sdkVersion} /> 82 </> 83 )} 84 {app.latestReleaseForReleaseChannel?.runtimeVersion && ( 85 <> 86 <Divider style={{ height: 1 }} /> 87 <ConstantItem 88 title="Runtime Version" 89 value={app.latestReleaseForReleaseChannel?.runtimeVersion} 90 /> 91 </> 92 )} 93 </View> 94 </View> 95 </ScrollView> 96 ); 97 } 98 99 React.useEffect(() => { 100 if (data?.app?.byId) { 101 const fullName = data?.app.byId.fullName; 102 const title = data?.app.byId.name ?? fullName; 103 navigation.setOptions({ 104 title, 105 headerRight: () => <ShareProjectButton fullName={fullName} />, 106 }); 107 } 108 }, [navigation, data?.app?.byId]); 109 110 return <View flex="1">{contents}</View>; 111} 112 113function appHasLegacyUpdate(app: ProjectPageApp): boolean { 114 return app.published; 115} 116 117function appHasEASUpdates(app: ProjectPageApp): boolean { 118 return app.updateBranches.some((branch) => branch.updates.length > 0); 119} 120