1*26ad19fcSEvan Bacon/**
2*26ad19fcSEvan Bacon * Copyright (c) 650 Industries.
3*26ad19fcSEvan Bacon * Copyright (c) Meta Platforms, Inc. and affiliates.
4*26ad19fcSEvan Bacon *
5*26ad19fcSEvan Bacon * This source code is licensed under the MIT license found in the
6*26ad19fcSEvan Bacon * LICENSE file in the root directory of this source tree.
7*26ad19fcSEvan Bacon */
8*26ad19fcSEvan Baconimport React, { useEffect, useState } from 'react';
9*26ad19fcSEvan Baconimport { Animated, Easing, GestureResponderEvent, StyleSheet, Text } from 'react-native';
10*26ad19fcSEvan Bacon
11*26ad19fcSEvan Baconimport { LogBoxButton } from '../UI/LogBoxButton';
12*26ad19fcSEvan Baconimport * as LogBoxStyle from '../UI/LogBoxStyle';
13*26ad19fcSEvan Bacon
14*26ad19fcSEvan Bacontype Props = {
15*26ad19fcSEvan Bacon  onPress?: ((event: GestureResponderEvent) => void) | null;
16*26ad19fcSEvan Bacon  status: 'COMPLETE' | 'FAILED' | 'NONE' | 'PENDING';
17*26ad19fcSEvan Bacon};
18*26ad19fcSEvan Bacon
19*26ad19fcSEvan Baconexport function LogBoxInspectorSourceMapStatus(props: Props) {
20*26ad19fcSEvan Bacon  const [state, setState] = useState<{
21*26ad19fcSEvan Bacon    animation: null | Animated.CompositeAnimation;
22*26ad19fcSEvan Bacon    rotate: null | Animated.AnimatedInterpolation<string>;
23*26ad19fcSEvan Bacon  }>({
24*26ad19fcSEvan Bacon    animation: null,
25*26ad19fcSEvan Bacon    rotate: null,
26*26ad19fcSEvan Bacon  });
27*26ad19fcSEvan Bacon
28*26ad19fcSEvan Bacon  useEffect(() => {
29*26ad19fcSEvan Bacon    if (props.status === 'PENDING') {
30*26ad19fcSEvan Bacon      if (state.animation == null) {
31*26ad19fcSEvan Bacon        const animated = new Animated.Value(0);
32*26ad19fcSEvan Bacon        const animation = Animated.loop(
33*26ad19fcSEvan Bacon          Animated.timing(animated, {
34*26ad19fcSEvan Bacon            duration: 2000,
35*26ad19fcSEvan Bacon            easing: Easing.linear,
36*26ad19fcSEvan Bacon            toValue: 1,
37*26ad19fcSEvan Bacon            useNativeDriver: true,
38*26ad19fcSEvan Bacon          })
39*26ad19fcSEvan Bacon        );
40*26ad19fcSEvan Bacon        setState({
41*26ad19fcSEvan Bacon          animation,
42*26ad19fcSEvan Bacon          rotate: animated.interpolate({
43*26ad19fcSEvan Bacon            inputRange: [0, 1],
44*26ad19fcSEvan Bacon            outputRange: ['0deg', '360deg'],
45*26ad19fcSEvan Bacon          }),
46*26ad19fcSEvan Bacon        });
47*26ad19fcSEvan Bacon        animation.start();
48*26ad19fcSEvan Bacon      }
49*26ad19fcSEvan Bacon    } else {
50*26ad19fcSEvan Bacon      if (state.animation != null) {
51*26ad19fcSEvan Bacon        state.animation.stop();
52*26ad19fcSEvan Bacon        setState({
53*26ad19fcSEvan Bacon          animation: null,
54*26ad19fcSEvan Bacon          rotate: null,
55*26ad19fcSEvan Bacon        });
56*26ad19fcSEvan Bacon      }
57*26ad19fcSEvan Bacon    }
58*26ad19fcSEvan Bacon
59*26ad19fcSEvan Bacon    return () => {
60*26ad19fcSEvan Bacon      if (state.animation != null) {
61*26ad19fcSEvan Bacon        state.animation.stop();
62*26ad19fcSEvan Bacon      }
63*26ad19fcSEvan Bacon    };
64*26ad19fcSEvan Bacon  }, [props.status, state.animation]);
65*26ad19fcSEvan Bacon
66*26ad19fcSEvan Bacon  let image;
67*26ad19fcSEvan Bacon  let color;
68*26ad19fcSEvan Bacon  switch (props.status) {
69*26ad19fcSEvan Bacon    case 'FAILED':
70*26ad19fcSEvan Bacon      image = require('@expo/metro-runtime/assets/alert-triangle.png');
71*26ad19fcSEvan Bacon      color = LogBoxStyle.getErrorColor(1);
72*26ad19fcSEvan Bacon      break;
73*26ad19fcSEvan Bacon    case 'PENDING':
74*26ad19fcSEvan Bacon      image = require('@expo/metro-runtime/assets/loader.png');
75*26ad19fcSEvan Bacon      color = LogBoxStyle.getWarningColor(1);
76*26ad19fcSEvan Bacon      break;
77*26ad19fcSEvan Bacon  }
78*26ad19fcSEvan Bacon
79*26ad19fcSEvan Bacon  if (props.status === 'COMPLETE' || image == null) {
80*26ad19fcSEvan Bacon    return null;
81*26ad19fcSEvan Bacon  }
82*26ad19fcSEvan Bacon
83*26ad19fcSEvan Bacon  return (
84*26ad19fcSEvan Bacon    <LogBoxButton
85*26ad19fcSEvan Bacon      backgroundColor={{
86*26ad19fcSEvan Bacon        default: 'transparent',
87*26ad19fcSEvan Bacon        pressed: LogBoxStyle.getBackgroundColor(1),
88*26ad19fcSEvan Bacon      }}
89*26ad19fcSEvan Bacon      hitSlop={{ bottom: 8, left: 8, right: 8, top: 8 }}
90*26ad19fcSEvan Bacon      onPress={props.onPress}
91*26ad19fcSEvan Bacon      style={styles.root}>
92*26ad19fcSEvan Bacon      <Animated.Image
93*26ad19fcSEvan Bacon        source={image}
94*26ad19fcSEvan Bacon        tintColor={color ?? LogBoxStyle.getTextColor(0.4)}
95*26ad19fcSEvan Bacon        style={[
96*26ad19fcSEvan Bacon          styles.image,
97*26ad19fcSEvan Bacon          state.rotate == null || props.status !== 'PENDING'
98*26ad19fcSEvan Bacon            ? null
99*26ad19fcSEvan Bacon            : { transform: [{ rotate: state.rotate }] },
100*26ad19fcSEvan Bacon        ]}
101*26ad19fcSEvan Bacon      />
102*26ad19fcSEvan Bacon      <Text style={[styles.text, { color }]}>Source Map</Text>
103*26ad19fcSEvan Bacon    </LogBoxButton>
104*26ad19fcSEvan Bacon  );
105*26ad19fcSEvan Bacon}
106*26ad19fcSEvan Bacon
107*26ad19fcSEvan Baconconst styles = StyleSheet.create({
108*26ad19fcSEvan Bacon  root: {
109*26ad19fcSEvan Bacon    alignItems: 'center',
110*26ad19fcSEvan Bacon    borderRadius: 12,
111*26ad19fcSEvan Bacon    flexDirection: 'row',
112*26ad19fcSEvan Bacon    height: 24,
113*26ad19fcSEvan Bacon    paddingHorizontal: 8,
114*26ad19fcSEvan Bacon  },
115*26ad19fcSEvan Bacon  image: {
116*26ad19fcSEvan Bacon    height: 14,
117*26ad19fcSEvan Bacon    width: 16,
118*26ad19fcSEvan Bacon    marginEnd: 4,
119*26ad19fcSEvan Bacon  },
120*26ad19fcSEvan Bacon  text: {
121*26ad19fcSEvan Bacon    fontSize: 12,
122*26ad19fcSEvan Bacon    includeFontPadding: false,
123*26ad19fcSEvan Bacon    lineHeight: 16,
124*26ad19fcSEvan Bacon  },
125*26ad19fcSEvan Bacon});
126