added_reveal_secret_and_delete_treasure
This commit is contained in:
@@ -51,6 +51,7 @@ export const API_ENDPOINTS = {
|
||||
CREATE: '/assets/create',
|
||||
CLAIM: '/assets/claim',
|
||||
ASSIGN: '/assets/assign',
|
||||
DELETE: '/assets/delete',
|
||||
},
|
||||
|
||||
// AI Services
|
||||
|
||||
@@ -11,6 +11,7 @@ import { useAuth } from '../context/AuthContext';
|
||||
import { assetsService } from '../services/assets.service';
|
||||
import { getVaultStorageKeys, DEBUG_MODE } from '../config';
|
||||
import { SentinelVault } from '../utils/crypto_core';
|
||||
import { storageService } from '../services/storage.service';
|
||||
import {
|
||||
initialVaultAssets,
|
||||
mapApiAssetsToVaultAssets,
|
||||
@@ -37,6 +38,8 @@ export interface UseVaultAssetsReturn {
|
||||
refreshAssets: () => Promise<void>;
|
||||
/** Create asset via POST /assets/create; on success refreshes list */
|
||||
createAsset: (params: { title: string; content: string }) => Promise<CreateAssetResult>;
|
||||
/** Delete asset via POST /assets/delete; on success refreshes list */
|
||||
deleteAsset: (assetId: number) => Promise<CreateAssetResult>;
|
||||
/** True while create request is in flight */
|
||||
isSealing: boolean;
|
||||
/** Error message from last create failure (non-401) */
|
||||
@@ -129,7 +132,7 @@ export function useVaultAssets(isUnlocked: boolean): UseVaultAssetsReturn {
|
||||
console.log(' Encrypted: ', content_inner_encrypted);
|
||||
}
|
||||
|
||||
await assetsService.createAsset(
|
||||
const createdAsset = await assetsService.createAsset(
|
||||
{
|
||||
title: title.trim(),
|
||||
private_key_shard: s1Str,
|
||||
@@ -137,6 +140,11 @@ export function useVaultAssets(isUnlocked: boolean): UseVaultAssetsReturn {
|
||||
},
|
||||
token
|
||||
);
|
||||
|
||||
// Backup plaintext content locally
|
||||
if (createdAsset && createdAsset.id && user?.id) {
|
||||
await storageService.saveAssetBackup(createdAsset.id, content, user.id);
|
||||
}
|
||||
await refreshAssets();
|
||||
return { success: true };
|
||||
} catch (err: unknown) {
|
||||
@@ -166,6 +174,44 @@ export function useVaultAssets(isUnlocked: boolean): UseVaultAssetsReturn {
|
||||
[token, user, refreshAssets, signOut]
|
||||
);
|
||||
|
||||
const deleteAsset = useCallback(
|
||||
async (assetId: number): Promise<CreateAssetResult> => {
|
||||
if (!token) {
|
||||
return { success: false, error: 'Not logged in.' };
|
||||
}
|
||||
setIsSealing(true);
|
||||
setCreateError(null);
|
||||
try {
|
||||
await assetsService.deleteAsset(assetId, 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 delete.');
|
||||
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, refreshAssets, signOut]
|
||||
);
|
||||
|
||||
const clearCreateError = useCallback(() => setCreateError(null), []);
|
||||
|
||||
return {
|
||||
@@ -173,6 +219,7 @@ export function useVaultAssets(isUnlocked: boolean): UseVaultAssetsReturn {
|
||||
setAssets,
|
||||
refreshAssets,
|
||||
createAsset,
|
||||
deleteAsset,
|
||||
isSealing,
|
||||
createError,
|
||||
clearCreateError,
|
||||
|
||||
@@ -542,12 +542,17 @@ export default function FlowScreen() {
|
||||
const encryptedSummary = vault.encryptData(aesKey, generatedSummary).toString('hex');
|
||||
|
||||
// Create asset in backend
|
||||
await assetsService.createAsset({
|
||||
const createdAsset = await assetsService.createAsset({
|
||||
title: `Chat Summary - ${new Date().toLocaleDateString()}`,
|
||||
private_key_shard: shareServer,
|
||||
content_inner_encrypted: encryptedSummary,
|
||||
}, token);
|
||||
|
||||
// Backup plaintext content locally
|
||||
if (createdAsset && createdAsset.id && user?.id) {
|
||||
await storageService.saveAssetBackup(createdAsset.id, generatedSummary, user.id);
|
||||
}
|
||||
|
||||
setSaveResult({ success: true, message: 'Summary encrypted and saved to your vault successfully.' });
|
||||
setShowSaveResultModal(true);
|
||||
} catch (error) {
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -245,4 +245,44 @@ export const assetsService = {
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Delete an asset
|
||||
* @param assetId - ID of the asset to delete
|
||||
* @param token - JWT token for authentication
|
||||
* @returns Success message
|
||||
*/
|
||||
async deleteAsset(assetId: number, token: string): Promise<{ message: string }> {
|
||||
if (NO_BACKEND_MODE) {
|
||||
logApiDebug('Delete Asset', `Using mock mode for ID: ${assetId}`);
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve({ message: 'Asset deleted successfully' });
|
||||
}, MOCK_CONFIG.RESPONSE_DELAY);
|
||||
});
|
||||
}
|
||||
|
||||
const url = buildApiUrl(API_ENDPOINTS.ASSETS.DELETE);
|
||||
logApiDebug('Delete Asset URL', url);
|
||||
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: getApiHeaders(token),
|
||||
body: JSON.stringify({ asset_id: assetId }),
|
||||
});
|
||||
|
||||
logApiDebug('Delete Asset Response Status', response.status);
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(errorData.detail || 'Failed to delete asset');
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
console.error('Delete asset error:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
@@ -14,6 +14,7 @@ import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
const STORAGE_KEYS = {
|
||||
CHAT_HISTORY: '@sentinel:chat_history',
|
||||
CURRENT_MESSAGES: '@sentinel:current_messages',
|
||||
ASSET_BACKUP: '@sentinel:asset_backup',
|
||||
} as const;
|
||||
|
||||
// =============================================================================
|
||||
@@ -115,6 +116,32 @@ export const storageService = {
|
||||
} catch (e) {
|
||||
console.error('Error clearing storage data:', e);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Save the plaintext backup of an asset locally
|
||||
*/
|
||||
async saveAssetBackup(assetId: number, content: string, userId: string | number): Promise<void> {
|
||||
try {
|
||||
const key = `${this.getUserKey(STORAGE_KEYS.ASSET_BACKUP, userId)}:${assetId}`;
|
||||
await AsyncStorage.setItem(key, content);
|
||||
console.log(`[Storage] Saved asset backup for user ${userId}, asset ${assetId}`);
|
||||
} catch (e) {
|
||||
console.error(`Error saving asset backup for asset ${assetId}:`, e);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve the plaintext backup of an asset locally
|
||||
*/
|
||||
async getAssetBackup(assetId: number, userId: string | number): Promise<string | null> {
|
||||
try {
|
||||
const key = `${this.getUserKey(STORAGE_KEYS.ASSET_BACKUP, userId)}:${assetId}`;
|
||||
return await AsyncStorage.getItem(key);
|
||||
} catch (e) {
|
||||
console.error(`Error getting asset backup for asset ${assetId}:`, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ export const colors = {
|
||||
// Base colors
|
||||
white: '#FFFFFF',
|
||||
black: '#1A2F3A',
|
||||
|
||||
|
||||
// Nautical palette
|
||||
nautical: {
|
||||
deepTeal: '#1B4D5C',
|
||||
@@ -21,7 +21,7 @@ export const colors = {
|
||||
navy: '#1A3A4A',
|
||||
sage: '#8CA5A5',
|
||||
},
|
||||
|
||||
|
||||
// Flow - Captain's Journal
|
||||
flow: {
|
||||
background: '#E8F6F8',
|
||||
@@ -38,7 +38,7 @@ export const colors = {
|
||||
archivedText: '#7A9A9A',
|
||||
highlight: '#B8E0E5',
|
||||
},
|
||||
|
||||
|
||||
// Vault - Ship's Vault
|
||||
vault: {
|
||||
background: '#1B4D5C',
|
||||
@@ -54,7 +54,7 @@ export const colors = {
|
||||
warning: '#E57373',
|
||||
success: '#6BBF8A',
|
||||
},
|
||||
|
||||
|
||||
// Sentinel - Lighthouse Watch
|
||||
sentinel: {
|
||||
background: '#1A3A4A',
|
||||
@@ -70,7 +70,7 @@ export const colors = {
|
||||
statusWarning: '#E5B873',
|
||||
statusCritical: '#E57373',
|
||||
},
|
||||
|
||||
|
||||
// Heritage - Legacy Fleet
|
||||
heritage: {
|
||||
background: '#E8F6F8',
|
||||
@@ -86,7 +86,7 @@ export const colors = {
|
||||
confirmed: '#6BBF8A',
|
||||
pending: '#E5B873',
|
||||
},
|
||||
|
||||
|
||||
// Me - Captain's Quarters
|
||||
me: {
|
||||
background: '#E8F6F8',
|
||||
|
||||
@@ -31,6 +31,8 @@ export const VAULT_ASSET_TYPES: VaultAssetType[] = [
|
||||
'custom',
|
||||
];
|
||||
|
||||
export const initialVaultAssets: VaultAsset[] = [];
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Mapping
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -60,41 +62,3 @@ export function mapApiAssetsToVaultAssets(apiList: ApiAsset[]): VaultAsset[] {
|
||||
return apiList.map(mapApiAssetToVaultAsset);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Mock / initial data (fallback when API is unavailable)
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
export const initialVaultAssets: 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,
|
||||
},
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user