* Add accumulation address cap helper + pair key (Task 1) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Add forceNew + cap enforcement + addressesForOption to accumulation hook (Task 2) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Guard createAddress against re-entrant double-mint (Task 2 review fix) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Add i18n keys for multi accumulation addresses (Task 3) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Add accumulation address + option selector half-modals (Task 4) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Reuse getPairKey in option selector modal (Task 4 review fix) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Group accumulation home by option with per-option count (Task 5) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Manage multiple addresses + option switching in accumulation detail (Task 6) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Guard accumulation detail against externally-emptied option (Task 6 review fix) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Add create-new-address button + direct-address display to deposit QR view (Task 7) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Add address-selector list icon to deposit QR header (Task 8) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Cap-gate create wizard + always mint additional address (Task 9) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * remove manual create button in favor of automatic * fix select address page * fix opacity * improve details page * fixing translations * adding dislcimer to address details page * adding reindex scan * fixing background color * fixing camel case var name * minor correctness fixes --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
186 lines
4.8 KiB
JavaScript
186 lines
4.8 KiB
JavaScript
import { Image } from 'expo-image';
|
|
import { StyleSheet, TouchableOpacity, View } from 'react-native';
|
|
import Animated, {
|
|
useAnimatedStyle,
|
|
useSharedValue,
|
|
withTiming,
|
|
} from 'react-native-reanimated';
|
|
import { CENTER, ICONS } from '../../../../constants';
|
|
import { COLORS, HIDDEN_OPACITY } from '../../../../constants/theme';
|
|
import {
|
|
CHAIN_ASSET_ROW_HEIGHT,
|
|
CHAIN_EXPAND_PADDING,
|
|
} from '../../../../constants/accumulationAddresses';
|
|
import ThemeIcon from '../../../../functions/CustomElements/themeIcon';
|
|
import { ThemeText } from '../../../../functions/CustomElements';
|
|
import { useEffect } from 'react';
|
|
|
|
export default function ChainRow({
|
|
chain,
|
|
expanded,
|
|
disableExpand = false,
|
|
onToggleExpand,
|
|
onSelectAsset,
|
|
isAssetTaken,
|
|
onDisabledAssetPress,
|
|
theme,
|
|
darkModeType,
|
|
backgroundColor,
|
|
backgroundOffset,
|
|
}) {
|
|
const expandHeight = useSharedValue(0);
|
|
const chevronRotation = useSharedValue(0);
|
|
|
|
useEffect(() => {
|
|
expandHeight.value = withTiming(expanded ? 1 : 0, { duration: 200 });
|
|
chevronRotation.value = withTiming(expanded ? 1 : 0, { duration: 200 });
|
|
}, [expanded]);
|
|
|
|
const expandedStyle = useAnimatedStyle(() => ({
|
|
height:
|
|
expandHeight.value *
|
|
(chain.assets.length * CHAIN_ASSET_ROW_HEIGHT + CHAIN_EXPAND_PADDING),
|
|
opacity: expandHeight.value,
|
|
}));
|
|
|
|
const chevronStyle = useAnimatedStyle(() => ({
|
|
transform: [{ rotate: `${chevronRotation.value * 180}deg` }],
|
|
}));
|
|
|
|
const isSpark = chain.id === 'spark';
|
|
|
|
return (
|
|
<View>
|
|
<TouchableOpacity
|
|
activeOpacity={0.7}
|
|
style={styles.chainRow}
|
|
onPress={() => onToggleExpand(chain.id)}
|
|
>
|
|
<View
|
|
style={[
|
|
styles.chainIconContainer,
|
|
{
|
|
backgroundColor:
|
|
theme && darkModeType ? backgroundColor : backgroundOffset,
|
|
},
|
|
isSpark && {
|
|
backgroundColor:
|
|
theme && darkModeType ? backgroundColor : COLORS.primary,
|
|
},
|
|
]}
|
|
>
|
|
<Image
|
|
style={[
|
|
styles.assetIcon,
|
|
isSpark && {
|
|
width: 20,
|
|
height: 20,
|
|
},
|
|
]}
|
|
source={
|
|
ICONS[
|
|
isSpark
|
|
? 'sparkAsteriskWhite'
|
|
: `chain_${chain.label.toLowerCase()}`
|
|
]
|
|
}
|
|
contentFit="contain"
|
|
/>
|
|
</View>
|
|
<ThemeText styles={styles.optionLabel} content={chain.label} />
|
|
|
|
<Animated.View style={[chevronStyle]}>
|
|
<ThemeIcon
|
|
iconName={disableExpand ? 'ChevronRight' : 'ChevronDown'}
|
|
size={18}
|
|
/>
|
|
</Animated.View>
|
|
</TouchableOpacity>
|
|
|
|
<Animated.View style={[styles.assetOptionsContainer, expandedStyle]}>
|
|
{chain.assets.map(asset => {
|
|
const disabled = isAssetTaken(asset);
|
|
return (
|
|
<TouchableOpacity
|
|
key={asset}
|
|
activeOpacity={0.7}
|
|
style={[
|
|
styles.assetOptionRow,
|
|
{
|
|
backgroundColor:
|
|
theme && darkModeType ? backgroundColor : backgroundOffset,
|
|
opacity: disabled ? HIDDEN_OPACITY : 1,
|
|
},
|
|
]}
|
|
onPress={() =>
|
|
disabled ? onDisabledAssetPress() : onSelectAsset(chain, asset)
|
|
}
|
|
>
|
|
<View style={styles.assetOptionIconContainer}>
|
|
<Image
|
|
style={styles.assetOptionIcon}
|
|
source={ICONS[`${asset.toLowerCase()}Logo`]}
|
|
contentFit="contain"
|
|
/>
|
|
</View>
|
|
<ThemeText styles={styles.optionLabel} content={asset} />
|
|
</TouchableOpacity>
|
|
);
|
|
})}
|
|
</Animated.View>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
chainRow: {
|
|
width: '100%',
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
// paddingVertical: 8,
|
|
},
|
|
chainIconContainer: {
|
|
width: 48,
|
|
height: 48,
|
|
borderRadius: 22.5,
|
|
overflow: 'hidden',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
marginRight: 15,
|
|
},
|
|
optionLabel: {
|
|
flex: 1,
|
|
includeFontPadding: false,
|
|
},
|
|
assetIcon: {
|
|
width: 48,
|
|
height: 48,
|
|
},
|
|
assetOptionsContainer: {
|
|
overflow: 'hidden',
|
|
gap: 12,
|
|
paddingTop: 8,
|
|
paddingBottom: 8,
|
|
},
|
|
assetOptionRow: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
paddingHorizontal: 16,
|
|
paddingVertical: 14,
|
|
borderRadius: 12,
|
|
gap: 12,
|
|
},
|
|
assetOptionIconContainer: {
|
|
width: 35,
|
|
height: 35,
|
|
borderRadius: 17.5,
|
|
overflow: 'hidden',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
},
|
|
assetOptionIcon: {
|
|
width: 35,
|
|
height: 35,
|
|
},
|
|
});
|