complete_heir_functions

This commit is contained in:
lusixing
2026-02-02 19:40:49 -08:00
parent 5c1172a912
commit 6822638d47
5 changed files with 373 additions and 9 deletions

View File

@@ -25,7 +25,7 @@ import { VaultAsset, VaultAssetType, Heir } from '../types';
import BiometricModal from '../components/common/BiometricModal';
import { useAuth } from '../context/AuthContext';
import { useVaultAssets } from '../hooks/useVaultAssets';
import { getVaultStorageKeys } from '../config';
import { getVaultStorageKeys, DEBUG_MODE } from '../config';
import { mnemonicToEntropy, splitSecret, serializeShare } from '../utils/sss';
import { storageService } from '../services/storage.service';
import { SentinelVault } from '@/utils/crypto_core';
@@ -116,6 +116,7 @@ export default function VaultScreen() {
refreshAssets,
createAsset: createVaultAsset,
deleteAsset: deleteVaultAsset,
assignAsset: assignVaultAsset,
isSealing,
createError: addError,
clearCreateError: clearAddError,
@@ -139,6 +140,11 @@ export default function VaultScreen() {
const [showAddBiometric, setShowAddBiometric] = useState(false);
const [accountProvider, setAccountProvider] = useState<'bank' | 'steam' | 'facebook' | 'custom'>('bank');
const [showMnemonic, setShowMnemonic] = useState(false);
const [showAssignModal, setShowAssignModal] = useState(false);
const [showAssignErrorModal, setShowAssignErrorModal] = useState(false);
const [assignErrorMessage, setAssignErrorMessage] = useState('');
const [isAssigning, setIsAssigning] = useState(false);
const [heirEmail, setHeirEmail] = useState('');
const [showLegacyAssignCta, setShowLegacyAssignCta] = useState(false);
const [hasS0, setHasS0] = useState<boolean | null>(null);
const [backupContent, setBackupContent] = useState<string | null>(null);
@@ -468,6 +474,10 @@ export default function VaultScreen() {
setSelectedAsset(asset);
setShowDetail(true);
setShowKeyPreview(false);
if (DEBUG_MODE) {
console.log('[DEBUG] Vault Asset Details:', JSON.stringify(asset.rawData, null, 2));
}
};
const handleCloseDetail = () => {
@@ -532,6 +542,38 @@ export default function VaultScreen() {
}
};
const handleAssignHeir = async () => {
if (!selectedAsset || isAssigning) return;
if (!heirEmail.trim() || !heirEmail.includes('@')) {
Alert.alert('Invalid Email', 'Please enter a valid email address.');
return;
}
setIsAssigning(true);
try {
const result = await assignVaultAsset(Number(selectedAsset.id), heirEmail.trim());
if (result.success) {
setShowAssignModal(false);
setHeirEmail('');
Alert.alert('Success', `Asset assigned to ${heirEmail.trim()}`);
} else if (result.isUnauthorized) {
setShowAssignModal(false);
if (typeof Alert !== 'undefined' && Alert.alert) {
Alert.alert('Unauthorized', 'Your session has expired. Please sign in again.');
}
} else if (result.error) {
setAssignErrorMessage(result.error);
setShowAssignErrorModal(true);
}
} catch (error) {
console.error('Assign error:', error);
setAssignErrorMessage('An unexpected error occurred during assignment.');
setShowAssignErrorModal(true);
} finally {
setIsAssigning(false);
}
};
const handleGuardedAccess = () => {
setShowGuardedBiometric(true);
};
@@ -1475,6 +1517,12 @@ export default function VaultScreen() {
<Text style={styles.metaLabel}>{detailMetaLabel}</Text>
<Text style={styles.metaValue}>{detailMetaValue}</Text>
</View>
{selectedAsset?.heirEmail ? (
<View style={[styles.metaCard, { width: '100%' }]}>
<Text style={styles.metaLabel}>ASSIGNED HEIR</Text>
<Text style={styles.metaValue}>{selectedAsset.heirEmail}</Text>
</View>
) : null}
</View>
<View style={styles.actionGroup}>
@@ -1487,14 +1535,26 @@ export default function VaultScreen() {
<MaterialCommunityIcons name="file-lock" size={18} color={colors.vault.primary} />
<Text style={styles.actionText}>Export Cipher Pack</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.actionRow} activeOpacity={0.8}>
<MaterialCommunityIcons name="refresh" size={18} color={colors.vault.primary} />
<TouchableOpacity style={styles.actionRow} activeOpacity={0.7}>
<Ionicons name="notifications-outline" size={18} color={colors.vault.text} />
<Text style={styles.actionText}>Reset Sentinel Timer</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.actionRow, styles.assignActionRow]}
onPress={() => setShowAssignModal(true)}
activeOpacity={0.7}
>
<Ionicons name="person-add-outline" size={18} color={colors.vault.text} />
<Text style={styles.actionText}>
{selectedAsset?.heirEmail ? 'Change Heir' : 'Assign Heir'}
</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.actionRow, styles.deleteActionRow]}
onPress={() => setShowDeleteConfirm(true)}
activeOpacity={0.8}
activeOpacity={0.7}
>
<Feather name="trash-2" size={18} color={colors.vault.warning} />
<Text style={[styles.actionText, styles.deleteActionText]}>Delete Treasure</Text>
@@ -1593,6 +1653,110 @@ export default function VaultScreen() {
</View>
</View>
</Modal>
{/* Assign Heir Modal */}
<Modal
visible={showAssignModal}
animationType="slide"
transparent
onRequestClose={() => setShowAssignModal(false)}
>
<View style={styles.modalOverlay}>
<TouchableOpacity
style={styles.modalOverlayDismiss}
activeOpacity={1}
onPress={() => setShowAssignModal(false)}
/>
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
style={styles.assignModalContainer}
>
<View style={styles.assignModalContent}>
<View style={styles.assignModalHeader}>
<View style={styles.assignIconGlow}>
<Ionicons name="person-add" size={32} color={colors.vault.primary} />
</View>
<Text style={styles.assignTitle}>Designate Heir</Text>
<Text style={styles.assignSubtitle}>
Enter the email address of the person who should inherit this treasure.
</Text>
</View>
<View style={styles.assignInputContainer}>
<Text style={styles.inputLabel}>HEIR EMAIL ADDRESS</Text>
<TextInput
style={styles.assignInput}
value={heirEmail}
onChangeText={setHeirEmail}
placeholder="name@example.com"
placeholderTextColor={colors.vault.textSecondary}
keyboardType="email-address"
autoCapitalize="none"
autoCorrect={false}
/>
</View>
<View style={styles.assignButtons}>
<TouchableOpacity
style={styles.assignCancelButton}
onPress={() => {
setShowAssignModal(false);
setHeirEmail('');
}}
disabled={isAssigning}
>
<Text style={styles.assignCancelText}>Cancel</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.assignConfirmButton}
onPress={handleAssignHeir}
disabled={isAssigning || !heirEmail.trim()}
>
<LinearGradient
colors={[colors.vault.primary, colors.vault.secondary]}
style={styles.assignConfirmGradient}
>
{isAssigning ? (
<Text style={styles.assignConfirmText}>Assigning...</Text>
) : (
<Text style={styles.assignConfirmText}>Confirm Heir</Text>
)}
</LinearGradient>
</TouchableOpacity>
</View>
</View>
</KeyboardAvoidingView>
</View>
</Modal>
{/* Assign Heir Error Modal */}
<Modal
visible={showAssignErrorModal}
animationType="fade"
transparent
onRequestClose={() => setShowAssignErrorModal(false)}
>
<View style={styles.modalOverlay}>
<TouchableOpacity
style={styles.modalOverlayDismiss}
activeOpacity={1}
onPress={() => setShowAssignErrorModal(false)}
/>
<View style={styles.errorModalContent}>
<View style={styles.errorIconContainer}>
<MaterialCommunityIcons name="alert-circle-outline" size={32} color={colors.vault.warning} />
</View>
<Text style={styles.errorTitle}>Assignment Failed</Text>
<Text style={styles.errorMessage}>{assignErrorMessage}</Text>
<TouchableOpacity
style={styles.errorCloseButton}
onPress={() => setShowAssignErrorModal(false)}
>
<Text style={styles.errorCloseButtonText}>Dismiss</Text>
</TouchableOpacity>
</View>
</View>
</Modal>
</View>
);
@@ -2794,4 +2958,142 @@ const styles = StyleSheet.create({
confirmCancelText: {
color: colors.vault.textSecondary,
},
assignActionRow: {
// Optional specific styling
},
assignModalContainer: {
backgroundColor: colors.vault.cardBackground,
marginHorizontal: spacing.lg,
borderRadius: borderRadius.xl,
borderWidth: 1,
borderColor: colors.vault.cardBorder,
overflow: 'hidden',
marginBottom: spacing.xxl,
},
assignModalContent: {
padding: spacing.xl,
},
assignModalHeader: {
alignItems: 'center',
marginBottom: spacing.xl,
},
assignIconGlow: {
width: 64,
height: 64,
borderRadius: 32,
backgroundColor: `${colors.vault.primary}15`,
alignItems: 'center',
justifyContent: 'center',
marginBottom: spacing.md,
},
assignTitle: {
fontSize: typography.fontSize.lg,
color: colors.vault.text,
fontWeight: '700',
marginBottom: spacing.xs,
fontFamily: typography.fontFamily.serif,
},
assignSubtitle: {
fontSize: typography.fontSize.sm,
color: colors.vault.textSecondary,
textAlign: 'center',
lineHeight: typography.fontSize.sm * 1.5,
},
assignInputContainer: {
marginBottom: spacing.xl,
},
inputLabel: {
fontSize: typography.fontSize.xs,
color: colors.vault.textSecondary,
fontWeight: '700',
letterSpacing: 1,
marginBottom: spacing.sm,
},
assignInput: {
backgroundColor: 'rgba(255, 255, 255, 0.05)',
borderRadius: borderRadius.lg,
padding: spacing.md,
fontSize: typography.fontSize.base,
color: colors.vault.text,
borderWidth: 1,
borderColor: 'rgba(255, 255, 255, 0.1)',
},
assignButtons: {
flexDirection: 'row',
gap: spacing.md,
},
assignCancelButton: {
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)',
},
assignCancelText: {
color: colors.vault.textSecondary,
fontSize: typography.fontSize.base,
fontWeight: '600',
},
assignConfirmButton: {
flex: 2,
borderRadius: borderRadius.lg,
overflow: 'hidden',
},
assignConfirmGradient: {
paddingVertical: spacing.md,
alignItems: 'center',
justifyContent: 'center',
},
assignConfirmText: {
color: colors.vault.text,
fontSize: typography.fontSize.base,
fontWeight: '700',
},
errorModalContent: {
backgroundColor: colors.vault.cardBackground,
marginHorizontal: spacing.lg,
borderRadius: borderRadius.xl,
padding: spacing.xl,
borderWidth: 1,
borderColor: colors.vault.cardBorder,
alignItems: 'center',
marginBottom: spacing.xxl,
},
errorIconContainer: {
width: 64,
height: 64,
borderRadius: 32,
backgroundColor: `${colors.vault.warning}15`,
alignItems: 'center',
justifyContent: 'center',
marginBottom: spacing.lg,
},
errorTitle: {
fontSize: typography.fontSize.lg,
color: colors.vault.text,
fontWeight: '700',
marginBottom: spacing.sm,
fontFamily: typography.fontFamily.serif,
},
errorMessage: {
fontSize: typography.fontSize.sm,
color: colors.vault.textSecondary,
textAlign: 'center',
lineHeight: typography.fontSize.sm * 1.5,
marginBottom: spacing.xl,
},
errorCloseButton: {
width: '100%',
paddingVertical: spacing.md,
borderRadius: borderRadius.lg,
backgroundColor: colors.vault.warning,
alignItems: 'center',
},
errorCloseButtonText: {
color: colors.vault.text,
fontSize: typography.fontSize.base,
fontWeight: '700',
},
});