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