summaryrefslogtreecommitdiff
path: root/apps/mobile/components/TaskListSkeleton.tsx
blob: 60e747d3c4fea07d15f7e2a9eb1852a9729b6324 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import React, { useEffect, useRef } from 'react';
import {
  View,
  StyleSheet,
  Animated,
  useColorScheme,
} from 'react-native';
import { Colors } from '../constants/Colors';

interface SkeletonRowProps {
  delay?: number;
}

function SkeletonRow({ delay = 0 }: SkeletonRowProps) {
  const colorScheme = useColorScheme() ?? 'light';
  const colors = Colors[colorScheme];
  const opacity = useRef(new Animated.Value(0.3)).current;

  useEffect(() => {
    const animation = Animated.loop(
      Animated.sequence([
        Animated.timing(opacity, {
          toValue: 0.7,
          duration: 800,
          useNativeDriver: true,
          delay,
        }),
        Animated.timing(opacity, {
          toValue: 0.3,
          duration: 800,
          useNativeDriver: true,
        }),
      ])
    );
    animation.start();
    return () => animation.stop();
  }, [opacity, delay]);

  const bgColor = colorScheme === 'dark' ? '#374151' : '#e5e7eb';

  return (
    <View
      style={[
        styles.row,
        {
          backgroundColor: colors.card,
          borderColor: colors.border,
        },
      ]}
    >
      <Animated.View
        style={[
          styles.dot,
          { backgroundColor: bgColor, opacity },
        ]}
      />
      <View style={styles.content}>
        <Animated.View
          style={[
            styles.titleBar,
            { backgroundColor: bgColor, opacity },
          ]}
        />
        <Animated.View
          style={[
            styles.subtitleBar,
            { backgroundColor: bgColor, opacity },
          ]}
        />
      </View>
    </View>
  );
}

export function TaskListSkeleton() {
  return (
    <View style={styles.container}>
      <SkeletonRow delay={0} />
      <SkeletonRow delay={100} />
      <SkeletonRow delay={200} />
      <SkeletonRow delay={300} />
      <SkeletonRow delay={400} />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  row: {
    flexDirection: 'row',
    alignItems: 'center',
    paddingVertical: 16,
    paddingHorizontal: 16,
    borderBottomWidth: StyleSheet.hairlineWidth,
    minHeight: 64,
  },
  dot: {
    width: 10,
    height: 10,
    borderRadius: 5,
    marginRight: 12,
  },
  content: {
    flex: 1,
    gap: 8,
  },
  titleBar: {
    height: 16,
    width: '70%',
    borderRadius: 4,
  },
  subtitleBar: {
    height: 12,
    width: '40%',
    borderRadius: 4,
  },
});