1import { getSDKVersionFromRuntimeVersion } from '@expo/sdk-runtime-versions';
2import { ChevronDownIcon } from '@expo/styleguide-native';
3import { useNavigation } from '@react-navigation/native';
4import { StackNavigationProp } from '@react-navigation/stack';
5import { Divider, Row, View, Text, useExpoTheme } from 'expo-dev-client-components';
6import React, { Fragment } from 'react';
7import { TouchableOpacity } from 'react-native-gesture-handler';
8import semver from 'semver';
9
10import { BranchListItem } from '../../components/BranchListItem';
11import { SectionHeader } from '../../components/SectionHeader';
12import { WebContainerProjectPage_Query } from '../../graphql/types';
13import { HomeStackRoutes } from '../../navigation/Navigation.types';
14
15type ProjectPageApp = WebContainerProjectPage_Query['app']['byId'];
16type ProjectUpdateBranch = WebContainerProjectPage_Query['app']['byId']['updateBranches'][0];
17
18function truthy<TValue>(value: TValue | null | undefined): value is TValue {
19  return !!value;
20}
21
22export function getSDKMajorVersionForEASUpdateBranch(branch: ProjectUpdateBranch): number | null {
23  const updates = branch.updates;
24  if (updates.length === 0) {
25    return null;
26  }
27
28  return (
29    updates
30      .map((update) => {
31        const potentialSDKVersion = getSDKVersionFromRuntimeVersion(update.runtimeVersion);
32        return potentialSDKVersion ? semver.major(potentialSDKVersion) : null;
33      })
34      .filter(truthy)
35      .sort((a, b) => b - a)[0] ?? null
36  );
37}
38
39export function EASUpdateLaunchSection({ app }: { app: ProjectPageApp }) {
40  const branchesToRender = app.updateBranches.filter(
41    (updateBranch) => updateBranch.updates.length > 0
42  );
43
44  const branchManifests = branchesToRender.slice(0, 3).map((branch) => ({
45    name: branch.name,
46    id: branch.id,
47    latestUpdate: branch.updates[0],
48    sdkVersion: getSDKMajorVersionForEASUpdateBranch(branch),
49  }));
50
51  const theme = useExpoTheme();
52  const navigation = useNavigation<StackNavigationProp<HomeStackRoutes>>();
53
54  function onSeeAllBranchesPress() {
55    navigation.navigate('Branches', { appId: app.id });
56  }
57
58  return (
59    <View>
60      <SectionHeader header="Branches" style={{ paddingTop: 0 }} />
61      {branchManifests.map((branch, i) => {
62        return (
63          <Fragment key={branch.id}>
64            <BranchListItem
65              first={i === 0}
66              last={i === 2 && branchesToRender.length <= 3}
67              appId={app.id}
68              name={branch.name}
69              latestUpdate={branch.latestUpdate}
70            />
71            {i < branchManifests.length - 1 && <Divider style={{ height: 1 }} />}
72          </Fragment>
73        );
74      })}
75      {branchesToRender.length > 3 && (
76        <>
77          <Divider style={{ height: 1 }} />
78          <TouchableOpacity onPress={onSeeAllBranchesPress}>
79            <View
80              padding="medium"
81              border="default"
82              bg="default"
83              style={{ borderTopWidth: 0 }}
84              roundedBottom="large">
85              <Row align="center" justify="between">
86                <Text type="InterRegular">See all branches</Text>
87                <ChevronDownIcon
88                  style={{ transform: [{ rotate: '-90deg' }] }}
89                  color={theme.icon.secondary}
90                />
91              </Row>
92            </View>
93          </TouchableOpacity>
94        </>
95      )}
96    </View>
97  );
98}
99