Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
3d9dac2
ledger connection working in demo
B0Y3R-AVA Aug 5, 2025
f3dad38
iOS able to pull addresses from ledger and create a new wallet, also …
B0Y3R-AVA Aug 8, 2025
0af220f
connection and wallet creation working
B0Y3R-AVA Aug 10, 2025
26eccce
avax / solana txs working, can pull addresses from ledger, create led…
B0Y3R-AVA Aug 11, 2025
75d1580
ledger provider, modifications to wallet, ui now using hook
B0Y3R-AVA Aug 12, 2025
509e076
docs
B0Y3R-AVA Aug 12, 2025
414243c
linting
B0Y3R-AVA Aug 19, 2025
408daef
ledger live address derivation working, transactiosn on avalanche and…
B0Y3R-AVA Sep 11, 2025
ac3abc7
ledger live wallet creation / demo ui flow much better
B0Y3R-AVA Sep 14, 2025
d72db5b
small fix for import wallet screen
B0Y3R-AVA Sep 15, 2025
3f68dcf
linting fixes
B0Y3R-AVA Sep 17, 2025
061d6e5
cleanup
B0Y3R-AVA Sep 17, 2025
7b722bf
test fixes
B0Y3R-AVA Sep 17, 2025
19f63c5
removed unused .mdc file
B0Y3R-AVA Sep 17, 2025
65d6b59
moved steps into routes, modified ui for path selection and bluettoth…
B0Y3R-AVA Sep 19, 2025
bc63bb7
wrapped up design
B0Y3R-AVA Sep 24, 2025
0c02394
fixes for bluetooth connect flows
B0Y3R-AVA Nov 5, 2025
2d000e0
linting fixes
B0Y3R-AVA Nov 6, 2025
cc73e73
removed changes to settings.json
B0Y3R-AVA Nov 6, 2025
bc84302
typescript errors
B0Y3R-AVA Nov 6, 2025
7bca4aa
clean up
B0Y3R-AVA Nov 6, 2025
457c71e
more clean up
B0Y3R-AVA Nov 6, 2025
ea44159
fixed ledger service after rebase
B0Y3R-AVA Nov 6, 2025
a1fc068
fixed imports
B0Y3R-AVA Nov 6, 2025
4d4fc87
addressed pr comments
B0Y3R-AVA Nov 13, 2025
59b689e
working on Ledger app connection
B0Y3R-AVA Nov 12, 2025
31381dd
working on ledger app connections ui / solana address derivation bein…
B0Y3R-AVA Nov 18, 2025
0e60685
ui coming together, wallet name now reflecting ledger device name for…
B0Y3R-AVA Nov 19, 2025
4985c54
modified scrollScreen to have center header component conditionally r…
B0Y3R-AVA Nov 19, 2025
22e9aaf
working on ui, refactored complete screen and polished up ui and app …
B0Y3R-AVA Nov 20, 2025
8aee2b6
polishing up component ui, moving logic out of hooks / provider and i…
B0Y3R-AVA Nov 20, 2025
c543a19
more component cleanup and ui polish
B0Y3R-AVA Nov 21, 2025
e94553b
wrapping up
B0Y3R-AVA Nov 21, 2025
9ebf6aa
reverted un needed changes
B0Y3R-AVA Nov 21, 2025
d6789f6
linting fixes
B0Y3R-AVA Nov 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions packages/core-mobile/app/new/common/components/ProgressDots.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import React from 'react'
import { View } from 'react-native'
import { useTheme } from '@avalabs/k2-alpine'
import Animated, { useAnimatedStyle, withTiming } from 'react-native-reanimated'

interface ProgressDotsProps {
/** Total number of steps */
totalSteps: number
/** Current active step (0-indexed) */
currentStep: number
/** Size of each dot */
dotSize?: number
/** Gap between dots */
gap?: number
/** Test ID for testing */
testID?: string
}

interface DotProps {
isActive: boolean
dotSize: number
colors: {
$textPrimary: string
}
}

const AnimatedDot: React.FC<DotProps> = ({ isActive, dotSize, colors }) => {
const animatedStyle = useAnimatedStyle(() => {
return {
width: withTiming(isActive ? dotSize * 2 : dotSize, {
duration: 200
}),
opacity: withTiming(isActive ? 1 : 0.4, {
duration: 200
})
}
})

return (
<Animated.View
style={[
{
height: dotSize,
borderRadius: dotSize / 2,
backgroundColor: colors.$textPrimary
},
animatedStyle
]}
/>
)
}

export const ProgressDots: React.FC<ProgressDotsProps> = ({
totalSteps,
currentStep,
dotSize = 6,
gap = 6,
testID
}) => {
const {
theme: { colors }
} = useTheme()

return (
<View
testID={testID}
style={{
flexDirection: 'row',
alignItems: 'center',
gap,
height: 56,
justifyContent: 'center'
}}>
{Array.from({ length: totalSteps }).map((_, index) => (
<AnimatedDot
key={index}
isActive={index === currentStep}
dotSize={dotSize}
colors={colors}
/>
))}
</View>
)
}
63 changes: 60 additions & 3 deletions packages/core-mobile/app/new/common/components/ScrollScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,17 @@ import React, {
useCallback,
useEffect,
useLayoutEffect,
useMemo,
useRef,
useState
} from 'react'
import { LayoutRectangle, StyleProp, View, ViewStyle } from 'react-native'
import {
LayoutRectangle,
StyleProp,
View,
ViewStyle,
Platform
} from 'react-native'
import { ScrollView } from 'react-native-gesture-handler'
import {
KeyboardAwareScrollView,
Expand Down Expand Up @@ -66,6 +73,8 @@ interface ScrollScreenProps extends KeyboardAwareScrollViewProps {
disableStickyFooter?: boolean
/** Title to be displayed in the navigation header */
navigationTitle?: string
/** Custom component to render in the navigation header title area (replaces navigationTitle) */
renderNavigationHeader?: () => React.ReactNode
/** Custom header component to be rendered */
renderHeader?: () => React.ReactNode
/** Custom footer component to be rendered at the bottom of the screen */
Expand All @@ -80,6 +89,8 @@ interface ScrollScreenProps extends KeyboardAwareScrollViewProps {
headerStyle?: StyleProp<ViewStyle>
/** Whether this screen should hide the header background */
hideHeaderBackground?: boolean
/** Custom component to render centered in the header area, vertically level with back arrow */
renderHeaderCenterComponent?: () => React.ReactNode
/** TestID for the screen */
testID?: string
}
Expand All @@ -96,10 +107,12 @@ export const ScrollScreen = ({
hasParent,
isModal,
navigationTitle,
renderNavigationHeader,
shouldAvoidKeyboard,
disableStickyFooter,
showNavigationHeaderTitle = true,
hideHeaderBackground,
renderHeaderCenterComponent,
headerStyle,
testID,
renderHeader,
Expand All @@ -118,9 +131,16 @@ export const ScrollScreen = ({
const footerHeight = useSharedValue<number>(0)
const footerRef = useRef<View>(null)

const navigationHeader = useMemo(() => {
if (renderNavigationHeader) {
return renderNavigationHeader()
}
return <NavigationTitleHeader title={navigationTitle ?? title ?? ''} />
}, [renderNavigationHeader, navigationTitle, title])

const { onScroll, scrollY, targetHiddenProgress } = useFadingHeaderNavigation(
{
header: <NavigationTitleHeader title={navigationTitle ?? title ?? ''} />,
header: navigationHeader,
targetLayout: headerLayout,
shouldHeaderHaveGrabber: isModal,
hasParent,
Expand Down Expand Up @@ -173,7 +193,8 @@ export const ScrollScreen = ({
style={[
headerStyle,
{
gap: 8
gap: 8,
paddingHorizontal: 16
}
]}>
{title ? (
Expand Down Expand Up @@ -312,6 +333,40 @@ export const ScrollScreen = ({
)
}, [headerHeight, animatedBorderStyle])

const renderHeaderCenterOverlay = useCallback(() => {
if (!renderHeaderCenterComponent) {
return null
}

// try insetsbottom + 16 for android
// be sure to check 3 button bottom and manual test on android with native buttons on the bottom of the screen
const paddingValue =
Platform.OS === 'ios'
? isModal
? 15 // iOS modal - positive padding to move dots down
: 10 // iOS regular - positive padding
: isModal
? 50 // Android modal
: 45 // Android regular

return (
<View
pointerEvents="none"
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
height: headerHeight,
justifyContent: 'center',
alignItems: 'center',
paddingTop: paddingValue
}}>
{renderHeaderCenterComponent()}
</View>
)
}, [renderHeaderCenterComponent, headerHeight, isModal])

// 90% of our screens reuse this component but only some need keyboard avoiding
// If you have an input on the screen, you need to enable this prop
if (shouldAvoidKeyboard) {
Expand Down Expand Up @@ -345,6 +400,7 @@ export const ScrollScreen = ({

{renderFooterContent()}
{renderHeaderBackground()}
{renderHeaderCenterOverlay()}
</View>
)
}
Expand Down Expand Up @@ -374,6 +430,7 @@ export const ScrollScreen = ({

{renderFooterContent()}
{renderHeaderBackground()}
{renderHeaderCenterOverlay()}
</View>
)
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useMemo } from 'react'
import React, { useMemo, useCallback } from 'react'
import {
GroupList,
useTheme,
Expand Down Expand Up @@ -26,59 +26,64 @@ export const AccountAddresses = ({
} = useTheme()
const { networks } = useCombinedPrimaryNetworks()

const onCopyAddress = (value: string, message: string): void => {
const onCopyAddress = useCallback((value: string, message: string): void => {
copyToClipboard(value, message)
}
}, [])

const data = useMemo(() => {
return networks.map(network => {
const address = (() => {
switch (network.vmName) {
case NetworkVMType.AVM:
case NetworkVMType.PVM:
return account.addressPVM.replace(/^[XP]-/, '')
case NetworkVMType.BITCOIN:
return account.addressBTC
case NetworkVMType.EVM:
return account.addressC
case NetworkVMType.SVM:
return account.addressSVM
default:
return undefined
return networks
.map(network => {
const address = (() => {
switch (network.vmName) {
case NetworkVMType.AVM:
case NetworkVMType.PVM:
return account.addressPVM.replace(/^[XP]-/, '')
case NetworkVMType.BITCOIN:
return account.addressBTC
case NetworkVMType.EVM:
return account.addressC
case NetworkVMType.SVM:
return account.addressSVM
default:
return undefined
}
})()

// Only return data if we have a valid address
if (!address) {
return null
}
})()

return {
subtitle: address
? truncateAddress(address, TRUNCATE_ADDRESS_LENGTH)
: '',
title: network.chainName,
leftIcon: (
<NetworkLogoWithChain
network={network}
networkSize={36}
outerBorderColor={colors.$surfaceSecondary}
showChainLogo={isXPChain(network.chainId)}
/>
),
value: (
<CopyButton
testID={`copy_btn__${network.chainName}`}
onPress={() =>
address &&
onCopyAddress(address, `${network.chainName} address copied`)
}
/>
)
}
})
return {
subtitle: truncateAddress(address, TRUNCATE_ADDRESS_LENGTH),
title: network.chainName,
leftIcon: (
<NetworkLogoWithChain
network={network}
networkSize={36}
outerBorderColor={colors.$surfaceSecondary}
showChainLogo={isXPChain(network.chainId)}
/>
),
value: (
<CopyButton
testID={`copy_btn__${network.chainName}`}
onPress={() =>
onCopyAddress(address, `${network.chainName} address copied`)
}
/>
)
}
})
.filter((item): item is NonNullable<typeof item> => item !== null) // Type-safe filter
}, [
account.addressBTC,
account.addressC,
account.addressPVM,
account.addressSVM,
colors.$surfaceSecondary,
networks
networks,
onCopyAddress
])

return (
Expand Down
Loading
Loading