import React, { useRef, useState, useEffect } from 'react';
import {
Animated,
PanResponder,
Dimensions,
StyleSheet,
Image,
Alert,
} from 'react-native';
import { ConfigDialog, LOCAL_CACHE_HOST_NAME } from './ConfigDialog';
import { dreaction } from '../dreaction';
const { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } = Dimensions.get('window');
const BALL_SIZE = 60;
const CLICK_THRESHOLD = 5;
const initialPosition = {
x: SCREEN_WIDTH - BALL_SIZE,
y: SCREEN_HEIGHT - BALL_SIZE - 100,
};
export const DraggableBall: React.FC = React.memo(() => {
const position = useRef(new Animated.ValueXY(initialPosition)).current;
const lastPosition = useRef(initialPosition);
const [isDragging, setIsDragging] = useState(false);
const [modalVisible, setModalVisible] = useState(false);
const [isReady, setIsReady] = useState(false);
useEffect(() => {
const timer = setInterval(() => {
setIsReady(dreaction.isReady);
}, 1000);
return () => {
clearInterval(timer);
};
}, []);
useEffect(() => {
if (dreaction.isReady || dreaction.connected) {
return;
}
dreaction.asyncStorageHandler
?.getItem(LOCAL_CACHE_HOST_NAME)
.then((cachedHost) => {
if (cachedHost && !dreaction.isReady && !dreaction.connected) {
dreaction.configure({ host: cachedHost }).connect();
}
})
.catch(() => {});
}, []);
const handleClick = (host: string) => {
try {
dreaction
.configure({
host,
})
.close()
.connect();
setModalVisible(false);
} catch (e) {
Alert.alert('Connected Failed', 'Please check your url');
}
};
const panResponder = useRef(
PanResponder.create({
onStartShouldSetPanResponder: () => true,
onPanResponderGrant: () => {
setIsDragging(true);
position.stopAnimation();
},
onPanResponderMove: (_, gestureState) => {
// 使用 lastPosition 计算位置
position.setValue({
x: lastPosition.current.x + gestureState.dx,
y: lastPosition.current.y + gestureState.dy,
});
},
onPanResponderRelease: (_, gestureState) => {
setIsDragging(false);
lastPosition.current = {
x: lastPosition.current.x + gestureState.dx,
y: lastPosition.current.y + gestureState.dy,
};
if (
Math.abs(gestureState.dx) < CLICK_THRESHOLD &&
Math.abs(gestureState.dy) < CLICK_THRESHOLD
) {
setModalVisible(true);
return;
}
const { moveX, moveY } = gestureState;
// Calculate adsorption to the nearest edge
let newX = moveX <= SCREEN_WIDTH / 2 ? 0 : SCREEN_WIDTH - BALL_SIZE; // Adsorb to the left or right
let newY = Math.min(
Math.max(moveY, 0), // Limit the top boundary
SCREEN_HEIGHT - BALL_SIZE // Limit the bottom boundary
);
// Animation
Animated.spring(position, {
toValue: { x: newX, y: newY },
useNativeDriver: false,
friction: 5,
}).start(() => {
lastPosition.current = { x: newX, y: newY };
});
},
})
).current;
return (
<>
setModalVisible(false)}
onConfirm={handleClick}
/>
>
);
});
DraggableBall.displayName = 'DraggableBall';
const styles = StyleSheet.create({
ball: {
width: BALL_SIZE,
height: BALL_SIZE,
borderRadius: BALL_SIZE / 2,
position: 'absolute',
overflow: 'hidden',
borderWidth: 1,
borderColor: '#eee',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#fff',
},
icon: {
width: BALL_SIZE * 0.8,
height: BALL_SIZE * 0.8,
},
});