diff options
| author | soryu <soryu@soryu.co> | 2026-01-18 17:44:50 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-01-18 17:44:50 +0000 |
| commit | 869f21ee2efaefed6a5aa4fbd417c25df8dec02a (patch) | |
| tree | 2a90820ac817173e5b7154e0ba5e4f5d095f9613 /apps/mobile/components/TaskListSkeleton.tsx | |
| parent | 219bca168508e1ea5e91e8a9ce98338afeddfbd2 (diff) | |
| download | soryu-869f21ee2efaefed6a5aa4fbd417c25df8dec02a.tar.gz soryu-869f21ee2efaefed6a5aa4fbd417c25df8dec02a.zip | |
Add React Native mobile app for Makima (#3)
* [WIP] Heartbeat checkpoint - 2026-01-18 02:58:27 UTC
* feat(mobile): complete mobile app integration and verification
- Add ThemeColors type export to Colors.ts for type safety
- Export SUPABASE_URL from supabase.ts and use environment variables
- Update .env.example with correct default URLs
- Add comprehensive README.md with setup instructions
Verified:
- TypeScript compiles without errors
- App exports successfully for iOS and Android
- All screens accessible (login, dashboard, tasks, settings, task detail)
- Auth flow working with Zustand store and Supabase
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Task completion checkpoint
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Diffstat (limited to 'apps/mobile/components/TaskListSkeleton.tsx')
| -rw-r--r-- | apps/mobile/components/TaskListSkeleton.tsx | 119 |
1 files changed, 119 insertions, 0 deletions
diff --git a/apps/mobile/components/TaskListSkeleton.tsx b/apps/mobile/components/TaskListSkeleton.tsx new file mode 100644 index 0000000..60e747d --- /dev/null +++ b/apps/mobile/components/TaskListSkeleton.tsx @@ -0,0 +1,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, + }, +}); |
