import { useEffect, useState } from "react";
import { ColorValue, StyleSheet, View } from "react-native";
import Animated from "react-native-reanimated";
import { useIOThemeContext } from "../../context";
import { hexToRgba, IOColors } from "../../core/IOColors";
import { triggerHaptic } from "../../functions";
import { useErrorShakeAnimation } from "../../utils/hooks/useErrorShakeAnimation";
import { HStack } from "../layout";
type CodeInputProps = {
value: string;
onValueChange: (value: string) => void;
length: number;
onValidate: (value: string) => boolean;
variant?: "primary" | "neutral";
};
const DOT_SIZE = 16;
const styles = StyleSheet.create({
dotShape: {
width: DOT_SIZE,
height: DOT_SIZE,
borderRadius: DOT_SIZE / 2,
borderWidth: 2
}
});
const EmptyDot = ({ color: borderColor }: { color: ColorValue }) => (
);
const FilledDot = ({ color: backgroundColor }: { color: ColorValue }) => (
);
export const CodeInput = ({
length,
value,
onValueChange,
variant = "primary",
onValidate
}: CodeInputProps) => {
const [status, setStatus] = useState<"default" | "error">("default");
const { themeType, theme } = useIOThemeContext();
const { translate, animatedStyle, shakeAnimation } = useErrorShakeAnimation();
/* Empty Dot
- Right color depending on both theme and variant */
const emptyDotColorLightBg = IOColors["grey-650"];
const emptyDotColorDarkBg = IOColors["grey-300"];
const emptyDotColorAccentBg = hexToRgba(IOColors.white, 0.75);
const emptyDotColorThemeBased =
themeType === "light" ? emptyDotColorLightBg : emptyDotColorDarkBg;
const emptyDotColor =
variant === "primary" ? emptyDotColorAccentBg : emptyDotColorThemeBased;
/* Filled Dot
- Right color depending on theme, variant and status */
const filledDotColorLightBg = IOColors.black;
const filledDotColorDarkBg = IOColors.white;
const filledDotColorError =
variant === "primary" ? IOColors["error-400"] : IOColors[theme.errorText];
const filledDotColorThemeBased =
themeType === "light" ? filledDotColorLightBg : filledDotColorDarkBg;
const filledDotColor =
status === "error"
? filledDotColorError
: variant === "primary"
? filledDotColorDarkBg
: filledDotColorThemeBased;
useEffect(() => {
if (onValidate && value.length === length) {
const isValid = onValidate(value);
if (!isValid) {
setStatus("error");
triggerHaptic("notificationError");
// eslint-disable-next-line functional/immutable-data
translate.value = shakeAnimation();
const timer = setTimeout(() => {
setStatus("default");
onValueChange("");
}, 500);
return () => clearTimeout(timer);
}
}
return;
}, [value, onValidate, length, onValueChange, translate, shakeAnimation]);
return (
{[...Array(length)].map((_, i) =>
value[i] ? (
) : (
)
)}
);
};