Skip to content
This repository was archived by the owner on Feb 8, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 7 additions & 7 deletions src/components/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {
import { SvgProps } from 'react-native-svg';
import isEqual from 'lodash/isEqual';

import { Switch } from '../styles/components';
import {
BodyM,
BodyMSB,
Expand All @@ -21,6 +20,7 @@ import {
Caption,
} from '../styles/text';
import { ChevronRight, Checkmark } from '../styles/icons';
import Switch from '../components/Switch';
import DraggableList from '../screens/Settings/PaymentPreference/DraggableList';

const _SectionHeader = memo(
Expand Down Expand Up @@ -87,14 +87,14 @@ export type ItemData = SwitchItem | ButtonItem | TextButtonItem | DraggableItem;

export type SwitchItem = {
type: EItemType.switch;
enabled: boolean;
title: string;
Icon?: React.FC<SvgProps>;
iconColor?: string;
enabled?: boolean;
disabled?: boolean;
hide?: boolean;
onPress?: () => void;
testID?: string;
onPress?: () => void;
};

export type ButtonItem = {
Expand All @@ -107,10 +107,10 @@ export type ButtonItem = {
iconColor?: string;
disabled?: boolean;
enabled?: boolean;
loading?: boolean;
hide?: boolean;
onPress?: () => void;
testID?: string;
loading?: boolean;
onPress?: () => void;
};

export type TextButtonItem = {
Expand All @@ -122,17 +122,17 @@ export type TextButtonItem = {
iconColor?: string;
enabled?: boolean;
hide?: boolean;
onPress?: () => void;
testID?: string;
onPress?: () => void;
};

export type DraggableItem = {
type: EItemType.draggable;
value: TItemDraggable[];
title: string;
hide?: boolean;
onDragEnd?: (data: TItemDraggable[]) => void;
testID?: string;
onDragEnd?: (data: TItemDraggable[]) => void;
};

const _Item = memo((item: ItemData): ReactElement => {
Expand Down
114 changes: 114 additions & 0 deletions src/components/Switch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import React, { ReactElement } from 'react';
import { Pressable, StyleSheet } from 'react-native';
import Animated, {
Easing,
interpolate,
interpolateColor,
useAnimatedStyle,
useDerivedValue,
useSharedValue,
withTiming,
} from 'react-native-reanimated';

import colors from '../styles/colors';
import { IThemeColors } from '../styles/themes';

const duration = 300;
const defaultHeight = 32;
const defaultWidth = 52;

const Switch = ({
value,
disabled,
color,
onValueChange,
}: {
value: boolean;
disabled?: boolean;
color?: keyof IThemeColors;
onValueChange?: () => void;
}): ReactElement => {
const height = useSharedValue(defaultHeight);
const width = useSharedValue(defaultWidth);
const sharedValue = useDerivedValue(() => {
return value ? 1 : 0;
});

const thumbColor = disabled ? '#A0A0A0' : colors.white;
const trackColor = color ? colors[color] : colors.brand;
const trackColors = { on: trackColor, off: '#3A3A3C' };

const trackAnimatedStyle = useAnimatedStyle(() => {
const animatedColor = interpolateColor(
sharedValue.value,
[0, 1],
[trackColors.off, trackColors.on],
);
const colorValue = withTiming(animatedColor, {
duration,
easing: Easing.inOut(Easing.ease),
});

return {
backgroundColor: colorValue,
borderRadius: height.value / 2,
};
});

const thumbAnimatedStyle = useAnimatedStyle(() => {
const moveValue = interpolate(
sharedValue.value,
[0, 1],
[0, width.value - height.value],
);
const translateValue = withTiming(moveValue, {
duration,
easing: Easing.bezier(0.61, 0.46, 0.3, 1.07),
});

return {
transform: [{ translateX: translateValue }],
borderRadius: height.value / 2,
};
});

const onPress = (): void => {
if (!disabled) {
onValueChange?.();
}
};

return (
<Pressable onPress={onPress}>
<Animated.View
style={[styles.track, trackAnimatedStyle]}
onLayout={(e) => {
height.value = e.nativeEvent.layout.height;
width.value = e.nativeEvent.layout.width;
}}>
<Animated.View
style={[
styles.thumb,
thumbAnimatedStyle,
{ backgroundColor: thumbColor },
]}
/>
</Animated.View>
</Pressable>
);
};

const styles = StyleSheet.create({
track: {
alignItems: 'flex-start',
height: defaultHeight,
width: defaultWidth,
padding: 4,
},
thumb: {
height: '100%',
aspectRatio: 1,
},
});

export default Switch;
2 changes: 1 addition & 1 deletion src/components/SwitchRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import {
View,
ViewStyle,
} from 'react-native';
import { Switch } from '../styles/components';
import { IThemeColors } from '../styles/themes';
import Switch from '../components/Switch';

const SwitchRow = ({
children,
Expand Down
2 changes: 1 addition & 1 deletion src/screens/Settings/PIN/AskForBiometrics.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ import {
} from 'react-native';
import { useTranslation } from 'react-i18next';

import { Switch } from '../../../styles/components';
import { BodyMSB, BodyM } from '../../../styles/text';
import { FaceIdIcon, TouchIdIcon } from '../../../styles/icons';
import BottomSheetNavigationHeader from '../../../components/BottomSheetNavigationHeader';
import SafeAreaInset from '../../../components/SafeAreaInset';
import GradientView from '../../../components/GradientView';
import Button from '../../../components/buttons/Button';
import Switch from '../../../components/Switch';
import { IsSensorAvailableResult } from '../../../components/Biometrics';
import { useAppDispatch } from '../../../hooks/redux';
import { useBottomSheetScreenBackPress } from '../../../hooks/bottomSheet';
Expand Down
2 changes: 1 addition & 1 deletion src/screens/Settings/PIN/Result.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import React, { memo, ReactElement, useMemo } from 'react';
import { StyleSheet, View, Pressable, Image } from 'react-native';
import { useTranslation } from 'react-i18next';

import { Switch } from '../../../styles/components';
import { BodyM, BodyMSB } from '../../../styles/text';
import BottomSheetNavigationHeader from '../../../components/BottomSheetNavigationHeader';
import SafeAreaInset from '../../../components/SafeAreaInset';
import GradientView from '../../../components/GradientView';
import Button from '../../../components/buttons/Button';
import Switch from '../../../components/Switch';
import { useAppDispatch, useAppSelector } from '../../../hooks/redux';
import { useBottomSheetScreenBackPress } from '../../../hooks/bottomSheet';
import { closeSheet } from '../../../store/slices/ui';
Expand Down
3 changes: 2 additions & 1 deletion src/screens/Wallets/Send/CoinSelection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ import { StyleSheet, View } from 'react-native';
import { BottomSheetScrollView } from '@gorhom/bottom-sheet';
import { useTranslation } from 'react-i18next';

import { ScrollView, Switch } from '../../../styles/components';
import { ScrollView } from '../../../styles/components';
import { Subtitle, BodyMSB, BodySSB, Caption13Up } from '../../../styles/text';
import GradientView from '../../../components/GradientView';
import BottomSheetNavigationHeader from '../../../components/BottomSheetNavigationHeader';
import SafeAreaInset from '../../../components/SafeAreaInset';
import Button from '../../../components/buttons/Button';
import Switch from '../../../components/Switch';
import Tag from '../../../components/Tag';

import useColors from '../../../hooks/colors';
Expand Down
16 changes: 0 additions & 16 deletions src/styles/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import {
ColorValue,
Platform,
PressableProps,
Switch as RNSwitch,
ScrollViewProps,
TouchableOpacity as RNTouchableOpacity,
TouchableHighlight as RNTouchableHighlight,
Expand All @@ -12,7 +11,6 @@ import {
ViewProps,
TextInput as RNTextInput,
TextInputProps as RNTextInputProps,
SwitchProps,
} from 'react-native';
import Color from 'color';
import Animated, { AnimatedProps } from 'react-native-reanimated';
Expand Down Expand Up @@ -126,20 +124,6 @@ export const Pressable = styled(RNPressable)<PressableProps & ColorProps>(
}),
);

export const Switch = styled(RNSwitch).attrs<SwitchProps & ColorProps>(
(props) => ({
trackColor: {
false: '#3A3A3C',
true: props.color
? props.theme.colors[props.color]
: props.theme.colors.brand,
},
thumbColor: props.disabled ? '#A0A0A0' : 'white',
ios_backgroundColor: '#3A3A3C',
...props,
}),
)<SwitchProps & ColorProps>(() => ({}));

export const TextInput = styled(RNTextInput).attrs<TextInputProps>((props) => ({
keyboardAppearance: props.theme.id,
selectionColor: colors.brand,
Expand Down
Loading