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