/ components / Alert.tsx
Alert.tsx
  1  import React from 'react';
  2  import {
  3    View,
  4    Text,
  5    TouchableOpacity,
  6    StyleSheet,
  7    Modal,
  8    useColorScheme,
  9    TouchableWithoutFeedback,
 10    Animated,
 11  } from 'react-native';
 12  import { Colors, ColorScheme } from '@/constants/Colors';
 13  import { useTheme } from '@/constants/ThemeContext';
 14  import { Ionicons } from '@expo/vector-icons';
 15  import { CustomAlertProps } from '@/types';
 16  
 17  const Alert: React.FC<CustomAlertProps> = ({
 18    visible,
 19    title,
 20    onClose,
 21    type,
 22    options,
 23    message,
 24  }) => {
 25    const { theme } = useTheme();
 26    const systemColorScheme = useColorScheme() as ColorScheme;
 27    const colorScheme =
 28      theme === 'system' ? systemColorScheme : (theme as ColorScheme);
 29    const colors = Colors[colorScheme];
 30  
 31    const styles = getStyles(colors);
 32    const scaleValue = React.useRef(new Animated.Value(0)).current;
 33  
 34    React.useEffect(() => {
 35      if (visible) {
 36        Animated.spring(scaleValue, {
 37          toValue: 1,
 38          useNativeDriver: true,
 39        }).start();
 40      } else {
 41        Animated.spring(scaleValue, {
 42          toValue: 0,
 43          useNativeDriver: true,
 44        }).start();
 45      }
 46    }, [visible]);
 47  
 48    const renderContent = () => {
 49      if (type === 'bookmarks') {
 50        return (
 51          <View testID="alert-options" style={styles.optionsContainer}>
 52            {options?.map((option, index) => (
 53              <TouchableOpacity
 54                key={index}
 55                style={styles.button}
 56                onPress={() => {
 57                  option.onPress();
 58                  onClose();
 59                }}
 60              >
 61                {option.icon && (
 62                  <View style={styles.iconContainer}>
 63                    <Ionicons
 64                      name={option.icon}
 65                      size={24}
 66                      color={colors.primary}
 67                    />
 68                  </View>
 69                )}
 70                <Text style={styles.textStyle}>{option.text}</Text>
 71              </TouchableOpacity>
 72            ))}
 73          </View>
 74        );
 75      } else if (type === 'confirm') {
 76        return (
 77          <>
 78            <Text style={styles.message}>{message}</Text>
 79            <View style={styles.confirmButtonsContainer}>
 80              {options?.map((option, index) => (
 81                <TouchableOpacity
 82                  key={index}
 83                  style={[
 84                    styles.confirmButton,
 85                    index === 0
 86                      ? styles.cancelButton
 87                      : styles.confirmActionButton,
 88                  ]}
 89                  onPress={() => {
 90                    option.onPress();
 91                    onClose();
 92                  }}
 93                >
 94                  <Text
 95                    style={[
 96                      styles.confirmButtonText,
 97                      index === 0
 98                        ? styles.cancelButtonText
 99                        : styles.confirmActionButtonText,
100                    ]}
101                  >
102                    {option.text}
103                  </Text>
104                </TouchableOpacity>
105              ))}
106            </View>
107          </>
108        );
109      }
110      return null;
111    };
112  
113    return (
114      <Modal
115        transparent={true}
116        visible={visible}
117        onRequestClose={onClose}
118        animationType="fade"
119      >
120        <TouchableWithoutFeedback onPress={onClose}>
121          <View style={styles.centeredView}>
122            <TouchableWithoutFeedback>
123              <Animated.View
124                style={[styles.modalView, { transform: [{ scale: scaleValue }] }]}
125              >
126                <TouchableOpacity
127                  testID="close-button"
128                  style={styles.closeButton}
129                  onPress={onClose}
130                >
131                  <Ionicons name="close" size={24} color={colors.text} />
132                </TouchableOpacity>
133                <Text testID="alert-title" style={styles.modalText}>
134                  {title}
135                </Text>
136                {renderContent()}
137              </Animated.View>
138            </TouchableWithoutFeedback>
139          </View>
140        </TouchableWithoutFeedback>
141      </Modal>
142    );
143  };
144  
145  const getStyles = (colors: typeof Colors.light) =>
146    StyleSheet.create({
147      centeredView: {
148        flex: 1,
149        justifyContent: 'center',
150        alignItems: 'center',
151        backgroundColor: 'rgba(0, 0, 0, 0.5)',
152      },
153      modalView: {
154        margin: 20,
155        backgroundColor: colors.card,
156        borderRadius: 24,
157        padding: 24,
158        alignItems: 'stretch',
159        shadowColor: '#000',
160        shadowOffset: {
161          width: 0,
162          height: 4,
163        },
164        shadowOpacity: 0.25,
165        shadowRadius: 8,
166        elevation: 5,
167        minWidth: 300,
168        maxWidth: '85%',
169      },
170      optionsContainer: {
171        marginTop: 16,
172      },
173      button: {
174        flexDirection: 'row',
175        alignItems: 'center',
176        borderRadius: 12,
177        padding: 14,
178        marginTop: 12,
179        backgroundColor: colors.card,
180        borderWidth: 1,
181        borderColor: colors.border,
182      },
183      iconContainer: {
184        width: 40,
185        height: 40,
186        borderRadius: 20,
187        backgroundColor: `${colors.primary}15`,
188        justifyContent: 'center',
189        alignItems: 'center',
190        marginRight: 16,
191      },
192      textStyle: {
193        color: colors.text,
194        fontWeight: '600',
195        fontSize: 16,
196        flex: 1,
197      },
198      modalText: {
199        marginBottom: 16,
200        textAlign: 'center',
201        fontWeight: 'bold',
202        fontSize: 24,
203        color: colors.primary,
204      },
205      closeButton: {
206        position: 'absolute',
207        right: 16,
208        top: 16,
209        zIndex: 1,
210        width: 32,
211        height: 32,
212        borderRadius: 16,
213        backgroundColor: `${colors.text}10`,
214        justifyContent: 'center',
215        alignItems: 'center',
216      },
217      message: {
218        fontSize: 16,
219        color: colors.text,
220        textAlign: 'center',
221        marginBottom: 20,
222      },
223      confirmButtonsContainer: {
224        flexDirection: 'row',
225        justifyContent: 'space-around',
226        gap: 12,
227      },
228      confirmButton: {
229        flex: 1,
230        paddingVertical: 12,
231        paddingHorizontal: 20,
232        borderRadius: 8,
233        alignItems: 'center',
234      },
235      confirmButtonText: {
236        fontSize: 16,
237        fontWeight: 'bold',
238      },
239      cancelButton: {
240        backgroundColor: colors.primary,
241      },
242      cancelButtonText: {
243        color: colors.card,
244      },
245      confirmActionButton: {
246        backgroundColor: colors.background,
247        borderWidth: 1,
248        borderColor: colors.border,
249      },
250      confirmActionButtonText: {
251        color: colors.text,
252      },
253    });
254  
255  export default Alert;