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 === branchesToRender.length - 1}
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        <View border="default" roundedBottom="large">
77          <TouchableOpacity onPress={onSeeAllBranchesPress}>
78            <View padding="medium" bg="default" roundedBottom="large">
79              <Row align="center" justify="between">
80                <Text type="InterRegular">See all branches</Text>
81                <ChevronDownIcon
82                  style={{ transform: [{ rotate: '-90deg' }] }}
83                  color={theme.icon.secondary}
84                />
85              </Row>
86            </View>
87          </TouchableOpacity>
88        </View>
89      )}
90    </View>
91  );
92}
93