Files
com.blitzwallet/app/components/admin/homeComponents/accumulationAddresses/chainRow.js
T
cb506c8c11 Multi static deposit addresses (#1012)
* 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>
2026-07-20 13:36:39 -04:00

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,
},
});