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