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