UI Imprivement for Vault and Flow

This commit is contained in:
2026-02-08 02:22:12 -04:00
parent 9f64bb32d0
commit d296a93c84
27 changed files with 4090 additions and 628 deletions

469
VAULT_USAGE_EXAMPLE.tsx Normal file
View File

@@ -0,0 +1,469 @@
/**
* VaultScreen 重构使用示例
*
* 这个文件展示了如何使用新创建的组件和 hooks 来简化 VaultScreen
*
* 使用方法:
* 1. 将这些代码片段复制到 VaultScreen.tsx 中替换对应的部分
* 2. 确保导入了所有必要的组件
*/
// ============================================
// 1. 导入新组件和 Hooks
// ============================================
// 在文件顶部添加这些导入
import { VaultButton, LabeledInput, AssetCard } from '@/components/vault';
import { useAddFlow, useMnemonicFlow } from '@/hooks/vault';
// ============================================
// 2. 使用 Hooks 管理状态
// ============================================
export default function VaultScreen() {
// 原来的代码:
// const [addStep, setAddStep] = useState(1);
// const [addMethod, setAddMethod] = useState<'text' | 'file' | 'scan'>('text');
// const [addVerified, setAddVerified] = useState(false);
// const [rehearsalConfirmed, setRehearsalConfirmed] = useState(false);
// const [selectedType, setSelectedType] = useState<VaultAssetType>('custom');
// const [newLabel, setNewLabel] = useState('');
// const [treasureContent, setTreasureContent] = useState('');
// const [accountProvider, setAccountProvider] = useState<'bank' | 'steam' | 'facebook' | 'custom'>('bank');
// 新代码:使用 useAddFlow hook
const addFlow = useAddFlow();
// 原来的代码:
// const [mnemonicWords, setMnemonicWords] = useState<string[]>([]);
// const [mnemonicParts, setMnemonicParts] = useState<string[][]>([]);
// const [mnemonicStep, setMnemonicStep] = useState<1 | 2 | 3 | 4 | 5>(1);
// const [heirStep, setHeirStep] = useState<'decision' | 'asset' | 'heir' | 'summary'>('decision');
// const [replaceIndex, setReplaceIndex] = useState<number | null>(null);
// const [replaceQuery, setReplaceQuery] = useState('');
// const [progressIndex, setProgressIndex] = useState(0);
// const [isCapturing, setIsCapturing] = useState(false);
// 新代码:使用 useMnemonicFlow hook
const mnemonicFlow = useMnemonicFlow();
// ... 其他状态保持不变
// ============================================
// 3. 更新 resetAddFlow 函数
// ============================================
const resetAddFlow = () => {
// 原来的代码:需要手动重置每个状态
// setAddStep(1);
// setAddMethod('text');
// setAddVerified(false);
// setRehearsalConfirmed(false);
// setSelectedType('custom');
// setNewLabel('');
// setAccountProvider('bank');
// 新代码:一行搞定
addFlow.reset();
};
// ============================================
// 4. 使用 AssetCard 组件渲染资产列表
// ============================================
// 原来的代码(在 return 语句中的资产列表部分,第 1089-1159 行):
/*
<ScrollView
style={styles.assetList}
showsVerticalScrollIndicator={false}
contentContainerStyle={styles.assetListContent}
>
{assets.map((asset, index) => {
const config = assetTypeConfig[asset.type];
if (!assetAnimations.current.has(asset.id)) {
const anim = new Animated.Value(0);
assetAnimations.current.set(asset.id, anim);
Animated.spring(anim, {
toValue: 1,
useNativeDriver: true,
tension: 65,
friction: 10,
delay: index * 80,
}).start();
}
const animValue = assetAnimations.current.get(asset.id) || new Animated.Value(1);
return (
<Animated.View
key={asset.id}
style={{
opacity: animValue,
transform: [
{
translateY: animValue.interpolate({
inputRange: [0, 1],
outputRange: [30, 0],
}),
},
{
scale: animValue.interpolate({
inputRange: [0, 1],
outputRange: [0.92, 1],
}),
},
],
}}
>
<TouchableOpacity
style={styles.assetCard}
activeOpacity={0.7}
onPress={() => handleOpenDetail(asset)}
>
<View style={styles.assetIconContainer}>
{renderAssetTypeIcon(config, 24, colors.vault.primary)}
</View>
<View style={styles.assetInfo}>
<Text style={styles.assetType}>{config.label}</Text>
<Text style={styles.assetLabel}>{asset.label}</Text>
<View style={styles.assetMetaRow}>
<Feather name="clock" size={11} color={colors.vault.textSecondary} />
<Text style={styles.assetMeta}>Sealed {formatDate(asset.createdAt)}</Text>
</View>
</View>
<View style={styles.encryptedBadge}>
<MaterialCommunityIcons name="lock" size={18} color="#fff" />
</View>
</TouchableOpacity>
</Animated.View>
);
})}
<View style={{ height: 100 }} />
</ScrollView>
*/
// 新代码:简洁清晰
const renderAssetList = () => (
<ScrollView
style={styles.assetList}
showsVerticalScrollIndicator={false}
contentContainerStyle={styles.assetListContent}
>
{assets.map((asset, index) => (
<AssetCard
key={asset.id}
asset={asset}
index={index}
onPress={handleOpenDetail}
/>
))}
<View style={{ height: 100 }} />
</ScrollView>
);
// ============================================
// 5. 使用 VaultButton 组件替换按钮
// ============================================
// 原来的代码(解锁按钮,第 1026-1041 行):
/*
<TouchableOpacity
style={styles.unlockButton}
onPress={handleUnlock}
activeOpacity={0.9}
>
<LinearGradient
colors={[colors.vault.primary, colors.vault.secondary]}
style={styles.unlockButtonGradient}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
>
<Ionicons
name="finger-print"
size={20}
color={colors.vault.background}
/>
<Text style={styles.unlockButtonText}>
{hasS0 ? 'Captain\'s Verification' : 'Enter Vault'}
</Text>
</LinearGradient>
</TouchableOpacity>
*/
// 新代码:
const renderUnlockButton = () => (
<VaultButton
variant="primary"
icon="finger-print"
onPress={handleUnlock}
style={styles.unlockButton}
>
{hasS0 ? "Captain's Verification" : "Enter Vault"}
</VaultButton>
);
// 原来的代码(添加按钮,第 1162-1180 行):
/*
<TouchableOpacity
style={styles.addButton}
onPress={() => {
resetAddFlow();
clearAddError();
setShowAddModal(true);
}}
activeOpacity={0.9}
>
<LinearGradient
colors={[colors.vault.primary, colors.vault.secondary]}
style={styles.addButtonGradient}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 0 }}
>
<FontAwesome5 name="plus" size={16} color={colors.vault.background} />
<Text style={styles.addButtonText}>Add Treasure</Text>
</LinearGradient>
</TouchableOpacity>
*/
// 新代码:
const renderAddButton = () => (
<VaultButton
variant="primary"
icon="plus"
onPress={() => {
resetAddFlow();
clearAddError();
setShowAddModal(true);
}}
style={styles.addButton}
>
Add Treasure
</VaultButton>
);
// ============================================
// 6. 使用 LabeledInput 组件替换输入框
// ============================================
// 在 Add Modal 中(第 1238-1245 行):
/*
<Text style={styles.modalLabel}>TREASURE TITLE</Text>
<TextInput
style={styles.input}
placeholder="e.g., Main wallet mnemonic"
placeholderTextColor={colors.nautical.sage}
value={newLabel}
onChangeText={setNewLabel}
/>
*/
// 新代码:
const renderTitleInput = () => (
<LabeledInput
label="TREASURE TITLE"
placeholder="e.g., Main wallet mnemonic"
value={addFlow.state.label}
onChangeText={addFlow.setLabel}
/>
);
// 在 Add Modal 内容步骤中(第 1305-1315 行):
/*
<Text style={styles.modalLabel}>CONTENT</Text>
<TextInput
style={[styles.input, styles.inputMultiline]}
placeholder="Enter content to seal (plaintext is encrypted locally before upload)"
placeholderTextColor={colors.nautical.sage}
value={treasureContent}
onChangeText={setTreasureContent}
multiline
numberOfLines={6}
textAlignVertical="top"
/>
*/
// 新代码:
const renderContentInput = () => (
<LabeledInput
label="CONTENT"
placeholder="Enter content to seal (plaintext is encrypted locally before upload)"
value={addFlow.state.content}
onChangeText={addFlow.setContent}
multiline
/>
);
// ============================================
// 7. 在 Modal 中使用 VaultButton
// ============================================
// 原来的模态框按钮代码(第 1428-1481 行):
/*
<View style={styles.modalButtons}>
<TouchableOpacity
style={styles.cancelButton}
onPress={() => {
if (addStep === 1) {
setShowAddModal(false);
setTreasureContent('');
clearAddError();
} else {
setAddStep(addStep - 1);
clearAddError();
}
}}
>
<Text style={styles.cancelButtonText}>
{addStep === 1 ? 'Cancel' : 'Back'}
</Text>
</TouchableOpacity>
{addStep < 3 ? (
<TouchableOpacity
style={styles.confirmButton}
onPress={() => setAddStep(addStep + 1)}
>
<LinearGradient
colors={[colors.nautical.teal, colors.nautical.seafoam]}
style={styles.confirmButtonGradient}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 0 }}
>
<Text style={styles.confirmButtonText}>Continue</Text>
</LinearGradient>
</TouchableOpacity>
) : (
<TouchableOpacity
style={styles.confirmButton}
onPress={handleAddAsset}
activeOpacity={canSeal ? 0.9 : 1}
disabled={!canSeal}
>
<LinearGradient
colors={[colors.nautical.teal, colors.nautical.seafoam]}
style={[
styles.confirmButtonGradient,
!canSeal && styles.confirmButtonGradientDisabled,
]}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 0 }}
>
<MaterialCommunityIcons name="lock" size={18} color="#fff" />
<Text style={styles.confirmButtonText}>{isSealing ? 'Sealing...' : 'Seal Treasure'}</Text>
</LinearGradient>
</TouchableOpacity>
)}
</View>
*/
// 新代码:
const renderModalButtons = () => {
const canSeal = addFlow.canProceed();
return (
<View style={styles.modalButtons}>
<VaultButton
variant="secondary"
onPress={() => {
if (addFlow.state.step === 1) {
setShowAddModal(false);
addFlow.reset();
clearAddError();
} else {
addFlow.setStep(addFlow.state.step - 1);
clearAddError();
}
}}
fullWidth
>
{addFlow.state.step === 1 ? 'Cancel' : 'Back'}
</VaultButton>
{addFlow.state.step < 3 ? (
<VaultButton
variant="primary"
onPress={() => addFlow.setStep(addFlow.state.step + 1)}
fullWidth
>
Continue
</VaultButton>
) : (
<VaultButton
variant="primary"
icon="lock"
loading={isSealing}
disabled={!canSeal}
onPress={handleAddAsset}
fullWidth
>
{isSealing ? 'Sealing...' : 'Seal Treasure'}
</VaultButton>
)}
</View>
);
};
// ============================================
// 8. 使用 Hook 访问状态的示例
// ============================================
// 原来访问状态的方式:
// if (addStep === 1) { ... }
// if (mnemonicStep === 3) { ... }
// setAddStep(2)
// setMnemonicWords(words)
// 新的访问方式:
// if (addFlow.state.step === 1) { ... }
// if (mnemonicFlow.state.step === 3) { ... }
// addFlow.setStep(2)
// mnemonicFlow.setWords(words)
return (
// ... 使用上面定义的渲染函数
);
}
// ============================================
// 9. 可以删除的代码
// ============================================
/*
重构后可以删除以下内容:
1. 大量的状态变量声明(第 111-167 行)
2. assetAnimations ref 和相关逻辑(第 171 行及使用处)
3. 资产卡片的动画代码(已移到 AssetCard 组件)
4. 所有重复的按钮样式定义
5. 所有重复的输入框样式定义
StyleSheet 中可以删除:
- unlockButton, unlockButtonGradient, unlockButtonText
- addButton, addButtonGradient, addButtonText
- assetCard, assetIconContainer, assetInfo, assetType, assetLabel, assetMetaRow, assetMeta, encryptedBadge
- 大部分 modal 相关的样式(已移到 modalStyles.ts
*/
// ============================================
// 10. 性能优化建议
// ============================================
/*
1. 使用 React.memo 包装 AssetCard 避免不必要的重渲染
2. 使用 useCallback 包装事件处理函数
3. 考虑使用 FlatList 替代 ScrollView如果资产列表很长
4. 延迟加载模态框组件React.lazy
示例:
const AssetList = React.memo(({ assets, onOpenDetail }) => (
assets.map((asset, index) => (
<AssetCard key={asset.id} asset={asset} index={index} onPress={onOpenDetail} />
))
));
const handleOpenDetail = useCallback((asset: VaultAsset) => {
setSelectedAsset(asset);
setShowDetail(true);
}, []);
*/