Skip to content

Instantly share code, notes, and snippets.

@kacperkapusciak
Created July 29, 2025 16:08
Show Gist options
  • Save kacperkapusciak/96885b863125b1c81216f89e02eb95cf to your computer and use it in GitHub Desktop.
Save kacperkapusciak/96885b863125b1c81216f89e02eb95cf to your computer and use it in GitHub Desktop.
import AntDesign from "@expo/vector-icons/AntDesign";
import MaskedView from "@react-native-masked-view/masked-view";
import { useRef, useState } from "react";
import { Pressable, StyleSheet, TextInput, View } from "react-native";
import { KeyboardAvoidingView } from "react-native-keyboard-controller";
import Animated, { FadeInDown } from "react-native-reanimated";
const AnimatedPressable = Animated.createAnimatedComponent(Pressable);
export default function Index() {
const ref = useRef<TextInput>(null);
const [isFocused, setIsFocused] = useState(false);
const [isPressed, setIsPressed] = useState(false);
const [isSending, setSending] = useState(false);
const handleSend = () => {
setSending(true);
setTimeout(() => {
ref.current?.blur();
ref.current?.clear();
}, 600);
setTimeout(() => {
setSending(false);
}, 800);
};
return (
<KeyboardAvoidingView behavior={"padding"} style={styles.content}>
<View
style={{
flex: 1,
justifyContent: "center",
alignItems: "center",
}}
>
<MaskedView
style={styles.maskView}
maskElement={
<View style={styles.mask}>
{"Hello, Casper".split("").map((char, index) => (
<Animated.Text
style={styles.greeting}
key={index}
entering={FadeInDown.delay(index * 33)
.springify()
.mass(2)}
>
{char}
</Animated.Text>
))}
</View>
}
>
<Animated.View
style={[
styles.gradient,
{
animationName: {
to: {
transform: [{ rotate: "360deg" }],
},
},
animationDuration: "3s",
animationIterationCount: "infinite",
},
]}
>
{" "}
</Animated.View>
</MaskedView>
<View style={styles.footer}>
<TextInput
ref={ref}
style={styles.input}
placeholder="Ask..."
onBlur={() => setIsFocused(false)}
onFocus={() => setIsFocused(true)}
/>
<Animated.View
style={{
transitionProperty: ["transform", "opacity"],
transitionDuration: "200ms",
transform: [
{ scale: isPressed ? 0.8 : 1 },
{ translateY: isSending ? -200 : 0 },
],
opacity: isSending ? 0 : 1,
}}
>
<AnimatedPressable
onPressIn={() => setIsPressed(true)}
onPressOut={() => setIsPressed(false)}
onPress={() => handleSend()}
style={[
styles.sendButton,
{
transitionProperty: ["opacity", "marginLeft", "transform"],
transitionDuration: "300ms",
opacity: isFocused ? 1 : 0,
marginLeft: isFocused ? 0 : -50,
transform: [{ translateX: isFocused ? 0 : 58 }],
},
]}
>
<AntDesign name="arrowup" size={24} color="white" />
</AnimatedPressable>
</Animated.View>
</View>
</View>
</KeyboardAvoidingView>
);
}
const styles = StyleSheet.create({
content: {
flex: 1,
},
greeting: {
fontSize: 40,
textAlign: "center",
fontWeight: "bold",
},
gradient: {
experimental_backgroundImage:
"linear-gradient(90deg,rgba(2, 0, 36, 1) 0%, rgba(9, 9, 121, 1) 35%, rgba(0, 212, 255, 1) 100%)",
width: "100%",
height: "100%",
},
mask: {
backgroundColor: "transparent",
flex: 1,
justifyContent: "center",
alignItems: "center",
flexDirection: "row",
},
maskView: { flex: 1, flexDirection: "row", height: "100%" },
input: {
flex: 1,
height: 50,
borderWidth: StyleSheet.hairlineWidth,
borderColor: "#94a3b8",
borderRadius: 25,
paddingHorizontal: 20,
fontSize: 18,
},
sendButton: {
backgroundColor: "#3b82f6",
width: 50,
height: 50,
borderRadius: 25,
justifyContent: "center",
alignItems: "center",
},
footer: {
flexDirection: "row",
position: "absolute",
bottom: 16,
flex: 1,
gap: 10,
padding: 8,
},
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment