complete_heir_functions
This commit is contained in:
@@ -40,6 +40,8 @@ export interface UseVaultAssetsReturn {
|
|||||||
createAsset: (params: { title: string; content: string }) => Promise<CreateAssetResult>;
|
createAsset: (params: { title: string; content: string }) => Promise<CreateAssetResult>;
|
||||||
/** Delete asset via POST /assets/delete; on success refreshes list */
|
/** Delete asset via POST /assets/delete; on success refreshes list */
|
||||||
deleteAsset: (assetId: number) => Promise<CreateAssetResult>;
|
deleteAsset: (assetId: number) => Promise<CreateAssetResult>;
|
||||||
|
/** Assign asset to heir via POST /assets/assign */
|
||||||
|
assignAsset: (assetId: number, heirEmail: string) => Promise<CreateAssetResult>;
|
||||||
/** True while create request is in flight */
|
/** True while create request is in flight */
|
||||||
isSealing: boolean;
|
isSealing: boolean;
|
||||||
/** Error message from last create failure (non-401) */
|
/** Error message from last create failure (non-401) */
|
||||||
@@ -68,10 +70,14 @@ export function useVaultAssets(isUnlocked: boolean): UseVaultAssetsReturn {
|
|||||||
if (Array.isArray(list)) {
|
if (Array.isArray(list)) {
|
||||||
setAssets(mapApiAssetsToVaultAssets(list as ApiAsset[]));
|
setAssets(mapApiAssetsToVaultAssets(list as ApiAsset[]));
|
||||||
}
|
}
|
||||||
} catch {
|
} catch (err: unknown) {
|
||||||
|
const rawMessage = err instanceof Error ? err.message : String(err ?? '');
|
||||||
|
if (/Could not validate credentials/i.test(rawMessage)) {
|
||||||
|
signOut();
|
||||||
|
}
|
||||||
// Keep current assets (mock or previous fetch)
|
// Keep current assets (mock or previous fetch)
|
||||||
}
|
}
|
||||||
}, [token]);
|
}, [token, signOut]);
|
||||||
|
|
||||||
// Fetch list when unlocked and token exists
|
// Fetch list when unlocked and token exists
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -84,7 +90,13 @@ export function useVaultAssets(isUnlocked: boolean): UseVaultAssetsReturn {
|
|||||||
setAssets(mapApiAssetsToVaultAssets(list as ApiAsset[]));
|
setAssets(mapApiAssetsToVaultAssets(list as ApiAsset[]));
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch((err) => {
|
||||||
|
if (!cancelled) {
|
||||||
|
const rawMessage = err instanceof Error ? err.message : String(err ?? '');
|
||||||
|
if (/Could not validate credentials/i.test(rawMessage)) {
|
||||||
|
signOut();
|
||||||
|
}
|
||||||
|
}
|
||||||
// Keep initial (mock) assets
|
// Keep initial (mock) assets
|
||||||
});
|
});
|
||||||
return () => {
|
return () => {
|
||||||
@@ -212,6 +224,44 @@ export function useVaultAssets(isUnlocked: boolean): UseVaultAssetsReturn {
|
|||||||
[token, refreshAssets, signOut]
|
[token, refreshAssets, signOut]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const assignAsset = useCallback(
|
||||||
|
async (assetId: number, heirEmail: string): Promise<CreateAssetResult> => {
|
||||||
|
if (!token) {
|
||||||
|
return { success: false, error: 'Not logged in.' };
|
||||||
|
}
|
||||||
|
setIsSealing(true);
|
||||||
|
setCreateError(null);
|
||||||
|
try {
|
||||||
|
await assetsService.assignAsset({ asset_id: assetId, heir_email: heirEmail }, token);
|
||||||
|
await refreshAssets();
|
||||||
|
return { success: true };
|
||||||
|
} catch (err: unknown) {
|
||||||
|
const status =
|
||||||
|
err && typeof err === 'object' && 'status' in err
|
||||||
|
? (err as { status?: number }).status
|
||||||
|
: undefined;
|
||||||
|
const rawMessage =
|
||||||
|
err instanceof Error ? err.message : String(err ?? 'Failed to assign.');
|
||||||
|
const isUnauthorized =
|
||||||
|
status === 401 || /401|Unauthorized/i.test(rawMessage);
|
||||||
|
|
||||||
|
if (isUnauthorized) {
|
||||||
|
signOut();
|
||||||
|
return { success: false, isUnauthorized: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
const friendlyMessage = /failed to fetch|network error/i.test(rawMessage)
|
||||||
|
? 'Network error. Please check that the backend is running and reachable.'
|
||||||
|
: rawMessage;
|
||||||
|
setCreateError(friendlyMessage);
|
||||||
|
return { success: false, error: friendlyMessage };
|
||||||
|
} finally {
|
||||||
|
setIsSealing(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[token, signOut]
|
||||||
|
);
|
||||||
|
|
||||||
const clearCreateError = useCallback(() => setCreateError(null), []);
|
const clearCreateError = useCallback(() => setCreateError(null), []);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -220,6 +270,7 @@ export function useVaultAssets(isUnlocked: boolean): UseVaultAssetsReturn {
|
|||||||
refreshAssets,
|
refreshAssets,
|
||||||
createAsset,
|
createAsset,
|
||||||
deleteAsset,
|
deleteAsset,
|
||||||
|
assignAsset,
|
||||||
isSealing,
|
isSealing,
|
||||||
createError,
|
createError,
|
||||||
clearCreateError,
|
clearCreateError,
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import { VaultAsset, VaultAssetType, Heir } from '../types';
|
|||||||
import BiometricModal from '../components/common/BiometricModal';
|
import BiometricModal from '../components/common/BiometricModal';
|
||||||
import { useAuth } from '../context/AuthContext';
|
import { useAuth } from '../context/AuthContext';
|
||||||
import { useVaultAssets } from '../hooks/useVaultAssets';
|
import { useVaultAssets } from '../hooks/useVaultAssets';
|
||||||
import { getVaultStorageKeys } from '../config';
|
import { getVaultStorageKeys, DEBUG_MODE } from '../config';
|
||||||
import { mnemonicToEntropy, splitSecret, serializeShare } from '../utils/sss';
|
import { mnemonicToEntropy, splitSecret, serializeShare } from '../utils/sss';
|
||||||
import { storageService } from '../services/storage.service';
|
import { storageService } from '../services/storage.service';
|
||||||
import { SentinelVault } from '@/utils/crypto_core';
|
import { SentinelVault } from '@/utils/crypto_core';
|
||||||
@@ -116,6 +116,7 @@ export default function VaultScreen() {
|
|||||||
refreshAssets,
|
refreshAssets,
|
||||||
createAsset: createVaultAsset,
|
createAsset: createVaultAsset,
|
||||||
deleteAsset: deleteVaultAsset,
|
deleteAsset: deleteVaultAsset,
|
||||||
|
assignAsset: assignVaultAsset,
|
||||||
isSealing,
|
isSealing,
|
||||||
createError: addError,
|
createError: addError,
|
||||||
clearCreateError: clearAddError,
|
clearCreateError: clearAddError,
|
||||||
@@ -139,6 +140,11 @@ export default function VaultScreen() {
|
|||||||
const [showAddBiometric, setShowAddBiometric] = useState(false);
|
const [showAddBiometric, setShowAddBiometric] = useState(false);
|
||||||
const [accountProvider, setAccountProvider] = useState<'bank' | 'steam' | 'facebook' | 'custom'>('bank');
|
const [accountProvider, setAccountProvider] = useState<'bank' | 'steam' | 'facebook' | 'custom'>('bank');
|
||||||
const [showMnemonic, setShowMnemonic] = useState(false);
|
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 [showLegacyAssignCta, setShowLegacyAssignCta] = useState(false);
|
||||||
const [hasS0, setHasS0] = useState<boolean | null>(null);
|
const [hasS0, setHasS0] = useState<boolean | null>(null);
|
||||||
const [backupContent, setBackupContent] = useState<string | null>(null);
|
const [backupContent, setBackupContent] = useState<string | null>(null);
|
||||||
@@ -468,6 +474,10 @@ export default function VaultScreen() {
|
|||||||
setSelectedAsset(asset);
|
setSelectedAsset(asset);
|
||||||
setShowDetail(true);
|
setShowDetail(true);
|
||||||
setShowKeyPreview(false);
|
setShowKeyPreview(false);
|
||||||
|
|
||||||
|
if (DEBUG_MODE) {
|
||||||
|
console.log('[DEBUG] Vault Asset Details:', JSON.stringify(asset.rawData, null, 2));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCloseDetail = () => {
|
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 = () => {
|
const handleGuardedAccess = () => {
|
||||||
setShowGuardedBiometric(true);
|
setShowGuardedBiometric(true);
|
||||||
};
|
};
|
||||||
@@ -1475,6 +1517,12 @@ export default function VaultScreen() {
|
|||||||
<Text style={styles.metaLabel}>{detailMetaLabel}</Text>
|
<Text style={styles.metaLabel}>{detailMetaLabel}</Text>
|
||||||
<Text style={styles.metaValue}>{detailMetaValue}</Text>
|
<Text style={styles.metaValue}>{detailMetaValue}</Text>
|
||||||
</View>
|
</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>
|
||||||
|
|
||||||
<View style={styles.actionGroup}>
|
<View style={styles.actionGroup}>
|
||||||
@@ -1487,14 +1535,26 @@ export default function VaultScreen() {
|
|||||||
<MaterialCommunityIcons name="file-lock" size={18} color={colors.vault.primary} />
|
<MaterialCommunityIcons name="file-lock" size={18} color={colors.vault.primary} />
|
||||||
<Text style={styles.actionText}>Export Cipher Pack</Text>
|
<Text style={styles.actionText}>Export Cipher Pack</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<TouchableOpacity style={styles.actionRow} activeOpacity={0.8}>
|
<TouchableOpacity style={styles.actionRow} activeOpacity={0.7}>
|
||||||
<MaterialCommunityIcons name="refresh" size={18} color={colors.vault.primary} />
|
<Ionicons name="notifications-outline" size={18} color={colors.vault.text} />
|
||||||
<Text style={styles.actionText}>Reset Sentinel Timer</Text>
|
<Text style={styles.actionText}>Reset Sentinel Timer</Text>
|
||||||
</TouchableOpacity>
|
</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
|
<TouchableOpacity
|
||||||
style={[styles.actionRow, styles.deleteActionRow]}
|
style={[styles.actionRow, styles.deleteActionRow]}
|
||||||
onPress={() => setShowDeleteConfirm(true)}
|
onPress={() => setShowDeleteConfirm(true)}
|
||||||
activeOpacity={0.8}
|
activeOpacity={0.7}
|
||||||
>
|
>
|
||||||
<Feather name="trash-2" size={18} color={colors.vault.warning} />
|
<Feather name="trash-2" size={18} color={colors.vault.warning} />
|
||||||
<Text style={[styles.actionText, styles.deleteActionText]}>Delete Treasure</Text>
|
<Text style={[styles.actionText, styles.deleteActionText]}>Delete Treasure</Text>
|
||||||
@@ -1593,6 +1653,110 @@ export default function VaultScreen() {
|
|||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</Modal>
|
</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>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -2794,4 +2958,142 @@ const styles = StyleSheet.create({
|
|||||||
confirmCancelText: {
|
confirmCancelText: {
|
||||||
color: colors.vault.textSecondary,
|
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',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ export interface Asset {
|
|||||||
author_id: number;
|
author_id: number;
|
||||||
private_key_shard: string;
|
private_key_shard: string;
|
||||||
content_outer_encrypted: string;
|
content_outer_encrypted: string;
|
||||||
|
heir_email?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AssetCreate {
|
export interface AssetCreate {
|
||||||
@@ -45,7 +46,7 @@ export interface AssetClaimResponse {
|
|||||||
|
|
||||||
export interface AssetAssign {
|
export interface AssetAssign {
|
||||||
asset_id: number;
|
asset_id: number;
|
||||||
heir_name: string;
|
heir_email: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
@@ -59,6 +60,7 @@ const MOCK_ASSETS: Asset[] = [
|
|||||||
author_id: MOCK_CONFIG.USER.id,
|
author_id: MOCK_CONFIG.USER.id,
|
||||||
private_key_shard: 'mock_shard_1',
|
private_key_shard: 'mock_shard_1',
|
||||||
content_outer_encrypted: 'mock_encrypted_content_1',
|
content_outer_encrypted: 'mock_encrypted_content_1',
|
||||||
|
heir_email: 'heir@example.com',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
@@ -217,7 +219,7 @@ export const assetsService = {
|
|||||||
logApiDebug('Assign Asset', 'Using mock mode');
|
logApiDebug('Assign Asset', 'Using mock mode');
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
resolve({ message: `Asset assigned to ${assignment.heir_name}` });
|
resolve({ message: `Asset assigned to ${assignment.heir_email}` });
|
||||||
}, MOCK_CONFIG.RESPONSE_DELAY);
|
}, MOCK_CONFIG.RESPONSE_DELAY);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ export interface VaultAsset {
|
|||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
updatedAt: Date;
|
updatedAt: Date;
|
||||||
isEncrypted: boolean;
|
isEncrypted: boolean;
|
||||||
|
heirEmail?: string;
|
||||||
|
rawData?: any; // For debug logging
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sentinel Types
|
// Sentinel Types
|
||||||
|
|||||||
@@ -14,8 +14,13 @@ export interface ApiAsset {
|
|||||||
id: number;
|
id: number;
|
||||||
title: string;
|
title: string;
|
||||||
type?: string;
|
type?: string;
|
||||||
|
author_id?: number;
|
||||||
|
private_key_shard?: string;
|
||||||
|
content_outer_encrypted?: string;
|
||||||
created_at?: string;
|
created_at?: string;
|
||||||
updated_at?: string;
|
updated_at?: string;
|
||||||
|
heir_id?: number;
|
||||||
|
heir_email?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
@@ -52,6 +57,8 @@ export function mapApiAssetToVaultAsset(api: ApiAsset): VaultAsset {
|
|||||||
createdAt: api.created_at ? new Date(api.created_at) : new Date(),
|
createdAt: api.created_at ? new Date(api.created_at) : new Date(),
|
||||||
updatedAt: api.updated_at ? new Date(api.updated_at) : new Date(),
|
updatedAt: api.updated_at ? new Date(api.updated_at) : new Date(),
|
||||||
isEncrypted: true,
|
isEncrypted: true,
|
||||||
|
heirEmail: api.heir_email,
|
||||||
|
rawData: api,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user