Skip to content

Commit 6cdc2ad

Browse files
committed
Refactor App and CircleTime components: Replace CircularDraggableSlider with CircleTime, update selectedDuration logic, and enhance TimeRange to directly modify selectedDuration
1 parent 1eaf112 commit 6cdc2ad

File tree

4 files changed

+163
-197
lines changed

4 files changed

+163
-197
lines changed

App.tsx

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,17 @@
11
import { useFonts } from "expo-font";
2-
import { useRef } from 'react';
32
import { StyleSheet, View } from 'react-native';
43
import { GestureHandlerRootView } from 'react-native-gesture-handler';
54
import { useSharedValue } from 'react-native-reanimated';
65
import { SafeAreaProvider } from 'react-native-safe-area-context';
76
import { BottomTab } from './src/components/bottom-tab';
8-
import { CircularDraggableSlider, CircularDraggableSliderRefType } from './src/components/circle-time';
7+
import { CircleTime } from './src/components/circle-time';
98
import { Footer } from "./src/components/footer";
109
import { Header } from "./src/components/header";
1110
import { Queued } from "./src/components/queued";
1211
import { TimeRange } from './src/components/time-range';
1312

14-
const LinesAmount = 200;
15-
1613
export default function App() {
17-
18-
const circularSliderRef = useRef<CircularDraggableSliderRefType>(null);
19-
const selectedDuration = useSharedValue(1);
14+
const selectedDuration = useSharedValue(5);
2015
return (
2116
<SafeAreaProvider>
2217
<FontsProvider>
@@ -25,20 +20,9 @@ export default function App() {
2520
<Header />
2621
<Queued />
2722
<View style={styles.container}>
28-
<CircularDraggableSlider
29-
ref={circularSliderRef}
30-
bigLineIndexOffset={10}
31-
linesAmount={LinesAmount}
32-
maxLineHeight={20}
33-
minLineHeight={8}
34-
selectedDuration={selectedDuration}
35-
/>
36-
<TimeRange
37-
onDurationChange={durationHours => {
38-
'worklet';
39-
selectedDuration.value = durationHours;
40-
}}
23+
<CircleTime selectedDuration={selectedDuration}
4124
/>
25+
<TimeRange selectedDuration={selectedDuration} />
4226
</View>
4327
<Footer />
4428
<BottomTab />
@@ -88,5 +72,5 @@ const styles = StyleSheet.create({
8872
alignItems: 'center',
8973
flex: 1,
9074
},
91-
75+
9276
});

src/components/circle-time/index.tsx

Lines changed: 148 additions & 164 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Dimensions, StyleSheet, Text, View } from 'react-native';
22

3-
import { forwardRef } from 'react';
3+
import { useMemo } from 'react';
44

55
import { LinearGradient } from 'expo-linear-gradient';
66
import type { SharedValue } from 'react-native-reanimated';
@@ -17,188 +17,172 @@ import { LineTime } from './line-time';
1717

1818

1919
export type DraggableSliderProps = {
20-
linesAmount: number;
21-
maxLineHeight: number;
22-
minLineHeight: number;
2320
bigLineIndexOffset?: number;
24-
lineWidth?: number;
25-
indicatorColor?: string;
26-
lineColor?: string;
27-
bigLineColor?: string;
28-
mediumLineColor?: string;
29-
selectedDuration?: SharedValue<number>;
21+
selectedDuration: SharedValue<number>;
3022
};
3123

3224
const { height: WindowHeight } = Dimensions.get('window');
3325
const radius = 200;
3426
const HOUR_LABELS_AMOUNT = 10;
27+
const maxLineHeight = 20;
28+
const minLineHeight = 8;
29+
const linesAmount = 200;
30+
const lineWidth = 1.5;
3531

36-
export type CircularDraggableSliderRefType = {
37-
resetTimer: () => void;
38-
runTimer: (to: number) => void;
39-
stopTimer: () => void;
40-
};
41-
42-
export const CircularDraggableSlider = forwardRef<
43-
CircularDraggableSliderRefType,
44-
DraggableSliderProps
45-
>(
46-
(
47-
{
48-
linesAmount,
49-
maxLineHeight,
50-
minLineHeight,
51-
bigLineIndexOffset = 10,
52-
lineWidth = 1.5,
53-
lineColor = '#c6c6c6',
54-
bigLineColor = '#c6c6c6',
55-
mediumLineColor = '#c6c6c6',
56-
selectedDuration,
57-
},
58-
ref,
59-
) => {
60-
const progress = useSharedValue(0);
61-
const isTimerEnabled = useSharedValue(false);
62-
const diameter = 2 * Math.PI * radius;
63-
const distanceBetweenTwoTicks = diameter / linesAmount;
64-
const listWidth = diameter;
32+
export const CircleTime: React.FC<DraggableSliderProps> = ({
33+
bigLineIndexOffset = 10,
34+
selectedDuration,
35+
}) => {
36+
const progress = useSharedValue(0);
37+
const diameter = 2 * Math.PI * radius;
38+
const distanceBetweenTwoTicks = diameter / linesAmount;
39+
const listWidth = diameter;
6540

66-
const offset = 0;
67-
const progressRadiants = useDerivedValue(() => {
68-
return interpolate(
69-
-progress.value,
70-
[0, listWidth],
71-
[offset, 2 * Math.PI + offset],
72-
);
73-
}, [listWidth]);
41+
const progressRadiants = useDerivedValue(() => {
42+
return interpolate(
43+
-progress.value,
44+
[0, listWidth],
45+
[0, 2 * Math.PI],
46+
);
47+
}, [listWidth]);
7448

75-
76-
useAnimatedReaction(
77-
() => selectedDuration?.value ?? null,
78-
duration => {
79-
if (duration === null) {
80-
return;
81-
}
49+
const mediumLineHeight = useMemo(
50+
() => (maxLineHeight + minLineHeight) / 2,
51+
[maxLineHeight, minLineHeight],
52+
);
53+
const lineIndices = useMemo(
54+
() => Array.from({ length: linesAmount }, (_, index) => index),
55+
[linesAmount],
56+
);
57+
const hourLabels = useMemo(() => {
58+
return Array.from({ length: HOUR_LABELS_AMOUNT }, (_, hourIndex) => {
59+
const label = `${hourIndex + 1} hr`;
60+
const tickIndex = (hourIndex + 1) * bigLineIndexOffset;
61+
return { label, tickIndex };
62+
});
63+
}, [bigLineIndexOffset]);
8264

83-
const clampedDuration = Math.max(0, duration);
84-
const targetIndex = Math.min(
85-
linesAmount - 1,
86-
clampedDuration * bigLineIndexOffset,
87-
);
88-
const targetProgress = -distanceBetweenTwoTicks * targetIndex;
65+
useAnimatedReaction(
66+
() => selectedDuration.value,
67+
duration => {
68+
'worklet';
69+
const clampedDuration = Math.max(0, duration);
70+
const targetIndex = Math.min(
71+
linesAmount - 1,
72+
clampedDuration * bigLineIndexOffset,
73+
);
74+
const targetProgress = -distanceBetweenTwoTicks * targetIndex;
75+
if (Math.abs(progress.value - targetProgress) < 0.01) {
76+
return;
77+
}
8978

90-
cancelAnimation(progress);
91-
// Use immediate update for real-time synchronization during scroll
92-
progress.value = targetProgress;
93-
},
94-
[bigLineIndexOffset, distanceBetweenTwoTicks, selectedDuration],
95-
);
79+
cancelAnimation(progress);
80+
progress.value = targetProgress;
81+
},
82+
[bigLineIndexOffset, distanceBetweenTwoTicks, linesAmount],
83+
);
9684

97-
return (
98-
<View style={styles.container}>
99-
<View
100-
pointerEvents="none"
101-
style={[
102-
{
103-
height: radius * 2,
104-
width: radius * 2,
105-
right: 60,
106-
transform: [
107-
{
108-
translateY: WindowHeight / 2 - radius - 36,
109-
},
110-
],
111-
},
112-
]}>
85+
return (
86+
<View style={styles.container}>
87+
<View
88+
pointerEvents="none"
89+
style={[
90+
{
91+
height: radius * 2,
92+
width: radius * 2,
93+
right: 60,
94+
transform: [
95+
{
96+
translateY: WindowHeight / 2 - radius - 36,
97+
},
98+
],
99+
},
100+
]}>
113101

114-
<Animated.View pointerEvents="none">
102+
<Animated.View pointerEvents="none">
115103

116-
{new Array(linesAmount).fill(0).map((_, index) => {
117-
const isBigLine = index % bigLineIndexOffset === 0;
118-
const midpointOffset = bigLineIndexOffset / 2;
119-
const isMediumLine = !isBigLine && index % bigLineIndexOffset === midpointOffset;
120-
const mediumLineHeight = (maxLineHeight + minLineHeight) / 2;
121-
let height: number;
122-
let color: string;
104+
{lineIndices.map(index => {
105+
const isBigLine = index % bigLineIndexOffset === 0;
106+
const midpointOffset = bigLineIndexOffset / 2;
107+
const isMediumLine =
108+
!isBigLine && index % bigLineIndexOffset === midpointOffset;
109+
let height: number;
110+
let color: string;
123111

124-
if (isBigLine) {
125-
height = maxLineHeight;
126-
color = bigLineColor;
127-
} else if (isMediumLine) {
128-
height = mediumLineHeight;
129-
color = mediumLineColor;
130-
} else {
131-
height = minLineHeight;
132-
color = lineColor;
133-
}
112+
if (isBigLine) {
113+
height = maxLineHeight;
114+
color = '#c6c6c6';
115+
} else if (isMediumLine) {
116+
height = mediumLineHeight;
117+
color = '#c6c6c6';
118+
} else {
119+
height = minLineHeight;
120+
color = '#c6c6c6';
121+
}
134122

135-
return (
136-
<LineTime
137-
disabled={isTimerEnabled}
138-
key={index}
139-
height={height}
140-
radius={radius}
141-
progressRadiants={progressRadiants}
142-
index={index}
143-
lineWidth={lineWidth}
144-
color={color}
145-
linesAmount={linesAmount}
146-
/>
147-
);
148-
})}
149-
{new Array(HOUR_LABELS_AMOUNT).fill(0).map((_, hourIndex) => {
150-
const label = `${hourIndex + 1} hr`;
151-
const tickIndex = (hourIndex + 1) * bigLineIndexOffset;
152-
if (tickIndex > linesAmount) {
153-
return null;
154-
}
123+
return (
124+
<LineTime
125+
key={index}
126+
height={height}
127+
radius={radius}
128+
progressRadiants={progressRadiants}
129+
index={index}
130+
lineWidth={lineWidth}
131+
color={color}
132+
linesAmount={linesAmount}
133+
/>
134+
);
135+
})}
136+
{hourLabels.map(({ label, tickIndex }) => {
137+
if (tickIndex > linesAmount) {
138+
return null;
139+
}
155140

156-
return (
157-
<HourLabel
158-
key={label}
159-
label={label}
160-
radius={radius}
161-
linesAmount={linesAmount}
162-
progressRadiants={progressRadiants}
163-
tickIndex={tickIndex}
164-
/>
165-
);
166-
})}
167-
</Animated.View>
168-
<LinearGradient
169-
colors={['#000000', '#000000', '#000000', '#00000070', 'transparent']}
170-
start={{
171-
x: 0,
172-
y: 0
173-
}}
174-
end={{
175-
x: 1.1,
176-
y: 0
177-
}}
178-
style={{
179-
position: 'absolute',
180-
height: radius * 2 + 94,
181-
width: radius * 2 + 94,
182-
left: -220,
183-
top: -(WindowHeight / 2 - radius + 8),
184-
// backgroundColor: 'red',
185-
borderRadius: 1000
186-
}}
187-
/>
188-
</View>
189-
<Animated.View
190-
pointerEvents="none"
191-
style={[
192-
{
193-
height: WindowHeight / 2,
194-
},
195-
styles.timer,
196-
]}
141+
return (
142+
<HourLabel
143+
key={label}
144+
label={label}
145+
radius={radius}
146+
linesAmount={linesAmount}
147+
progressRadiants={progressRadiants}
148+
tickIndex={tickIndex}
149+
/>
150+
);
151+
})}
152+
</Animated.View>
153+
<LinearGradient
154+
colors={['#000000', '#000000', '#000000', '#00000070', 'transparent']}
155+
start={{
156+
x: 0,
157+
y: 0
158+
}}
159+
end={{
160+
x: 1.1,
161+
y: 0
162+
}}
163+
style={{
164+
position: 'absolute',
165+
height: radius * 2 + 94,
166+
width: radius * 2 + 94,
167+
left: -220,
168+
top: -(WindowHeight / 2 - radius + 8),
169+
// backgroundColor: 'red',
170+
borderRadius: 1000
171+
}}
197172
/>
198173
</View>
199-
);
200-
},
201-
);
174+
<Animated.View
175+
pointerEvents="none"
176+
style={[
177+
{
178+
height: WindowHeight / 2,
179+
},
180+
styles.timer,
181+
]}
182+
/>
183+
</View>
184+
);
185+
};
202186

203187
type HourLabelProps = {
204188
label: string;

0 commit comments

Comments
 (0)