Skip to content

Commit c062477

Browse files
committed
feature: unstake sheet complete design
1 parent 4f74a37 commit c062477

File tree

11 files changed

+538
-216
lines changed

11 files changed

+538
-216
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { memo } from 'react';
2+
import { Cover, useColorMode } from '@/design-system';
3+
import { IS_IOS } from '@/env';
4+
import chroma from 'chroma-js';
5+
import Animated, { interpolate, useAnimatedStyle, type SharedValue } from 'react-native-reanimated';
6+
7+
type HoldToActivateProgressProps = {
8+
holdProgress: SharedValue<number>;
9+
color: string;
10+
};
11+
12+
export const HoldToActivateProgress = memo(function HoldToActivateProgress({ holdProgress, color }: HoldToActivateProgressProps) {
13+
const { isDarkMode } = useColorMode();
14+
15+
const brightenedColor = chroma(color)
16+
.saturate(isDarkMode ? 0.15 : 0.1)
17+
.brighten(isDarkMode ? 0.5 : 0.3)
18+
.css();
19+
20+
const holdProgressStyle = useAnimatedStyle(() => ({
21+
opacity: interpolate(holdProgress.value, [0, 4, 20, 96, 100], [0, 0, 1, 1, 0], 'clamp'),
22+
width: `${holdProgress.value ?? 0}%`,
23+
}));
24+
25+
return (
26+
<Cover style={{ borderRadius: 100, overflow: 'hidden' }}>
27+
<Animated.View
28+
style={[
29+
holdProgressStyle,
30+
{
31+
backgroundColor: brightenedColor,
32+
height: '100%',
33+
...(IS_IOS
34+
? {
35+
shadowColor: brightenedColor,
36+
shadowOffset: { width: 12, height: 0 },
37+
shadowOpacity: 1,
38+
shadowRadius: 6,
39+
}
40+
: {}),
41+
},
42+
]}
43+
/>
44+
</Cover>
45+
);
46+
});
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { useCallback } from 'react';
2+
import { Easing, type SharedValue, useSharedValue, withSpring, withTiming } from 'react-native-reanimated';
3+
import { triggerHaptics } from 'react-native-turbo-haptics';
4+
import { SPRING_CONFIGS } from '@/components/animations/animationConfigs';
5+
import { LONG_PRESS_DURATION_IN_MS } from '@/components/buttons/hold-to-authorize/constants';
6+
7+
type UseHoldToActivateOptions = {
8+
onActivate: () => void;
9+
duration?: number;
10+
disabled?: boolean;
11+
};
12+
13+
type HoldToActivateGestureProps = {
14+
longPressDuration: number;
15+
disabled: boolean;
16+
onPressStartWorklet: () => void;
17+
onLongPressWorklet: () => void;
18+
onLongPressEndWorklet: (success?: boolean) => void;
19+
onLongPressJS: () => void;
20+
};
21+
22+
type UseHoldToActivateResult = {
23+
holdProgress: SharedValue<number>;
24+
gestureHandlerProps: HoldToActivateGestureProps;
25+
};
26+
27+
export function useHoldToActivate({
28+
onActivate,
29+
duration = LONG_PRESS_DURATION_IN_MS,
30+
disabled = false,
31+
}: UseHoldToActivateOptions): UseHoldToActivateResult {
32+
const holdProgress = useSharedValue(0);
33+
34+
const onPressStartWorklet = useCallback(() => {
35+
'worklet';
36+
holdProgress.value = 0;
37+
holdProgress.value = withTiming(100, { duration, easing: Easing.inOut(Easing.sin) }, isFinished => {
38+
if (isFinished) {
39+
holdProgress.value = 0;
40+
}
41+
});
42+
}, [duration, holdProgress]);
43+
44+
const onLongPressWorklet = useCallback(() => {
45+
'worklet';
46+
triggerHaptics('notificationSuccess');
47+
}, []);
48+
49+
const onLongPressEndWorklet = useCallback(
50+
(success?: boolean) => {
51+
'worklet';
52+
if (!success) {
53+
holdProgress.value = withSpring(0, SPRING_CONFIGS.slowSpring);
54+
}
55+
},
56+
[holdProgress]
57+
);
58+
59+
return {
60+
holdProgress,
61+
gestureHandlerProps: {
62+
longPressDuration: duration,
63+
disabled,
64+
onPressStartWorklet,
65+
onLongPressWorklet,
66+
onLongPressEndWorklet,
67+
onLongPressJS: onActivate,
68+
},
69+
};
70+
}

src/features/rnbw-airdrop/screens/rnbw-airdrop-screen/components/RnbwHeroCoin.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { type RnbwAirdropScene, RnbwAirdropScenes } from '@/features/rnbw-airdro
1616
import { DEVICE_WIDTH } from '@/utils/deviceUtils';
1717
import { time } from '@/utils/time';
1818
import { transitionEasing } from '@/features/rnbw-rewards/animations/sceneTransitions';
19-
import { LoadingSpinner } from '@/features/rnbw-rewards/screens/rnbw-rewards-screen/components/LoadingSpinner';
19+
import { LoadingSpinner } from '@/framework/ui/components/LoadingSpinner';
2020
import concentricCircleImage from '@/features/rnbw-rewards/assets/radial-circle.png';
2121
import { SpinnableCoin, type SpinnableCoinHandle } from '@/features/rnbw-rewards/screens/rnbw-rewards-screen/components/SpinnableCoin';
2222
import { opacity } from '@/framework/ui/utils/opacity';
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
import React, { memo, useMemo } from 'react';
2+
import { LinearGradient } from 'expo-linear-gradient';
3+
import { StyleSheet, type StyleProp, type ViewStyle } from 'react-native';
4+
import { Text, useColorMode, type TextProps } from '@/design-system';
5+
import { GradientText } from '@/components/text';
6+
import { GradientBorderView } from '@/components/gradient-border/GradientBorderView';
7+
import { THICK_BORDER_WIDTH } from '@/styles/constants';
8+
import { InnerShadow } from '@/features/polymarket/components/InnerShadow';
9+
import { opacity } from '@/framework/ui/utils/opacity';
10+
import { getValueForColorMode } from '@/design-system/color/palettes';
11+
12+
type ShadowStyle = Pick<ViewStyle, 'shadowColor' | 'shadowOffset' | 'shadowOpacity' | 'shadowRadius'>;
13+
14+
export const RNBW_BUTTON_CONFIG = {
15+
gradient: {
16+
start: { x: 0, y: 0 },
17+
end: { x: 0, y: 1 },
18+
},
19+
primary: {
20+
colors: ['#FFE380', '#E3A700'],
21+
border: {
22+
light: [opacity('#B5910D', 0.1125), opacity('#B5910D', 0.15)],
23+
dark: [opacity('#B5910D', 0.1125), opacity('#B5910D', 0.15)],
24+
},
25+
text: {
26+
colors: ['#4F3605', '#90630E'],
27+
start: { x: 0, y: 0 },
28+
end: { x: 0, y: 1 },
29+
shadow: {
30+
textShadowOffset: { width: 1, height: 1 },
31+
textShadowRadius: 0,
32+
textShadowColor: 'rgba(255, 255, 255, 0.26)',
33+
},
34+
},
35+
shadows: {
36+
light: [
37+
{ shadowColor: '#8A6216', shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.04, shadowRadius: 10 },
38+
{ shadowColor: '#000000', shadowOffset: { width: 0, height: 3 }, shadowOpacity: 0.08, shadowRadius: 2.5 },
39+
] as ShadowStyle[],
40+
dark: [{ shadowColor: '#FFDD20', shadowOffset: { width: 0, height: 0 }, shadowOpacity: 0.3, shadowRadius: 15 }] as ShadowStyle[],
41+
},
42+
},
43+
secondary: {
44+
colors: {
45+
light: [opacity('#F8DE7D', 0.4), opacity('#D9B550', 0.4)],
46+
dark: [opacity('#FFE380', 0.1), opacity('#E3A700', 0.1)],
47+
},
48+
border: {
49+
light: [opacity('#B5910D', 0.08), opacity('#B5910D', 0.08)],
50+
dark: [opacity('#B5910D', 0.049), opacity('#B5910D', 0.07)],
51+
},
52+
text: {
53+
color: { light: '#6F4D0A', dark: '#FAD96B' },
54+
},
55+
shadows: {
56+
light: [
57+
{ shadowColor: '#8A6216', shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.04, shadowRadius: 10 },
58+
{ shadowColor: '#000000', shadowOffset: { width: 0, height: 3 }, shadowOpacity: 0.08, shadowRadius: 2.5 },
59+
] as ShadowStyle[],
60+
dark: [{ shadowColor: '#F8DE7D', shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.04, shadowRadius: 10 }] as ShadowStyle[],
61+
},
62+
},
63+
} as const;
64+
65+
type RnbwButtonSurfaceProps = {
66+
variant?: 'primary' | 'secondary';
67+
height?: number;
68+
children: React.ReactNode;
69+
style?: StyleProp<ViewStyle>;
70+
};
71+
72+
export const RnbwButtonSurface = memo(function RnbwButtonSurface({
73+
variant = 'primary',
74+
height = 42,
75+
children,
76+
style,
77+
}: RnbwButtonSurfaceProps) {
78+
const { isDarkMode, colorMode } = useColorMode();
79+
const borderRadius = height / 2;
80+
81+
const { borderGradientColors, gradientColors, shadows } = useMemo(
82+
() => ({
83+
borderGradientColors: getValueForColorMode(RNBW_BUTTON_CONFIG[variant].border, colorMode),
84+
gradientColors: getValueForColorMode(RNBW_BUTTON_CONFIG[variant].colors, colorMode),
85+
shadows: getValueForColorMode(RNBW_BUTTON_CONFIG[variant].shadows, colorMode),
86+
}),
87+
[variant, colorMode]
88+
);
89+
90+
const resolvedContainerStyle = useMemo(
91+
() =>
92+
[
93+
{
94+
height,
95+
borderRadius,
96+
justifyContent: 'center',
97+
alignItems: 'center',
98+
overflow: 'visible',
99+
},
100+
shadows,
101+
style,
102+
] as StyleProp<ViewStyle>,
103+
[height, borderRadius, style, shadows]
104+
);
105+
106+
return (
107+
<GradientBorderView
108+
borderWidth={isDarkMode ? THICK_BORDER_WIDTH : 1}
109+
borderGradientColors={borderGradientColors}
110+
start={RNBW_BUTTON_CONFIG.gradient.start}
111+
end={RNBW_BUTTON_CONFIG.gradient.end}
112+
style={resolvedContainerStyle}
113+
>
114+
<LinearGradient
115+
colors={gradientColors}
116+
start={RNBW_BUTTON_CONFIG.gradient.start}
117+
end={RNBW_BUTTON_CONFIG.gradient.end}
118+
style={[StyleSheet.absoluteFill, { borderRadius }]}
119+
/>
120+
{variant === 'primary' && (
121+
<LinearGradient
122+
colors={[opacity('#FFE67E', 0), opacity('#FFE67E', 0.5), opacity('#FFE67E', 0)]}
123+
start={{ x: 0.25, y: 0.5 }}
124+
end={{ x: 0.75, y: 0.5 }}
125+
style={StyleSheet.absoluteFill}
126+
/>
127+
)}
128+
<InnerShadow color={'rgba(255, 255, 255, 0.1)'} blur={1} dx={0} dy={3} borderRadius={borderRadius} />
129+
{children}
130+
</GradientBorderView>
131+
);
132+
});
133+
134+
export const RnbwButtonText = memo(function RnbwButtonText({
135+
variant = 'primary',
136+
size = '20pt',
137+
weight = 'heavy',
138+
children,
139+
}: {
140+
variant?: 'primary' | 'secondary';
141+
size?: TextProps['size'];
142+
weight?: TextProps['weight'];
143+
children: string;
144+
}) {
145+
const { colorMode } = useColorMode();
146+
147+
if (variant === 'primary') {
148+
return (
149+
<GradientText
150+
colors={RNBW_BUTTON_CONFIG.primary.text.colors}
151+
start={RNBW_BUTTON_CONFIG.primary.text.start}
152+
end={RNBW_BUTTON_CONFIG.primary.text.end}
153+
shadow={RNBW_BUTTON_CONFIG.primary.text.shadow}
154+
>
155+
<Text size={size} weight={weight} color="label">
156+
{children}
157+
</Text>
158+
</GradientText>
159+
);
160+
}
161+
162+
return (
163+
<Text size={size} weight={weight} color={{ custom: getValueForColorMode(RNBW_BUTTON_CONFIG.secondary.text.color, colorMode) }}>
164+
{children}
165+
</Text>
166+
);
167+
});

0 commit comments

Comments
 (0)