Skip to content

Commit 5b0ca68

Browse files
committed
Account export
1 parent 7907f8b commit 5b0ca68

File tree

1 file changed

+207
-41
lines changed
  • packages/mobile-app/app/(drawer)/account/account-settings/export-account

1 file changed

+207
-41
lines changed
Lines changed: 207 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,231 @@
11
import { StatusBar } from "expo-status-bar";
2-
import { Button, StyleSheet, Text, View } from "react-native";
3-
import { useRouter } from "expo-router";
2+
import { StyleSheet, View } from "react-native";
3+
import { Stack } from "expo-router";
44
import { useFacade } from "@/data/facades";
55
import { AccountFormat } from "@ironfish/sdk";
6+
import { Text, Modal, Card, Button } from "@ui-kitten/components";
7+
import { setStringAsync } from "expo-clipboard";
8+
import { useState, useRef, useEffect } from "react";
69

710
export default function ExportAccount() {
8-
const router = useRouter();
11+
const [modalVisible, setModalVisible] = useState(false);
12+
const [exportType, setExportType] = useState<AccountFormat | null>(null);
13+
const [exportedContent, setExportedContent] = useState<string | null>(null);
14+
const [holdProgress, setHoldProgress] = useState(3);
15+
const holdTimerRef = useRef<NodeJS.Timeout | null>(null);
16+
const isHoldingRef = useRef(false);
917

1018
const facade = useFacade();
1119
const { data, isLoading } = facade.getAccount.useQuery({});
1220
const exportAccount = facade.exportAccount.useMutation();
1321

22+
useEffect(() => {
23+
return () => {
24+
if (holdTimerRef.current) {
25+
clearInterval(holdTimerRef.current);
26+
}
27+
};
28+
}, []);
29+
1430
if (isLoading) return <Text>Loading...</Text>;
1531
if (!data) return <Text>No Account</Text>;
1632

33+
const handleExport = async (format: AccountFormat) => {
34+
setExportType(format);
35+
setExportedContent(null);
36+
setHoldProgress(3);
37+
setModalVisible(true);
38+
};
39+
40+
const startHoldTimer = () => {
41+
if (isHoldingRef.current) return;
42+
isHoldingRef.current = true;
43+
44+
holdTimerRef.current = setInterval(() => {
45+
setHoldProgress((prev) => {
46+
if (prev <= 1) {
47+
if (holdTimerRef.current) {
48+
clearInterval(holdTimerRef.current);
49+
}
50+
handleReveal();
51+
return 0;
52+
}
53+
return prev - 1;
54+
});
55+
}, 1000);
56+
};
57+
58+
const cancelHoldTimer = () => {
59+
if (holdTimerRef.current) {
60+
clearInterval(holdTimerRef.current);
61+
holdTimerRef.current = null;
62+
}
63+
isHoldingRef.current = false;
64+
setHoldProgress(3);
65+
};
66+
67+
const handleReveal = async () => {
68+
if (!exportType) return;
69+
70+
const acc = await exportAccount.mutateAsync({
71+
name: data.name,
72+
format: exportType,
73+
});
74+
setExportedContent(acc);
75+
};
76+
77+
const handleCopy = async () => {
78+
if (exportedContent) {
79+
await setStringAsync(exportedContent);
80+
}
81+
};
82+
83+
const closeModal = () => {
84+
setModalVisible(false);
85+
setExportType(null);
86+
setExportedContent(null);
87+
cancelHoldTimer();
88+
};
89+
90+
const getExportTypeName = (format: AccountFormat) => {
91+
switch (format) {
92+
case AccountFormat.Mnemonic:
93+
return "Mnemonic Phrase";
94+
case AccountFormat.Base64Json:
95+
return "Encoded Key";
96+
case AccountFormat.SpendingKey:
97+
return "Spending Key";
98+
default:
99+
return "Export";
100+
}
101+
};
102+
17103
return (
18-
<View style={styles.container}>
19-
<Button title="Back" onPress={() => router.dismiss()} />
20-
21-
<View>
22-
<Button
23-
onPress={async () => {
24-
const acc = await exportAccount.mutateAsync({
25-
name: data.name,
26-
format: AccountFormat.Mnemonic,
27-
});
28-
console.log(acc);
29-
}}
30-
title="Mnemonic Phrase"
31-
/>
32-
<Button
33-
onPress={async () => {
34-
const acc = await exportAccount.mutateAsync({
35-
name: data.name,
36-
format: AccountFormat.Base64Json,
37-
});
38-
console.log(acc);
39-
}}
40-
title="Encoded Key"
41-
/>
42-
<Button
43-
onPress={async () => {
44-
const acc = await exportAccount.mutateAsync({
45-
name: data.name,
46-
format: AccountFormat.SpendingKey,
47-
});
48-
console.log(acc);
49-
}}
50-
title="Spending Key"
51-
/>
104+
<>
105+
<Stack.Screen options={{ title: "Export Account" }} />
106+
<View style={styles.container}>
107+
<View style={styles.contentContainer}>
108+
<Text category="s1" appearance="hint" style={styles.explanationText}>
109+
Export your account to transition to another wallet, or to ensure
110+
complete control and safety of your digital assets
111+
</Text>
112+
<View style={styles.buttonContainer}>
113+
<Button onPress={() => handleExport(AccountFormat.Mnemonic)}>
114+
Mnemonic Phrase
115+
</Button>
116+
<Button onPress={() => handleExport(AccountFormat.Base64Json)}>
117+
Encoded Key
118+
</Button>
119+
<Button onPress={() => handleExport(AccountFormat.SpendingKey)}>
120+
Spending Key
121+
</Button>
122+
</View>
123+
</View>
124+
125+
<Modal
126+
visible={modalVisible}
127+
backdropStyle={styles.backdrop}
128+
onBackdropPress={closeModal}
129+
>
130+
<Card disabled style={styles.modalCard}>
131+
{!exportedContent ? (
132+
<>
133+
<Text category="h6" style={styles.modalTitle}>
134+
{`Export ${exportType ? getExportTypeName(exportType) : ""}`}
135+
</Text>
136+
<Text style={styles.modalText}>
137+
Make sure you're in a private setting before revealing your{" "}
138+
{exportType
139+
? getExportTypeName(exportType).toLowerCase()
140+
: ""}
141+
. This information is sensitive and should be kept secure.
142+
</Text>
143+
<Button
144+
onPressIn={startHoldTimer}
145+
onPressOut={cancelHoldTimer}
146+
style={styles.modalButton}
147+
>
148+
{`Hold to reveal (${holdProgress}s)`}
149+
</Button>
150+
<Button appearance="ghost" onPress={closeModal}>
151+
Cancel
152+
</Button>
153+
</>
154+
) : (
155+
<>
156+
<Text category="h6" style={styles.modalTitle}>
157+
{`Export ${exportType ? getExportTypeName(exportType) : ""}`}
158+
</Text>
159+
<Card disabled style={styles.sensitiveContentContainer}>
160+
<Text
161+
selectable
162+
style={styles.sensitiveContent}
163+
numberOfLines={8}
164+
>
165+
{exportedContent || ""}
166+
</Text>
167+
</Card>
168+
<Button onPress={handleCopy} style={styles.modalButton}>
169+
{`Copy to clipboard`}
170+
</Button>
171+
<Button appearance="ghost" onPress={closeModal}>
172+
Close
173+
</Button>
174+
</>
175+
)}
176+
</Card>
177+
</Modal>
178+
179+
<StatusBar style="auto" />
52180
</View>
53-
<StatusBar style="auto" />
54-
</View>
181+
</>
55182
);
56183
}
57184

58185
const styles = StyleSheet.create({
59186
container: {
60187
flex: 1,
61188
backgroundColor: "#fff",
62-
alignItems: "center",
63-
justifyContent: "center",
189+
},
190+
contentContainer: {
191+
flex: 1,
192+
padding: 16,
193+
paddingTop: 32,
194+
},
195+
explanationText: {
196+
textAlign: "center",
197+
marginBottom: 24,
198+
paddingHorizontal: 16,
199+
},
200+
buttonContainer: {
201+
gap: 16,
202+
width: "100%",
203+
},
204+
backdrop: {
205+
backgroundColor: "rgba(0, 0, 0, 0.5)",
206+
},
207+
modalCard: {
208+
margin: 16,
209+
paddingHorizontal: 16,
210+
paddingTop: 16,
211+
},
212+
modalTitle: {
213+
textAlign: "center",
214+
marginBottom: 16,
215+
},
216+
modalText: {
217+
textAlign: "center",
218+
marginBottom: 16,
219+
},
220+
modalButton: {
221+
marginBottom: 8,
222+
},
223+
sensitiveContentContainer: {
224+
marginBottom: 16,
225+
},
226+
sensitiveContent: {
227+
// textAlign: "center",
228+
fontSize: 16,
229+
fontFamily: "monospace",
64230
},
65231
});

0 commit comments

Comments
 (0)