added_reveal_secret_and_delete_treasure
This commit is contained in:
@@ -27,6 +27,7 @@ import { useAuth } from '../context/AuthContext';
|
||||
import { useVaultAssets } from '../hooks/useVaultAssets';
|
||||
import { getVaultStorageKeys } from '../config';
|
||||
import { mnemonicToEntropy, splitSecret, serializeShare } from '../utils/sss';
|
||||
import { storageService } from '../services/storage.service';
|
||||
import { SentinelVault } from '@/utils/crypto_core';
|
||||
|
||||
// Asset type configuration with nautical theme
|
||||
@@ -92,41 +93,6 @@ type HeirAssignment = {
|
||||
heir: Heir;
|
||||
};
|
||||
|
||||
// Mock data
|
||||
// const initialAssets: VaultAsset[] = [
|
||||
// {
|
||||
// id: '1',
|
||||
// type: 'private_key',
|
||||
// label: 'ETH Main Wallet Key',
|
||||
// createdAt: new Date('2024-01-10'),
|
||||
// updatedAt: new Date('2024-01-10'),
|
||||
// isEncrypted: true,
|
||||
// },
|
||||
// {
|
||||
// id: '2',
|
||||
// type: 'game_account',
|
||||
// label: 'Steam Account Credentials',
|
||||
// createdAt: new Date('2024-01-08'),
|
||||
// updatedAt: new Date('2024-01-08'),
|
||||
// isEncrypted: true,
|
||||
// },
|
||||
// {
|
||||
// id: '3',
|
||||
// type: 'document',
|
||||
// label: 'Insurance Policy Scan',
|
||||
// createdAt: new Date('2024-01-05'),
|
||||
// updatedAt: new Date('2024-01-05'),
|
||||
// isEncrypted: true,
|
||||
// },
|
||||
// {
|
||||
// id: '4',
|
||||
// type: 'will',
|
||||
// label: 'Testament Draft v2',
|
||||
// createdAt: new Date('2024-01-02'),
|
||||
// updatedAt: new Date('2024-01-15'),
|
||||
// isEncrypted: true,
|
||||
// },
|
||||
// ];
|
||||
|
||||
const renderAssetTypeIcon = (config: typeof assetTypeConfig[VaultAssetType], size: number, color: string) => {
|
||||
switch (config.iconType) {
|
||||
@@ -149,11 +115,14 @@ export default function VaultScreen() {
|
||||
setAssets,
|
||||
refreshAssets,
|
||||
createAsset: createVaultAsset,
|
||||
deleteAsset: deleteVaultAsset,
|
||||
isSealing,
|
||||
createError: addError,
|
||||
clearCreateError: clearAddError,
|
||||
} = useVaultAssets(isUnlocked);
|
||||
const [showAddModal, setShowAddModal] = useState(false);
|
||||
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
|
||||
const [isDeleting, setIsDeleting] = useState(false);
|
||||
const [selectedType, setSelectedType] = useState<VaultAssetType>('custom');
|
||||
const [newLabel, setNewLabel] = useState('');
|
||||
const [showUploadSuccess, setShowUploadSuccess] = useState(false);
|
||||
@@ -172,6 +141,8 @@ export default function VaultScreen() {
|
||||
const [showMnemonic, setShowMnemonic] = useState(false);
|
||||
const [showLegacyAssignCta, setShowLegacyAssignCta] = useState(false);
|
||||
const [hasS0, setHasS0] = useState<boolean | null>(null);
|
||||
const [backupContent, setBackupContent] = useState<string | null>(null);
|
||||
const [isFetchingBackup, setIsFetchingBackup] = useState(false);
|
||||
const [mnemonicWords, setMnemonicWords] = useState<string[]>([]);
|
||||
const [mnemonicParts, setMnemonicParts] = useState<string[][]>([]);
|
||||
const [mnemonicStep, setMnemonicStep] = useState<1 | 2 | 3 | 4 | 5>(1);
|
||||
@@ -504,6 +475,61 @@ export default function VaultScreen() {
|
||||
setSelectedAsset(null);
|
||||
setShowKeyPreview(false);
|
||||
setShowGuardedBiometric(false);
|
||||
setBackupContent(null);
|
||||
};
|
||||
|
||||
const handleFetchBackup = async () => {
|
||||
if (!selectedAsset || !user?.id) return;
|
||||
|
||||
setIsFetchingBackup(true);
|
||||
try {
|
||||
const content = await storageService.getAssetBackup(Number(selectedAsset.id), user.id);
|
||||
if (content) {
|
||||
setBackupContent(content);
|
||||
} else {
|
||||
if (typeof Alert !== 'undefined' && Alert.alert) {
|
||||
Alert.alert('No Backup Found', 'No local plaintext backup found for this treasure.');
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Fetch backup error:', error);
|
||||
if (typeof Alert !== 'undefined' && Alert.alert) {
|
||||
Alert.alert('Error', 'Failed to retrieve local backup.');
|
||||
}
|
||||
} finally {
|
||||
setIsFetchingBackup(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeleteAsset = async () => {
|
||||
if (!selectedAsset || isDeleting) return;
|
||||
|
||||
setIsDeleting(true);
|
||||
try {
|
||||
const result = await deleteVaultAsset(Number(selectedAsset.id));
|
||||
if (result.success) {
|
||||
setShowDeleteConfirm(false);
|
||||
handleCloseDetail();
|
||||
if (typeof Alert !== 'undefined' && Alert.alert) {
|
||||
Alert.alert('Success', 'Treasure removed from the vault.');
|
||||
}
|
||||
} else if (result.isUnauthorized) {
|
||||
setShowDeleteConfirm(false);
|
||||
handleCloseDetail();
|
||||
if (typeof Alert !== 'undefined' && Alert.alert) {
|
||||
Alert.alert('Unauthorized', 'Your session has expired. Please sign in again.');
|
||||
}
|
||||
} else if (result.error && typeof Alert !== 'undefined' && Alert.alert) {
|
||||
Alert.alert('Failed', result.error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Delete error:', error);
|
||||
if (typeof Alert !== 'undefined' && Alert.alert) {
|
||||
Alert.alert('Error', 'An unexpected error occurred during deletion.');
|
||||
}
|
||||
} finally {
|
||||
setIsDeleting(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleGuardedAccess = () => {
|
||||
@@ -513,6 +539,7 @@ export default function VaultScreen() {
|
||||
const handleGuardedSuccess = () => {
|
||||
setShowGuardedBiometric(false);
|
||||
setShowKeyPreview(true);
|
||||
handleFetchBackup();
|
||||
};
|
||||
|
||||
const handleAddVerification = () => {
|
||||
@@ -1464,6 +1491,14 @@ export default function VaultScreen() {
|
||||
<MaterialCommunityIcons name="refresh" size={18} color={colors.vault.primary} />
|
||||
<Text style={styles.actionText}>Reset Sentinel Timer</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
style={[styles.actionRow, styles.deleteActionRow]}
|
||||
onPress={() => setShowDeleteConfirm(true)}
|
||||
activeOpacity={0.8}
|
||||
>
|
||||
<Feather name="trash-2" size={18} color={colors.vault.warning} />
|
||||
<Text style={[styles.actionText, styles.deleteActionText]}>Delete Treasure</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
<View style={styles.guardCard}>
|
||||
@@ -1474,6 +1509,7 @@ export default function VaultScreen() {
|
||||
<Text style={styles.guardText}>
|
||||
Plaintext access requires biometric verification and a memory rehearsal step.
|
||||
</Text>
|
||||
|
||||
<TouchableOpacity
|
||||
style={styles.guardButton}
|
||||
onPress={handleGuardedAccess}
|
||||
@@ -1481,10 +1517,13 @@ export default function VaultScreen() {
|
||||
>
|
||||
<Text style={styles.guardButtonText}>Begin Verification</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
{showKeyPreview && (
|
||||
<View style={styles.previewCard}>
|
||||
<Text style={styles.previewLabel}>MNEMONIC SHARD (MASKED)</Text>
|
||||
<Text style={styles.previewValue}>ocean-anchored-ember-veil</Text>
|
||||
<Text style={styles.previewLabel}>LOCAL PLAINTEXT BACKUP</Text>
|
||||
<Text style={styles.previewValue}>
|
||||
{isFetchingBackup ? 'Fetching content...' : (backupContent || 'No local backup found for this treasure')}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
@@ -1509,6 +1548,51 @@ export default function VaultScreen() {
|
||||
isDark
|
||||
/>
|
||||
</Modal>
|
||||
|
||||
{/* Delete Confirmation Modal */}
|
||||
<Modal
|
||||
visible={showDeleteConfirm}
|
||||
animationType="fade"
|
||||
transparent
|
||||
onRequestClose={() => setShowDeleteConfirm(false)}
|
||||
>
|
||||
<View style={styles.modalOverlay}>
|
||||
<TouchableOpacity
|
||||
style={styles.modalOverlayDismiss}
|
||||
activeOpacity={1}
|
||||
onPress={() => setShowDeleteConfirm(false)}
|
||||
/>
|
||||
<View style={styles.deleteConfirmContent}>
|
||||
<View style={styles.deleteIconContainer}>
|
||||
<Feather name="alert-triangle" size={32} color={colors.vault.warning} />
|
||||
</View>
|
||||
<Text style={styles.deleteTitle}>Remove Treasure?</Text>
|
||||
<Text style={styles.deleteMessage}>
|
||||
This action cannot be undone. The treasure will be permanently shredded from the deep vault.
|
||||
</Text>
|
||||
<View style={styles.deleteButtons}>
|
||||
<TouchableOpacity
|
||||
style={styles.deleteCancelButton}
|
||||
onPress={() => setShowDeleteConfirm(false)}
|
||||
disabled={isDeleting}
|
||||
>
|
||||
<Text style={styles.deleteCancelText}>Cancel</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
style={styles.deleteConfirmButton}
|
||||
onPress={handleDeleteAsset}
|
||||
disabled={isDeleting}
|
||||
>
|
||||
{isDeleting ? (
|
||||
<Text style={styles.deleteConfirmText}>Shredding...</Text>
|
||||
) : (
|
||||
<Text style={styles.deleteConfirmText}>Confirm Delete</Text>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</Modal>
|
||||
</View>
|
||||
);
|
||||
|
||||
@@ -1777,6 +1861,73 @@ const styles = StyleSheet.create({
|
||||
backgroundColor: 'rgba(26, 58, 74, 0.8)',
|
||||
justifyContent: 'flex-end',
|
||||
},
|
||||
modalOverlayDismiss: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
},
|
||||
deleteConfirmContent: {
|
||||
backgroundColor: colors.vault.cardBackground,
|
||||
marginHorizontal: spacing.lg,
|
||||
borderRadius: borderRadius.xl,
|
||||
padding: spacing.xl,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.vault.cardBorder,
|
||||
alignItems: 'center',
|
||||
marginBottom: spacing.xxl + spacing.lg,
|
||||
},
|
||||
deleteIconContainer: {
|
||||
width: 64,
|
||||
height: 64,
|
||||
borderRadius: 32,
|
||||
backgroundColor: `${colors.vault.warning}20`,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
marginBottom: spacing.lg,
|
||||
},
|
||||
deleteTitle: {
|
||||
fontSize: typography.fontSize.lg,
|
||||
color: colors.vault.text,
|
||||
fontWeight: '700',
|
||||
marginBottom: spacing.sm,
|
||||
fontFamily: typography.fontFamily.serif,
|
||||
},
|
||||
deleteMessage: {
|
||||
fontSize: typography.fontSize.sm,
|
||||
color: colors.vault.textSecondary,
|
||||
textAlign: 'center',
|
||||
lineHeight: typography.fontSize.sm * 1.5,
|
||||
marginBottom: spacing.xl,
|
||||
},
|
||||
deleteButtons: {
|
||||
flexDirection: 'row',
|
||||
gap: spacing.md,
|
||||
width: '100%',
|
||||
},
|
||||
deleteCancelButton: {
|
||||
flex: 1,
|
||||
paddingVertical: spacing.md,
|
||||
borderRadius: borderRadius.lg,
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.08)',
|
||||
alignItems: 'center',
|
||||
borderWidth: 1,
|
||||
borderColor: 'rgba(255, 255, 255, 0.1)',
|
||||
},
|
||||
deleteCancelText: {
|
||||
color: colors.vault.textSecondary,
|
||||
fontSize: typography.fontSize.base,
|
||||
fontWeight: '600',
|
||||
},
|
||||
deleteConfirmButton: {
|
||||
flex: 2,
|
||||
paddingVertical: spacing.md,
|
||||
borderRadius: borderRadius.lg,
|
||||
backgroundColor: colors.vault.warning,
|
||||
alignItems: 'center',
|
||||
},
|
||||
deleteConfirmText: {
|
||||
color: colors.vault.text,
|
||||
fontSize: typography.fontSize.base,
|
||||
fontWeight: '700',
|
||||
},
|
||||
modalContent: {
|
||||
backgroundColor: colors.nautical.cream,
|
||||
borderTopLeftRadius: borderRadius.xxl,
|
||||
@@ -2581,8 +2732,66 @@ const styles = StyleSheet.create({
|
||||
borderColor: colors.sentinel.cardBorder,
|
||||
},
|
||||
mnemonicSecondaryText: {
|
||||
color: colors.sentinel.text,
|
||||
fontWeight: '700',
|
||||
letterSpacing: typography.letterSpacing.wide,
|
||||
},
|
||||
deleteActionRow: {
|
||||
backgroundColor: 'rgba(229, 115, 115, 0.08)',
|
||||
borderColor: 'rgba(229, 115, 115, 0.2)',
|
||||
marginTop: spacing.sm,
|
||||
},
|
||||
deleteActionText: {
|
||||
color: colors.vault.warning,
|
||||
},
|
||||
confirmModalOverlay: {
|
||||
flex: 1,
|
||||
backgroundColor: 'rgba(11, 20, 24, 0.85)',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
padding: spacing.xl,
|
||||
},
|
||||
confirmModalContent: {
|
||||
width: '100%',
|
||||
backgroundColor: colors.vault.cardBackground,
|
||||
borderRadius: borderRadius.xl,
|
||||
padding: spacing.lg,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.vault.cardBorder,
|
||||
gap: spacing.md,
|
||||
},
|
||||
confirmModalTitle: {
|
||||
fontSize: typography.fontSize.lg,
|
||||
color: colors.vault.text,
|
||||
fontWeight: '700',
|
||||
textAlign: 'center',
|
||||
},
|
||||
confirmModalText: {
|
||||
fontSize: typography.fontSize.base,
|
||||
color: colors.vault.textSecondary,
|
||||
textAlign: 'center',
|
||||
lineHeight: 22,
|
||||
},
|
||||
confirmModalButtons: {
|
||||
flexDirection: 'row',
|
||||
gap: spacing.md,
|
||||
marginTop: spacing.sm,
|
||||
},
|
||||
confirmModalButton: {
|
||||
flex: 1,
|
||||
paddingVertical: spacing.md,
|
||||
borderRadius: borderRadius.lg,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
confirmCancelButton: {
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.05)',
|
||||
borderWidth: 1,
|
||||
borderColor: 'rgba(255, 255, 255, 0.1)',
|
||||
},
|
||||
confirmDeleteButton: {
|
||||
backgroundColor: colors.vault.warning,
|
||||
},
|
||||
confirmCancelText: {
|
||||
color: colors.vault.textSecondary,
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user