gitshark

Clone repository

git clone https://git.vilip.de/git/vilip/taskflow.git
git clone git@git.vilip.de:vilip/taskflow.git
import React, { useState } from 'react';
import { StyleSheet, FlatList, TouchableOpacity, View } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { router, useFocusEffect } from 'expo-router';
import { Ionicons } from '@expo/vector-icons';
import { ThemedView } from '@/components/themed-view';
import { ThemedText } from '@/components/themed-text';
import { Task } from '@/types/gtd';
import { taskService } from '@/services';
import { Colors } from '@/constants/theme';
import { useColorScheme } from '@/hooks/use-color-scheme';
import { describeRecurrence } from '@/utils/recurrence';

export default function RepeatingTasksScreen() {
  const [tasks, setTasks] = useState<Task[]>([]);
  const colorScheme = useColorScheme() ?? 'light';
  const colors = Colors[colorScheme];

  useFocusEffect(
    React.useCallback(() => {
      loadRepeatingTasks();
    }, [])
  );

  const loadRepeatingTasks = async () => {
    try {
      const repeating = await taskService.getRepeatingTasks();
      setTasks(repeating);
    } catch (error) {
      console.error('Failed to load repeating tasks:', error);
    }
  };

  const handleTaskPress = (task: Task) => {
    router.push({ pathname: '/modal', params: { taskId: task.id } });
  };

  const formatNextDue = (timestamp?: number) => {
    if (!timestamp) return 'no date set';
    return new Date(timestamp).toLocaleDateString('de-DE', {
      weekday: 'short',
      day: '2-digit',
      month: 'short',
    });
  };

  return (
    <ThemedView style={styles.container}>
      <SafeAreaView style={styles.safeArea} edges={['top']}>
        <ThemedView style={styles.header}>
          <TouchableOpacity onPress={() => router.back()} style={styles.backButton}>
            <Ionicons name="chevron-back" size={28} color={colors.tint} />
          </TouchableOpacity>
          <ThemedText type="title">Repeating Tasks</ThemedText>
        </ThemedView>

        {tasks.length === 0 ? (
          <ThemedView style={styles.emptyState}>
            <ThemedText style={styles.emptyTitle}>No Repeating Tasks</ThemedText>
            <ThemedText style={styles.emptyDescription}>
              Set a task to repeat (weekly, monthly or custom) and it will appear here.
            </ThemedText>
          </ThemedView>
        ) : (
          <FlatList
            data={tasks}
            keyExtractor={(item) => item.id}
            contentContainerStyle={styles.listContent}
            renderItem={({ item }) => (
              <TouchableOpacity
                style={[styles.row, { borderBottomColor: colors.border }]}
                onPress={() => handleTaskPress(item)}
              >
                <View style={styles.rowContent}>
                  <ThemedText style={styles.taskTitle} numberOfLines={1}>
                    {item.title}
                  </ThemedText>
                  <ThemedText style={[styles.taskMeta, { color: colors.subtitle }]}>
                    {item.recurrence ? describeRecurrence(item.recurrence) : ''} · Next: {formatNextDue(item.dueDate)}
                  </ThemedText>
                </View>
                <Ionicons name="chevron-forward" size={20} color={colors.placeholder} />
              </TouchableOpacity>
            )}
          />
        )}
      </SafeAreaView>
    </ThemedView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  safeArea: {
    flex: 1,
  },
  header: {
    flexDirection: 'row',
    alignItems: 'center',
    gap: 4,
    paddingHorizontal: 12,
    paddingTop: 16,
    paddingBottom: 12,
  },
  backButton: {
    padding: 4,
  },
  listContent: {
    paddingHorizontal: 16,
    paddingTop: 8,
    paddingBottom: 32,
  },
  row: {
    flexDirection: 'row',
    alignItems: 'center',
    paddingVertical: 14,
    borderBottomWidth: StyleSheet.hairlineWidth,
  },
  rowContent: {
    flex: 1,
  },
  taskTitle: {
    fontSize: 16,
    marginBottom: 4,
  },
  taskMeta: {
    fontSize: 13,
  },
  emptyState: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    paddingHorizontal: 32,
  },
  emptyTitle: {
    fontSize: 20,
    fontWeight: '600',
    marginBottom: 8,
  },
  emptyDescription: {
    fontSize: 16,
    opacity: 0.6,
    textAlign: 'center',
  },
});

Keyboard shortcuts

?Show this help
g hGo home
EscClose dialog