xref: /expo/home/menu/DevMenuTaskInfo.tsx (revision 4bf00a55)
1import Ionicons from '@expo/vector-icons/build/Ionicons';
2import Constants from 'expo-constants';
3import { Row, View, Text, useExpoTheme } from 'expo-dev-client-components';
4import React from 'react';
5import { Image, Linking, Platform, StyleSheet, TouchableOpacity } from 'react-native';
6
7type Props = {
8  task: { manifestUrl: string; manifestString: string };
9};
10
11function stringOrUndefined<T>(anything: T): string | undefined {
12  if (typeof anything === 'string') {
13    return anything;
14  }
15
16  return undefined;
17}
18
19function getInfoFromManifest(
20  manifest: NonNullable<typeof Constants.manifest | typeof Constants.manifest2>
21): {
22  iconUrl?: string;
23  taskName?: string;
24  sdkVersion?: string;
25  runtimeVersion?: string;
26  isVerified?: boolean;
27} {
28  if ('metadata' in manifest) {
29    // modern manifest
30    return {
31      iconUrl: undefined, // no icon for modern manifests
32      taskName: manifest.extra?.expoClient?.name,
33      sdkVersion: manifest.extra?.expoClient?.sdkVersion,
34      runtimeVersion: stringOrUndefined(manifest.runtimeVersion),
35      isVerified: (manifest as any).isVerified,
36    };
37  } else {
38    return {
39      iconUrl: manifest.iconUrl,
40      taskName: manifest.name,
41      sdkVersion: manifest.sdkVersion,
42      runtimeVersion: stringOrUndefined(manifest.runtimeVersion),
43      isVerified: manifest.isVerified,
44    };
45  }
46}
47
48export function DevMenuTaskInfo({ task }: Props) {
49  const theme = useExpoTheme();
50
51  const manifest = task.manifestString
52    ? (JSON.parse(task.manifestString) as typeof Constants.manifest | typeof Constants.manifest2)
53    : null;
54  const manifestInfo = manifest ? getInfoFromManifest(manifest) : null;
55
56  return (
57    <View>
58      <Row bg="default" padding="medium">
59        {manifestInfo?.iconUrl ? (
60          <Image source={{ uri: manifestInfo.iconUrl }} style={styles.taskIcon} />
61        ) : null}
62        <View flex="1" style={{ justifyContent: 'center' }}>
63          <Text type="InterBold" color="default" size="medium" numberOfLines={1}>
64            {manifestInfo?.taskName ? manifestInfo.taskName : 'Untitled Experience'}
65          </Text>
66          {manifestInfo?.sdkVersion && (
67            <Text size="small" type="InterRegular" color="secondary">
68              SDK version:{' '}
69              <Text type="InterSemiBold" color="secondary" size="small">
70                {manifestInfo.sdkVersion}
71              </Text>
72            </Text>
73          )}
74          {manifestInfo?.runtimeVersion && (
75            <Text size="small" type="InterRegular" color="secondary">
76              Runtime version:{' '}
77              <Text type="InterSemiBold" color="secondary" size="small">
78                {manifestInfo.runtimeVersion}
79              </Text>
80            </Text>
81          )}
82          {!manifestInfo?.isVerified && (
83            <TouchableOpacity
84              onPress={() => Linking.openURL('https://expo.fyi/unverified-app-expo-go')}>
85              <Row
86                bg="warning"
87                border="warning"
88                rounded="medium"
89                padding="0"
90                align="center"
91                style={{
92                  alignSelf: 'flex-start',
93                  marginTop: 3,
94                }}>
95                <Ionicons
96                  name={Platform.select({ ios: 'ios-warning', default: 'md-warning' })}
97                  size={14}
98                  color={theme.text.warning}
99                  lightColor={theme.text.warning}
100                  darkColor={theme.text.warning}
101                  style={{
102                    marginHorizontal: 4,
103                  }}
104                />
105                <Text
106                  color="warning"
107                  type="InterSemiBold"
108                  size="small"
109                  style={{
110                    marginRight: 4,
111                  }}>
112                  Unverified
113                </Text>
114              </Row>
115            </TouchableOpacity>
116          )}
117        </View>
118      </Row>
119    </View>
120  );
121}
122
123const styles = StyleSheet.create({
124  taskIcon: {
125    width: 40,
126    height: 40,
127    marginRight: 8,
128    borderRadius: 8,
129    alignSelf: 'center',
130    backgroundColor: 'transparent',
131  },
132});
133