Skip to content

Commit

Permalink
feat: added custom Alert component
Browse files Browse the repository at this point in the history
  • Loading branch information
Vali-98 committed Oct 13, 2024
1 parent 6c4c23f commit f740676
Show file tree
Hide file tree
Showing 6 changed files with 279 additions and 21 deletions.
2 changes: 2 additions & 0 deletions app/_layout.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { AlertBox } from '@components/Alert'
import { db, rawdb } from '@db'
import { Style, initializeApp, startupApp } from '@globals'
import { useMigrations } from 'drizzle-orm/expo-sqlite/migrator'
Expand Down Expand Up @@ -45,6 +46,7 @@ const Layout = () => {
<GestureHandlerRootView style={{ flex: 1 }}>
{__DEV__ && <DevDB />}
<MenuProvider>
<AlertBox />
<Stack
screenOptions={{
headerStyle: { backgroundColor: Style.getColor('primary-surface1') },
Expand Down
159 changes: 159 additions & 0 deletions app/components/Alert.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import { Style } from '@globals'
import React, { ReactNode } from 'react'
import { Modal, View, Text, TouchableOpacity, StyleSheet } from 'react-native'
import Animated, { FadeInDown } from 'react-native-reanimated'
import { create } from 'zustand'

import FadeBackrop from './FadeBackdrop'

type AlertButtonProps = {
label: string
onPress?: () => void | Promise<void>
type?: 'warning' | 'default'
}

type AlertProps = {
title: string
description: string
buttons: AlertButtonProps[]
alignButtons?: 'left' | 'right'
}

type AlertState = {
visible: boolean
props: AlertProps
hide: () => void
show: (props: AlertProps) => void
}

export namespace Alert {
export const alert = (props: AlertProps) => {
useAlert.getState().show(props)
}
}

const useAlert = create<AlertState>()((set, get) => ({
visible: false,
props: {
title: 'Are You Sure?',
description: 'LIke `sure` sure?',
buttons: [
{ label: 'Cancel', onPress: () => {}, type: 'default' },
{ label: 'Confirm', onPress: () => {}, type: 'warning' },
],
alignButtons: 'right',
},
hide: () => {
set((state) => ({ ...state, visible: false }))
},
show: (props: AlertProps) => {
set((state) => ({ ...state, visible: true, props: props }))
},
}))

type AlertProviderProps = {
children: ReactNode
}

const AlertButton: React.FC<AlertButtonProps> = ({ label, onPress, type = 'default' }) => {
return (
<TouchableOpacity
onPress={async () => {
onPress && onPress()
useAlert.getState().hide()
}}>
<Text style={buttonStyleMap[type]}>{label}</Text>
</TouchableOpacity>
)
}

export const AlertBox = () => {
const { visible, props } = useAlert((state) => ({ visible: state.visible, props: state.props }))

return (
<Modal visible={visible} transparent style={styles.modal} animationType="fade">
<FadeBackrop
handleOverlayClick={() => {
useAlert.getState().hide()
}}>
<Animated.View style={styles.textBoxContainer} entering={FadeInDown}>
<View style={styles.textBox}>
<Text style={styles.title}>{props.title}</Text>
<Text style={styles.description}>{props.description}</Text>
<View style={styles.buttonContainer}>
{props.buttons.map((item, index) => (
<AlertButton {...item} key={index} />
))}
</View>
</View>
</Animated.View>
</FadeBackrop>
</Modal>
)
}

export const AlertProvider: React.FC<AlertProviderProps> = ({ children }) => {
return (
<View style={{ flex: 1 }}>
<AlertBox />
{children}
</View>
)
}

const styles = StyleSheet.create({
modal: {
flex: 1,
},
textBoxContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},

textBox: {
backgroundColor: Style.getColor('primary-surface2'),
paddingHorizontal: 32,
paddingBottom: 16,
paddingTop: 24,
borderRadius: 16,
width: '90%',
},

title: {
color: Style.getColor('primary-text1'),
fontSize: 20,
fontWeight: '500',
marginBottom: 12,
},

description: {
color: Style.getColor('primary-text1'),
marginBottom: 12,
fontSize: 16,
},

buttonContainer: {
flexDirection: 'row',
columnGap: 24,
justifyContent: 'flex-end',
alignItems: 'center',
},

button: {
paddingHorizontal: 4,
paddingVertical: 8,
color: Style.getColor('primary-text2'),
},

buttonWarning: {
paddingHorizontal: 4,
paddingVertical: 8,
color: '#d2574b',
},
})

const buttonStyleMap = {
warning: styles.buttonWarning,
default: styles.button,
}
46 changes: 42 additions & 4 deletions app/components/CharacterMenu/CharacterEditPopup.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { Alert } from '@components/Alert'
import { CharInfo } from '@constants/Characters'
import { AntDesign, FontAwesome } from '@expo/vector-icons'
import { Characters, Style } from '@globals'
import { useFocusEffect, useRouter } from 'expo-router'
import React, { useRef, useState } from 'react'
import { StyleSheet, TouchableOpacity, Text, BackHandler, Alert } from 'react-native'
import { StyleSheet, TouchableOpacity, Text, BackHandler } from 'react-native'
import {
Menu,
MenuOption,
Expand Down Expand Up @@ -61,6 +62,24 @@ const CharacterEditPopup: React.FC<CharacterEditPopupProps> = ({
}))

const deleteCard = () => {
Alert.alert({
title: 'Delete Chracter',
description: `Are you sure you want to delete '${characterInfo.name}'? This cannot be undone.`,
buttons: [
{
label: 'Cancel',
},
{
label: 'Delete Character',
onPress: async () => {
Characters.db.mutate.deleteCard(characterInfo.id ?? -1)
},
type: 'warning',
},
],
})

/*
Alert.alert(
`Delete Character`,
`Are you sure you want to delete '${characterInfo.name}'? This cannot be undone.`,
Expand All @@ -76,11 +95,30 @@ const CharacterEditPopup: React.FC<CharacterEditPopupProps> = ({
},
],
{ cancelable: true }
)
)*/
}

const cloneCard = () => {
Alert.alert(
Alert.alert({
title: 'Clone Character',
description: `Are you sure you want to clone '${characterInfo.name}'?`,
buttons: [
{
label: 'Cancel',
},
{
label: 'Clone Character',
onPress: async () => {
setNowLoading(true)
await Characters.db.mutate.duplicateCard(characterInfo.id)
menuRef.current?.close()
setNowLoading(false)
},
},
],
})

/* Alert.alert(
`Clone Character`,
`Are you sure you want to clone '${characterInfo.name}'?`,
[
Expand All @@ -97,7 +135,7 @@ const CharacterEditPopup: React.FC<CharacterEditPopupProps> = ({
},
],
{ cancelable: true }
)
) */
}

const editCharacter = async () => {
Expand Down
2 changes: 1 addition & 1 deletion app/components/ChatMenu/ChatWindow/EditorModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ type EditorProps = {
}

type FadeScreenProps = {
handleOverlayClick: (e: GestureResponderEvent) => void
handleOverlayClick?: (e: GestureResponderEvent) => void
children: ReactElement
}
const FadeScreen: React.FC<FadeScreenProps> = ({ handleOverlayClick, children }) => {
Expand Down
24 changes: 24 additions & 0 deletions app/components/FadeBackdrop.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { ReactNode } from 'react'
import { GestureResponderEvent, TouchableOpacity } from 'react-native'

type FadeScreenProps = {
handleOverlayClick?: (e: GestureResponderEvent) => void
children: ReactNode
}

const FadeBackrop: React.FC<FadeScreenProps> = ({ handleOverlayClick, children }) => {
return (
<TouchableOpacity
activeOpacity={1}
onPress={handleOverlayClick}
style={{
height: '100%',
justifyContent: 'flex-end',
backgroundColor: 'rgba(0, 0, 0, 0.5)',
}}>
{children}
</TouchableOpacity>
)
}

export default FadeBackrop
Loading

0 comments on commit f740676

Please sign in to comment.