xref: /expo/home/menu/DevMenuTaskInfo.tsx (revision 5a02d672)
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    // no properties for bare manifests
39    return {
40      iconUrl: undefined,
41      taskName: undefined,
42      sdkVersion: undefined,
43      runtimeVersion: undefined,
44      isVerified: undefined,
45    };
46  }
47}
48
49export function DevMenuTaskInfo({ task }: Props) {
50  const theme = useExpoTheme();
51
52  const manifest = task.manifestString
53    ? (JSON.parse(task.manifestString) as typeof Constants.manifest | typeof Constants.manifest2)
54    : null;
55  const manifestInfo = manifest ? getInfoFromManifest(manifest) : null;
56
57  return (
58    <View>
59      <Row bg="default" padding="medium">
60        {manifestInfo?.iconUrl ? (
61          <Image source={{ uri: manifestInfo.iconUrl }} style={styles.taskIcon} />
62        ) : null}
63        <View flex="1" style={{ justifyContent: 'center' }}>
64          <Text type="InterBold" color="default" size="medium" numberOfLines={1}>
65            {manifestInfo?.taskName ? manifestInfo.taskName : 'Untitled Experience'}
66          </Text>
67          {manifestInfo?.sdkVersion && (
68            <Text size="small" type="InterRegular" color="secondary">
69              SDK version:{' '}
70              <Text type="InterSemiBold" color="secondary" size="small">
71                {manifestInfo.sdkVersion}
72              </Text>
73            </Text>
74          )}
75          {manifestInfo?.runtimeVersion && (
76            <Text size="small" type="InterRegular" color="secondary">
77              Runtime version:{' '}
78              <Text type="InterSemiBold" color="secondary" size="small">
79                {manifestInfo.runtimeVersion}
80              </Text>
81            </Text>
82          )}
83          {!manifestInfo?.isVerified && (
84            <TouchableOpacity
85              onPress={() => Linking.openURL('https://expo.fyi/unverified-app-expo-go')}>
86              <Row
87                bg="warning"
88                border="warning"
89                rounded="medium"
90                padding="0"
91                align="center"
92                style={{
93                  alignSelf: 'flex-start',
94                  marginTop: 3,
95                }}>
96                <Ionicons
97                  name={Platform.select({ ios: 'ios-warning', default: 'md-warning' })}
98                  size={14}
99                  color={theme.text.warning}
100                  lightColor={theme.text.warning}
101                  darkColor={theme.text.warning}
102                  style={{
103                    marginHorizontal: 4,
104                  }}
105                />
106                <Text
107                  color="warning"
108                  type="InterSemiBold"
109                  size="small"
110                  style={{
111                    marginRight: 4,
112                  }}>
113                  Unverified
114                </Text>
115              </Row>
116            </TouchableOpacity>
117          )}
118        </View>
119      </Row>
120    </View>
121  );
122}
123
124const styles = StyleSheet.create({
125  taskIcon: {
126    width: 40,
127    height: 40,
128    marginRight: 8,
129    borderRadius: 8,
130    alignSelf: 'center',
131    backgroundColor: 'transparent',
132  },
133});
134