import React, { useState } from 'react';
import { StyleSheet, Alert, TouchableOpacity, ScrollView, Switch, Platform } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { router, useFocusEffect } from 'expo-router';
import * as DocumentPicker from 'expo-document-picker';
import DateTimePicker from '@react-native-community/datetimepicker';
import { ThemedView } from '@/components/themed-view';
import { ThemedText } from '@/components/themed-text';
import { storageService, taskService, webdavService, notificationService } from '@/services';
import { useTheme } from '@/contexts/theme-context';
import { Colors } from '@/constants/theme';
import { useColorScheme } from '@/hooks/use-color-scheme';
import {
DEFAULT_REMINDER_SCHEDULE,
ReminderSchedule,
ReminderTime,
WEEKDAY_DISPLAY_ORDER,
WEEKDAY_LABELS,
describeReminderSchedule,
formatReminderTime,
} from '@/utils/reminder';
export default function SettingsScreen() {
const [syncing, setSyncing] = useState(false);
const [autoArchive, setAutoArchive] = useState(false);
const [dailyNotifications, setDailyNotifications] = useState(false);
const [reminderSchedule, setReminderSchedule] = useState<ReminderSchedule>(DEFAULT_REMINDER_SCHEDULE);
const [pickerWeekday, setPickerWeekday] = useState<number | null>(null);
const [webdavConfigured, setWebdavConfigured] = useState(false);
const [webdavInfo, setWebdavInfo] = useState<{ url: string; username: string } | null>(null);
const { themeMode, setThemeMode } = useTheme();
const colorScheme = useColorScheme();
const colors = Colors[colorScheme];
useFocusEffect(
React.useCallback(() => {
checkWebDAVStatus();
checkNotificationStatus();
}, [])
);
const checkWebDAVStatus = async () => {
const isConfigured = await webdavService.isConfigured();
setWebdavConfigured(isConfigured);
if (isConfigured) {
const config = await webdavService.getConfig();
setWebdavInfo(config);
}
};
const checkNotificationStatus = async () => {
setDailyNotifications(await notificationService.isDailyReminderEnabled());
setReminderSchedule(await notificationService.getReminderSchedule());
};
const handleExport = async () => {
try {
await storageService.exportToFile();
Alert.alert(
'Export Successful',
'Your data has been exported as JSON. You can now save or share the file.',
[{ text: 'OK' }]
);
} catch {
Alert.alert('Export Failed', 'Failed to export data. Please try again.');
}
};
const handleImport = async () => {
try {
const result = await DocumentPicker.getDocumentAsync({
type: 'application/json',
copyToCacheDirectory: true,
});
if (result.canceled) {
return;
}
Alert.alert(
'Import Data',
'This will replace all existing data. Do you want to continue?',
[
{ text: 'Cancel', style: 'cancel' },
{
text: 'Import',
style: 'destructive',
onPress: async () => {
try {
await storageService.importFromFile(result.assets[0].uri);
Alert.alert(
'Import Successful',
'Your data has been imported successfully.',
[
{
text: 'OK',
onPress: () => {
// Navigate to inbox to trigger reload
router.replace('/(tabs)');
}
}
]
);
} catch {
Alert.alert(
'Import Failed',
'Failed to import data. Please make sure the file is a valid Taskflow export.',
[{ text: 'OK' }]
);
}
},
},
]
);
} catch {
Alert.alert('Import Failed', 'Failed to select file. Please try again.');
}
};
const handleClearCompleted = async () => {
Alert.alert(
'Archive Completed Tasks',
'Archive all completed tasks older than 30 days?',
[
{ text: 'Cancel', style: 'cancel' },
{
text: 'Archive',
style: 'destructive',
onPress: async () => {
try {
const count = await taskService.archiveOldCompletedTasks(30);
Alert.alert('Success', `Archived ${count} tasks`);
} catch {
Alert.alert('Error', 'Failed to archive tasks');
}
},
},
]
);
};
const handleClearAll = async () => {
Alert.alert(
'Clear All Data',
'This will delete all tasks, projects, and tags. This action cannot be undone!',
[
{ text: 'Cancel', style: 'cancel' },
{
text: 'Delete All',
style: 'destructive',
onPress: async () => {
try {
await storageService.clearAll();
Alert.alert('Success', 'All data cleared');
} catch {
Alert.alert('Error', 'Failed to clear data');
}
},
},
]
);
};
const handleSync = async () => {
if (!webdavConfigured) {
Alert.alert(
'WebDAV Not Configured',
'Please configure your WebDAV/Nextcloud connection first.',
[
{ text: 'Cancel', style: 'cancel' },
{ text: 'Configure', onPress: () => router.push('/webdav-setup') },
]
);
return;
}
setSyncing(true);
try {
const result = await webdavService.sync();
Alert.alert(
result.success ? 'Sync Complete' : 'Sync Failed',
result.message
);
} catch {
Alert.alert('Sync Failed', 'An error occurred during sync');
} finally {
setSyncing(false);
}
};
const handleDisconnectWebDAV = () => {
Alert.alert(
'Disconnect WebDAV',
'This will remove your WebDAV connection. Your local data will not be affected.',
[
{ text: 'Cancel', style: 'cancel' },
{
text: 'Disconnect',
style: 'destructive',
onPress: async () => {
await webdavService.disconnect();
await checkWebDAVStatus();
Alert.alert('Disconnected', 'WebDAV connection removed');
},
},
]
);
};
const handleToggleDailyNotifications = async (enabled: boolean) => {
if (enabled) {
const hasPermission = await notificationService.requestPermissions();
if (!hasPermission) {
Alert.alert(
'Permission Required',
'Please enable notifications in your device settings to receive daily task reminders.'
);
return;
}
const success = await notificationService.enableDailyReminder();
if (success) {
setDailyNotifications(true);
Alert.alert(
'Notifications Enabled',
`You will receive a daily reminder ${describeReminderSchedule(reminderSchedule)}`
);
}
} else {
await notificationService.disableDailyReminder();
setDailyNotifications(false);
Alert.alert('Notifications Disabled', 'Daily reminders have been turned off');
}
};
const handleReminderTimeChange = async (
weekday: number,
event: { type: string },
selectedDate?: Date
) => {
setPickerWeekday(Platform.OS === 'ios' ? weekday : null);
if (event.type === 'dismissed' || !selectedDate) {
return;
}
const newTime: ReminderTime = {
hour: selectedDate.getHours(),
minute: selectedDate.getMinutes(),
};
try {
setReminderSchedule(await notificationService.setReminderTimeForWeekday(weekday, newTime));
} catch {
Alert.alert('Error', 'Failed to update reminder time');
}
};
const pickerDate = (() => {
const { hour, minute } = reminderSchedule[pickerWeekday ?? 0];
const date = new Date();
date.setHours(hour, minute, 0, 0);
return date;
})();
const handleTestNotification = async () => {
const hasPermission = await notificationService.requestPermissions();
if (!hasPermission) {
Alert.alert(
'Permission Required',
'Please enable notifications in your device settings.'
);
return;
}
await notificationService.sendTestNotification();
Alert.alert('Test Sent', 'Check your notifications');
};
return (
<ThemedView style={styles.container}>
<SafeAreaView style={styles.safeArea} edges={['top']}>
<ThemedView style={styles.header}>
<ThemedText type="title">Settings</ThemedText>
</ThemedView>
<ScrollView style={styles.scrollView}>
<ThemedView style={styles.section}>
<ThemedText type="subtitle" style={styles.sectionTitle}>Appearance</ThemedText>
<ThemedView style={[styles.option, { borderBottomColor: colors.border }]}>
<ThemedText style={styles.optionText}>Theme</ThemedText>
<ThemedView style={styles.themeOptions}>
<TouchableOpacity
style={[
styles.themeButton,
{ borderColor: colors.border },
themeMode === 'light' && [styles.themeButtonActive, { backgroundColor: colors.tint, borderColor: colors.tint }],
]}
onPress={() => setThemeMode('light')}
>
<ThemedText style={[
styles.themeButtonText,
themeMode === 'light' && styles.themeButtonTextActive,
]}>☀️ Light</ThemedText>
</TouchableOpacity>
<TouchableOpacity
style={[
styles.themeButton,
{ borderColor: colors.border },
themeMode === 'dark' && [styles.themeButtonActive, { backgroundColor: colors.tint, borderColor: colors.tint }],
]}
onPress={() => setThemeMode('dark')}
>
<ThemedText style={[
styles.themeButtonText,
themeMode === 'dark' && styles.themeButtonTextActive,
]}>🌙 Dark</ThemedText>
</TouchableOpacity>
<TouchableOpacity
style={[
styles.themeButton,
{ borderColor: colors.border },
themeMode === 'system' && [styles.themeButtonActive, { backgroundColor: colors.tint, borderColor: colors.tint }],
]}
onPress={() => setThemeMode('system')}
>
<ThemedText style={[
styles.themeButtonText,
themeMode === 'system' && styles.themeButtonTextActive,
]}>⚙️ System</ThemedText>
</TouchableOpacity>
</ThemedView>
</ThemedView>
</ThemedView>
<ThemedView style={styles.section}>
<ThemedText type="subtitle" style={styles.sectionTitle}>Notifications</ThemedText>
<ThemedView style={[styles.option, { borderBottomColor: colors.border }]}>
<ThemedView style={styles.optionWithSwitch}>
<ThemedView style={styles.optionTextContainer}>
<ThemedText style={styles.optionText}>Daily Reminder</ThemedText>
<ThemedText style={styles.optionDescription}>
Get notified with your task count, {describeReminderSchedule(reminderSchedule)}
</ThemedText>
</ThemedView>
<Switch
value={dailyNotifications}
onValueChange={handleToggleDailyNotifications}
/>
</ThemedView>
</ThemedView>
{dailyNotifications && (
<>
<ThemedView style={[styles.option, { borderBottomColor: colors.border }]}>
<ThemedText style={styles.optionText}>Reminder Time</ThemedText>
<ThemedText style={styles.optionDescription}>
When the reminder is sent on each weekday
</ThemedText>
</ThemedView>
{WEEKDAY_DISPLAY_ORDER.map(weekday => (
<ThemedView
key={weekday}
style={[styles.option, { borderBottomColor: colors.border }]}
>
<ThemedView style={styles.optionWithSwitch}>
<ThemedView style={styles.optionTextContainer}>
<ThemedText style={styles.optionText}>{WEEKDAY_LABELS[weekday]}</ThemedText>
</ThemedView>
<TouchableOpacity
style={[styles.timeButton, { borderColor: colors.border }]}
onPress={() => setPickerWeekday(weekday)}
>
<ThemedText style={[styles.timeButtonText, { color: colors.tint }]}>
{formatReminderTime(reminderSchedule[weekday])}
</ThemedText>
</TouchableOpacity>
</ThemedView>
</ThemedView>
))}
{pickerWeekday !== null && (
<DateTimePicker
value={pickerDate}
mode="time"
is24Hour
display={Platform.OS === 'ios' ? 'spinner' : 'default'}
onChange={(event, date) => handleReminderTimeChange(pickerWeekday, event, date)}
/>
)}
<TouchableOpacity
style={[styles.option, { borderBottomColor: colors.border }]}
onPress={handleTestNotification}
>
<ThemedText style={styles.optionText}>Send Test Notification</ThemedText>
<ThemedText style={styles.optionDescription}>
Test your notification settings
</ThemedText>
</TouchableOpacity>
</>
)}
</ThemedView>
<ThemedView style={styles.section}>
<ThemedText type="subtitle" style={styles.sectionTitle}>Data</ThemedText>
<TouchableOpacity style={[styles.option, { borderBottomColor: colors.border }]} onPress={handleExport}>
<ThemedText style={styles.optionText}>Export Data</ThemedText>
<ThemedText style={styles.optionDescription}>
Export all data as JSON file
</ThemedText>
</TouchableOpacity>
<TouchableOpacity style={[styles.option, { borderBottomColor: colors.border }]} onPress={handleImport}>
<ThemedText style={styles.optionText}>Import Data</ThemedText>
<ThemedText style={styles.optionDescription}>
Import data from JSON file
</ThemedText>
</TouchableOpacity>
<TouchableOpacity
style={[styles.option, { borderBottomColor: colors.border }]}
onPress={() => router.push('/repeating')}
>
<ThemedText style={styles.optionText}>Repeating Tasks</ThemedText>
<ThemedText style={styles.optionDescription}>
View and edit your recurring tasks
</ThemedText>
</TouchableOpacity>
<TouchableOpacity
style={[styles.option, { borderBottomColor: colors.border }]}
onPress={() => router.push('/archive')}
>
<ThemedText style={styles.optionText}>View Archive</ThemedText>
<ThemedText style={styles.optionDescription}>
View all completed tasks
</ThemedText>
</TouchableOpacity>
<TouchableOpacity style={[styles.option, { borderBottomColor: colors.border }]} onPress={handleClearCompleted}>
<ThemedText style={styles.optionText}>Archive Completed Tasks</ThemedText>
<ThemedText style={styles.optionDescription}>
Remove old completed tasks (30+ days)
</ThemedText>
</TouchableOpacity>
</ThemedView>
<ThemedView style={styles.section}>
<ThemedText type="subtitle" style={styles.sectionTitle}>Sync</ThemedText>
{!webdavConfigured ? (
<TouchableOpacity
style={[styles.option, { borderBottomColor: colors.border }]}
onPress={() => router.push('/webdav-setup')}
>
<ThemedText style={styles.optionText}>Configure WebDAV / Nextcloud</ThemedText>
<ThemedText style={styles.optionDescription}>
Set up sync with your server
</ThemedText>
</TouchableOpacity>
) : (
<>
<ThemedView style={[styles.option, { borderBottomColor: colors.border }]}>
<ThemedText style={styles.optionText}>WebDAV Connected</ThemedText>
<ThemedText style={styles.optionDescription}>
{webdavInfo?.username}@{webdavInfo?.url.replace(/^https?:\/\//, '').split('/')[0]}
</ThemedText>
</ThemedView>
<TouchableOpacity
style={[styles.option, { borderBottomColor: colors.border }]}
onPress={handleSync}
disabled={syncing}
>
<ThemedText style={styles.optionText}>
{syncing ? 'Syncing...' : 'Sync Now'}
</ThemedText>
<ThemedText style={styles.optionDescription}>
Sync your data with the server
</ThemedText>
</TouchableOpacity>
<TouchableOpacity
style={[styles.option, { borderBottomColor: colors.border }]}
onPress={handleDisconnectWebDAV}
>
<ThemedText style={[styles.optionText, { color: colors.danger }]}>
Disconnect WebDAV
</ThemedText>
<ThemedText style={styles.optionDescription}>
Remove server connection
</ThemedText>
</TouchableOpacity>
</>
)}
<ThemedView style={[styles.option, { borderBottomColor: colors.border }]}>
<ThemedView style={styles.optionWithSwitch}>
<ThemedView style={styles.optionTextContainer}>
<ThemedText style={styles.optionText}>Auto Sync</ThemedText>
<ThemedText style={styles.optionDescription}>
Automatically sync on app start (Coming Soon)
</ThemedText>
</ThemedView>
<Switch
value={autoArchive}
onValueChange={setAutoArchive}
disabled
/>
</ThemedView>
</ThemedView>
</ThemedView>
<ThemedView style={styles.section}>
<ThemedText type="subtitle" style={styles.sectionTitle}>About</ThemedText>
<ThemedView style={[styles.option, { borderBottomColor: colors.border }]}>
<ThemedText style={styles.optionText}>Version</ThemedText>
<ThemedText style={styles.optionDescription}>1.0.0</ThemedText>
</ThemedView>
<ThemedView style={[styles.option, { borderBottomColor: colors.border }]}>
<ThemedText style={styles.optionText}>Storage Format</ThemedText>
<ThemedText style={styles.optionDescription}>JSON (Human-readable)</ThemedText>
</ThemedView>
</ThemedView>
<ThemedView style={styles.section}>
<TouchableOpacity style={[styles.dangerOption, { backgroundColor: colors.dangerBackground }]} onPress={handleClearAll}>
<ThemedText style={[styles.dangerText, { color: colors.danger }]}>Clear All Data</ThemedText>
</TouchableOpacity>
</ThemedView>
</ScrollView>
</SafeAreaView>
</ThemedView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
safeArea: {
flex: 1,
},
header: {
paddingHorizontal: 16,
paddingTop: 16,
paddingBottom: 12,
},
scrollView: {
flex: 1,
},
section: {
marginBottom: 32,
paddingHorizontal: 16,
},
sectionTitle: {
marginBottom: 12,
},
option: {
paddingVertical: 12,
borderBottomWidth: StyleSheet.hairlineWidth,
},
themeOptions: {
flexDirection: 'row',
gap: 8,
marginTop: 8,
},
themeButton: {
flex: 1,
paddingVertical: 8,
paddingHorizontal: 12,
borderRadius: 8,
borderWidth: 1,
alignItems: 'center',
},
themeButtonActive: {
},
themeButtonText: {
fontSize: 14,
},
themeButtonTextActive: {
color: '#fff',
fontWeight: '600',
},
optionWithSwitch: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
timeButton: {
paddingVertical: 8,
paddingHorizontal: 16,
borderRadius: 8,
borderWidth: 1,
},
timeButtonText: {
fontSize: 16,
fontWeight: '600',
},
optionTextContainer: {
flex: 1,
},
optionText: {
fontSize: 16,
marginBottom: 4,
},
optionDescription: {
fontSize: 14,
opacity: 0.6,
},
dangerOption: {
paddingVertical: 16,
alignItems: 'center',
borderRadius: 8,
},
dangerText: {
fontSize: 16,
fontWeight: '600',
},
});