Added placeholder task info and made some UI/UX improvements

This commit is contained in:
Chris Sanden
2026-04-22 20:41:19 +02:00
parent b7d62637e6
commit 1879c7c1f7

View File

@@ -19,32 +19,55 @@ const colors = {
const timers = [...Array(13).keys()].map((i) => (i === 0 ? 1 : i * 5)); const timers = [...Array(13).keys()].map((i) => (i === 0 ? 1 : i * 5));
const ITEM_SIZE = width * 0.38; const ITEM_SIZE = width * 0.38;
const ITEM_SPACING = (width - ITEM_SIZE) / 2; const ITEM_SPACING = (width - ITEM_SIZE) / 2;
const TIMER_UNIT_IN_SECONDS = 1; // Set to 60 for timer value to represent minutes
const placeholderTask = { const placeholderTask = {
name: 'Read chapter 4', name: 'Read chapter 4',
description: 'Focus on the summary questions and write down anything unclear.', description: 'Focus on the summary questions and write down anything unclear.',
}; };
/* function formatTime(totalSeconds: number) {
Har bare skrevet timeren som en egen tab til å begynne med. const minutes = Math.floor(totalSeconds / 60);
Planen er at når bruker starter en task så vil de få opp denne timeren const seconds = totalSeconds % 60;
som viser TaskName og Description der tallene står nå
Kanskje en animert figur hvis vi får tid return `${minutes.toString().padStart(2, '0')}:${
*/ seconds.toString().padStart(2, '0')}`;
}
export default function App() { export default function App() {
const [containerHeight, setContainerHeight] = React.useState(0) const [containerHeight, setContainerHeight] = React.useState(0)
const scrollX = React.useRef(new Animated.Value(0)).current;
const [duration, setDuration] = React.useState(timers[0]) const [duration, setDuration] = React.useState(timers[0])
const [isRunning, setIsRunning] = React.useState(false) const [isRunning, setIsRunning] = React.useState(false)
const [timeRemaining, setTimeRemaining] = React.useState(0)
const [selectedIndex, setSelectedIndex] = React.useState(0)
const [showCountdownText, setShowCountdownText] = React.useState(false)
const scrollX = React.useRef(new Animated.Value(0)).current;
const timerAnimation = React.useRef(new Animated.Value(0)).current const timerAnimation = React.useRef(new Animated.Value(0)).current
const buttonAnimation = React.useRef(new Animated.Value(0)).current const buttonAnimation = React.useRef(new Animated.Value(0)).current
const taskDetailsAnimation = React.useRef(new Animated.Value(0)).current const taskDetailsAnimation = React.useRef(new Animated.Value(0)).current
const countdownAnimation = React.useRef(new Animated.Value(0)).current
const animation = React.useCallback(() => { const animation = React.useCallback(() => {
if (isRunning) { if (isRunning || containerHeight === 0) {
return; return;
} }
setIsRunning(true); setIsRunning(true);
setShowCountdownText(true);
taskDetailsAnimation.setValue(0); taskDetailsAnimation.setValue(0);
countdownAnimation.setValue(0);
const totalSeconds = duration * TIMER_UNIT_IN_SECONDS;
setTimeRemaining(totalSeconds);
const countdown = setInterval(() => {
setTimeRemaining((currentTime) => {
if (currentTime <= 1) {
clearInterval(countdown)
return 0;
}
return currentTime -1;
});
}, 1000);
Animated.sequence([ Animated.sequence([
Animated.parallel([ Animated.parallel([
@@ -58,6 +81,11 @@ export default function App() {
duration: 500, duration: 500,
useNativeDriver: true useNativeDriver: true
}), }),
Animated.timing(countdownAnimation, {
toValue: 1,
duration: 300,
useNativeDriver: true
}),
]), ]),
Animated.timing(timerAnimation, { Animated.timing(timerAnimation, {
toValue: 0, toValue: 0,
@@ -66,15 +94,22 @@ export default function App() {
}), }),
Animated.timing(timerAnimation, { Animated.timing(timerAnimation, {
toValue: containerHeight, toValue: containerHeight,
duration: duration * 1000, duration: totalSeconds * 1000,
useNativeDriver: true useNativeDriver: true
}), })
]).start(({ finished }) => { ]).start(({ finished }) => {
if (!finished) { if (!finished) {
setIsRunning(false); setIsRunning(false);
return; return;
} }
Animated.timing(countdownAnimation, {
toValue: 0,
duration: 200,
useNativeDriver: true
}).start(() => {
setShowCountdownText(false);
Animated.parallel([ Animated.parallel([
Animated.timing(buttonAnimation, { Animated.timing(buttonAnimation, {
toValue: 0, toValue: 0,
@@ -90,13 +125,14 @@ export default function App() {
setIsRunning(false); setIsRunning(false);
}) })
}) })
}, [buttonAnimation, duration, isRunning, taskDetailsAnimation, timerAnimation]) })
}, [countdownAnimation, buttonAnimation, containerHeight, duration, isRunning, taskDetailsAnimation, timerAnimation])
React.useEffect(() => { React.useEffect(() => {
if (containerHeight > 0) { if (containerHeight > 0 && !isRunning) {
timerAnimation.setValue(containerHeight); timerAnimation.setValue(containerHeight);
} }
}) }, [containerHeight, isRunning, timerAnimation])
const opacity = buttonAnimation.interpolate({ const opacity = buttonAnimation.interpolate({
inputRange: [0, 1], inputRange: [0, 1],
@@ -127,7 +163,7 @@ return (
<StatusBar hidden /> <StatusBar hidden />
<Animated.View <Animated.View
style={[StyleSheet.absoluteFillObject, { style={[StyleSheet.absoluteFillObject, {
height, height: containerHeight,
width, width,
backgroundColor: colors.red, backgroundColor: colors.red,
transform: [{ transform: [{
@@ -176,8 +212,10 @@ return (
showsHorizontalScrollIndicator={false} showsHorizontalScrollIndicator={false}
onMomentumScrollEnd={ev => { onMomentumScrollEnd={ev => {
const index = Math.round(ev.nativeEvent.contentOffset.x / ITEM_SIZE) const index = Math.round(ev.nativeEvent.contentOffset.x / ITEM_SIZE)
setSelectedIndex(index);
setDuration(timers[index]); setDuration(timers[index]);
}} }}
snapToInterval={ITEM_SIZE} snapToInterval={ITEM_SIZE}
decelerationRate={"fast"} decelerationRate={"fast"}
style={{flexGrow: 0}} style={{flexGrow: 0}}
@@ -185,12 +223,15 @@ return (
paddingHorizontal: ITEM_SPACING paddingHorizontal: ITEM_SPACING
}} }}
renderItem={({item, index}) => { renderItem={({item, index}) => {
const isSelected = index === selectedIndex;
const timerText = showCountdownText && isSelected ? formatTime(timeRemaining) : item;
const inputRange = [ const inputRange = [
(index - 1) * ITEM_SIZE, (index - 1) * ITEM_SIZE,
index * ITEM_SIZE, index * ITEM_SIZE,
(index + 1) * ITEM_SIZE, (index + 1) * ITEM_SIZE,
] ]
const normalOpacity = scrollX.interpolate({ const normalOpacity = scrollX.interpolate({
inputRange, inputRange,
outputRange: [.4, 1, .4] outputRange: [.4, 1, .4]
@@ -202,21 +243,21 @@ return (
}) })
const opacity = Animated.add( const opacity = Animated.add(
Animated.multiply(normalOpacity, inactiveTimerNumberOpacity), Animated.multiply(normalOpacity, inactiveTimerNumberOpacity),
Animated.multiply(selectedOpacity, buttonAnimation) Animated.multiply(selectedOpacity, countdownAnimation)
) )
const scale = scrollX.interpolate({ const scale = scrollX.interpolate({
inputRange, inputRange,
outputRange: [.7, 1, .7] outputRange: [.7, 1, .7]
}) })
return <View style={{width: ITEM_SIZE, justifyContent: 'center', alignItems: 'center'}}> return <View style={{width: ITEM_SIZE, justifyContent: 'center', alignItems: 'center'}}>
<Animated.Text style={[styles.text, { <Animated.Text style={[showCountdownText && isSelected ? styles.countdownText : styles.text, {
opacity, opacity,
transform: [{ transform: [{
scale scale
}] }]
}]}> }]}>
{item} {timerText}
</Animated.Text> </Animated.Text>
</View> </View>
} }
@@ -251,6 +292,7 @@ const styles = StyleSheet.create({
height: 80, height: 80,
borderRadius: 80, borderRadius: 80,
backgroundColor: colors.red, backgroundColor: colors.red,
textAlign: 'center',
}, },
text: { text: {
fontSize: ITEM_SIZE * 0.8, fontSize: ITEM_SIZE * 0.8,
@@ -277,5 +319,11 @@ const styles = StyleSheet.create({
lineHeight: 22, lineHeight: 22,
marginTop: 10, marginTop: 10,
textAlign: 'center', textAlign: 'center',
},
countdownText: {
fontSize: ITEM_SIZE * 0.32,
fontFamily: 'Menlo',
color: colors.text,
fontWeight: '900',
} }
}); });