Skip to content

Commit b89d20f

Browse files
committed
better delete flow
1 parent c12748b commit b89d20f

File tree

8 files changed

+254
-208
lines changed

8 files changed

+254
-208
lines changed

app/settings-pages/delete.tsx

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
import React, { useCallback } from 'react';
2+
import { ScrollView, StyleSheet, Dimensions } from 'react-native';
3+
import { Text } from 'components/ui/Text';
4+
import { useTheme } from 'providers/ThemeProvider';
5+
import Container from 'components/blocks/Container';
6+
import { VStack } from 'components/ui/View/VStack';
7+
import { View } from 'components/ui/View/View';
8+
import Icon from 'assets/icons';
9+
import { router } from 'expo-router';
10+
import { useDispatch } from 'react-redux';
11+
import type { AppThunk } from 'redux/store/reducer';
12+
import { resetApp } from '@/redux/store';
13+
import * as Updates from 'expo-updates';
14+
import { Card } from 'components/ui/Card';
15+
import { Gesture, GestureDetector, GestureHandlerRootView } from 'react-native-gesture-handler';
16+
import Animated, {
17+
useAnimatedStyle,
18+
useSharedValue,
19+
withSpring,
20+
runOnJS,
21+
interpolate,
22+
Extrapolation,
23+
} from 'react-native-reanimated';
24+
25+
const SLIDER_WIDTH = Dimensions.get('window').width - 48; // Account for padding
26+
const THUMB_SIZE = 56;
27+
const TRACK_PADDING = 4;
28+
const MAX_TRANSLATE = SLIDER_WIDTH - THUMB_SIZE - TRACK_PADDING * 2;
29+
30+
interface SlideToDeleteProps {
31+
onComplete: () => void;
32+
trackColor: string;
33+
thumbColor: string;
34+
textColor: string;
35+
iconColor: string;
36+
}
37+
38+
const SlideToDelete: React.FC<SlideToDeleteProps> = ({
39+
onComplete,
40+
trackColor,
41+
thumbColor,
42+
textColor,
43+
iconColor,
44+
}) => {
45+
const translateX = useSharedValue(0);
46+
const isComplete = useSharedValue(false);
47+
48+
const handleComplete = useCallback(() => {
49+
onComplete();
50+
}, [onComplete]);
51+
52+
const panGesture = Gesture.Pan()
53+
.onUpdate((event) => {
54+
if (isComplete.value) return;
55+
translateX.value = Math.max(0, Math.min(event.translationX, MAX_TRANSLATE));
56+
})
57+
.onEnd(() => {
58+
if (isComplete.value) return;
59+
60+
if (translateX.value > MAX_TRANSLATE * 0.9) {
61+
translateX.value = withSpring(MAX_TRANSLATE, { damping: 20, stiffness: 200 });
62+
isComplete.value = true;
63+
runOnJS(handleComplete)();
64+
} else {
65+
translateX.value = withSpring(0, { damping: 20, stiffness: 200 });
66+
}
67+
});
68+
69+
const thumbAnimatedStyle = useAnimatedStyle(() => ({
70+
transform: [{ translateX: translateX.value }],
71+
}));
72+
73+
const textAnimatedStyle = useAnimatedStyle(() => ({
74+
opacity: interpolate(translateX.value, [0, MAX_TRANSLATE * 0.5], [1, 0], Extrapolation.CLAMP),
75+
}));
76+
77+
return (
78+
<GestureHandlerRootView>
79+
<View
80+
style={[
81+
styles.track,
82+
{
83+
backgroundColor: trackColor,
84+
width: SLIDER_WIDTH,
85+
},
86+
]}>
87+
<Animated.View style={[styles.textContainer, textAnimatedStyle]}>
88+
<Text size={16} medium style={{ color: textColor }}>
89+
Swipe to delete →
90+
</Text>
91+
</Animated.View>
92+
<GestureDetector gesture={panGesture}>
93+
<Animated.View
94+
style={[
95+
styles.thumb,
96+
thumbAnimatedStyle,
97+
{
98+
backgroundColor: thumbColor,
99+
},
100+
]}>
101+
<Icon name="mdi:trash-can-outline" size={24} color={iconColor} />
102+
</Animated.View>
103+
</GestureDetector>
104+
</View>
105+
</GestureHandlerRootView>
106+
);
107+
};
108+
109+
const styles = StyleSheet.create({
110+
track: {
111+
height: THUMB_SIZE + TRACK_PADDING * 2,
112+
borderRadius: (THUMB_SIZE + TRACK_PADDING * 2) / 2,
113+
justifyContent: 'center',
114+
padding: TRACK_PADDING,
115+
},
116+
textContainer: {
117+
position: 'absolute',
118+
left: 0,
119+
right: 0,
120+
alignItems: 'center',
121+
justifyContent: 'center',
122+
},
123+
thumb: {
124+
width: THUMB_SIZE,
125+
height: THUMB_SIZE,
126+
borderRadius: THUMB_SIZE / 2,
127+
alignItems: 'center',
128+
justifyContent: 'center',
129+
},
130+
});
131+
132+
const DeleteScreen: React.FC = () => {
133+
const { getPrimaryColor, getRedColor } = useTheme();
134+
const dispatch = useDispatch();
135+
136+
const handleDeleteProfile = useCallback(async () => {
137+
await (dispatch as (thunk: AppThunk) => Promise<void>)(resetApp());
138+
await Updates.reloadAsync();
139+
}, [dispatch]);
140+
141+
const handleCancel = useCallback(() => {
142+
router.back();
143+
}, []);
144+
145+
return (
146+
<Container>
147+
<ScrollView className="flex-1" contentContainerStyle={{ flexGrow: 1 }}>
148+
<VStack spacing={24} className="flex-1 items-center justify-center px-6">
149+
<View
150+
style={{
151+
backgroundColor: getRedColor('900'),
152+
width: 96,
153+
height: 96,
154+
borderRadius: 48,
155+
alignItems: 'center',
156+
justifyContent: 'center',
157+
alignSelf: 'center',
158+
}}>
159+
<Icon name="mdi:trash-can-outline" size={48} color={getRedColor('400')} />
160+
</View>
161+
162+
<VStack spacing={8} className="items-center">
163+
<Text size={24} bold style={{ color: getPrimaryColor('0'), textAlign: 'center' }}>
164+
Delete Account
165+
</Text>
166+
<Text
167+
size={16}
168+
style={{ color: getPrimaryColor('300'), textAlign: 'center', lineHeight: 24 }}>
169+
Are you sure you want to delete your profile? This action cannot be reversed.
170+
</Text>
171+
</VStack>
172+
173+
<Card
174+
variant="warning"
175+
message="There is no guarantee that your mnemonic phrase will allow you to recover your funds. If you were a TestFlight user it's possible your recovery phrase won't restore all your funds."
176+
/>
177+
178+
<View
179+
className="w-full rounded-xl p-4"
180+
style={{ backgroundColor: getPrimaryColor('900') }}>
181+
<VStack spacing={8}>
182+
<Text size={14} medium style={{ color: getPrimaryColor('200') }}>
183+
Before deleting, make sure you have:
184+
</Text>
185+
<Text size={13} style={{ color: getPrimaryColor('400'), lineHeight: 20 }}>
186+
• Backed up your mnemonic phrase{'\n'}• Transferred any remaining funds{'\n'}
187+
Exported any important data
188+
</Text>
189+
</VStack>
190+
</View>
191+
192+
<VStack spacing={12} className="w-full items-center">
193+
<SlideToDelete
194+
onComplete={handleDeleteProfile}
195+
trackColor={getRedColor('900')}
196+
thumbColor={getPrimaryColor('0')}
197+
textColor={getPrimaryColor('0')}
198+
iconColor={getRedColor('300')}
199+
/>
200+
</VStack>
201+
</VStack>
202+
</ScrollView>
203+
</Container>
204+
);
205+
};
206+
207+
export default DeleteScreen;

app/settings-pages/index.tsx

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import { ActionSheetProvider, connectActionSheet } from '@expo/react-native-acti
99
import { Link } from 'expo-router';
1010
import { truncateMiddle } from 'helper/strings';
1111
import Container from 'components/blocks/Container';
12-
import { SheetManager } from 'react-native-actions-sheet';
1312
import * as Application from 'expo-application';
1413
import { withSheetProvider } from 'hocs/withSheetProvider';
1514
import { VStack } from 'components/ui/View/VStack';
@@ -248,14 +247,7 @@ const ModalScreen = () => {
248247
</Section>
249248

250249
<Section title="Danger Zone" isDanger>
251-
<RowButton
252-
label="Delete Account"
253-
onPress={() => {
254-
SheetManager.show('delete-router');
255-
}}
256-
isLast
257-
isDanger
258-
/>
250+
<RowButton label="Delete Account" href="/settings-pages/delete" isLast isDanger />
259251
</Section>
260252

261253
<Link href="/settings-pages/design" asChild>

app/settings-pages/terms.tsx

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import React, { useState } from 'react';
22
import { Button } from 'components/ui/Button';
3-
import { ScrollView } from 'react-native';
3+
import { ScrollView, Switch } from 'react-native';
44
import { useTheme } from 'providers/ThemeProvider';
55
import Container from 'components/blocks/Container';
66
import { VStack } from 'components/ui/View/VStack';
77
import { HStack } from 'components/ui/View/HStack';
8-
import { Checkbox } from 'expo-checkbox';
98
import { TouchableOpacity } from 'components/ui/TouchableOpacity';
109
import { Text } from 'components/ui/Text';
1110

@@ -154,45 +153,61 @@ export default function TermsAndConditions({
154153
checkboxText = 'I have read and agree to the Terms and Conditions',
155154
showCheckbox = true,
156155
}: TermsAndConditionsProps) {
157-
const { getShadeColor } = useTheme();
156+
const { getPrimaryColor, getShadeColor } = useTheme();
158157
const [isChecked, setIsChecked] = useState(false);
159158

160159
const toggleCheckbox = () => setIsChecked(!isChecked);
161160

162161
return (
163162
<Container className="bg-primary-900">
164-
<ScrollView>
165-
<VStack spacing={16} className="p-4">
166-
<Text overpass bold size={32} className="py-2 text-center text-primary-50">
167-
{title}
168-
</Text>
163+
<VStack spacing={16} flex={1} className="p-4">
164+
{/* Header - fixed at top */}
165+
<Text overpass bold size={32} className="py-2 text-center text-primary-50">
166+
{title}
167+
</Text>
168+
169+
{/* Scrollable terms content */}
170+
<ScrollView
171+
style={{
172+
flex: 1,
173+
backgroundColor: getPrimaryColor('800'),
174+
borderRadius: 12,
175+
}}
176+
contentContainerStyle={{ padding: 16 }}>
169177
<Text overpass size={14} className="leading-[22px] text-primary-0">
170178
{terms}
171179
</Text>
180+
</ScrollView>
172181

173-
<Button
174-
variant="primary"
175-
text={buttonText}
176-
onPress={onClose}
177-
disabled={showCheckbox ? !isChecked : false}
178-
/>
179-
182+
{/* Fixed bottom section */}
183+
<VStack spacing={16}>
180184
{showCheckbox && (
181185
<TouchableOpacity onPress={toggleCheckbox}>
182-
<HStack align="center" spacing={8} className="px-4">
183-
<Checkbox
186+
<HStack align="center" spacing={12}>
187+
<Switch
184188
value={isChecked}
185189
onValueChange={toggleCheckbox}
186-
color={isChecked ? getShadeColor('300') : undefined}
190+
trackColor={{
191+
false: getPrimaryColor('700'),
192+
true: getShadeColor('300'),
193+
}}
194+
thumbColor={getPrimaryColor('0')}
187195
/>
188196
<Text id="terms-checkbox" overpass size={14} className="flex-1 text-primary-0">
189197
{checkboxText}
190198
</Text>
191199
</HStack>
192200
</TouchableOpacity>
193201
)}
202+
203+
<Button
204+
variant="primary"
205+
text={buttonText}
206+
onPress={onClose}
207+
disabled={showCheckbox ? !isChecked : false}
208+
/>
194209
</VStack>
195-
</ScrollView>
210+
</VStack>
196211
</Container>
197212
);
198213
}

components/blocks/sheets/delete/index.tsx

Lines changed: 0 additions & 62 deletions
This file was deleted.

0 commit comments

Comments
 (0)