learning_ai_notes/mobile/src/app/(tabs)/settings.tsx
saravanakumardb1 83f4953870 fix(mobile): add missing accessibilityLabels across all screens
- settings.tsx: labels on sign-out, feedback type chips, submit button
- capture.tsx: labels on workspace selection chips
- index.tsx: labels on workspace filter chips, note card pressables
- search.tsx: labels on search input, result row pressables
- note/[id].tsx: labels on edit, save, cancel buttons
2026-03-31 00:55:05 -07:00

220 lines
6.2 KiB
TypeScript

import { useState } from 'react';
import { Alert, Pressable, ScrollView, StyleSheet, Text, TextInput, View } from 'react-native';
import { router } from 'expo-router';
import { useAuthStore, type AuthState } from '../../store/auth-store';
import { getFeedbackClient } from '../../lib/feedback-client';
import { APP_PLATFORM } from '../../lib/app-metadata';
import { colors } from '../../theme';
type FeedbackType = 'bug' | 'feature' | 'praise' | 'other';
export default function SettingsScreen() {
const email = useAuthStore((state: AuthState) => state.email);
const signOut = useAuthStore((state: AuthState) => state.signOut);
const [feedbackType, setFeedbackType] = useState<FeedbackType>('bug');
const [feedbackTitle, setFeedbackTitle] = useState('');
const [feedbackBody, setFeedbackBody] = useState('');
const [isSubmitting, setIsSubmitting] = useState(false);
async function submitFeedback(): Promise<void> {
const title = feedbackTitle.trim();
if (!title || isSubmitting) {
return;
}
setIsSubmitting(true);
try {
await getFeedbackClient().submitWithScreenshot({
type: feedbackType,
title,
body: feedbackBody.trim() || undefined,
platform: APP_PLATFORM,
});
setFeedbackTitle('');
setFeedbackBody('');
Alert.alert('Feedback sent', 'Thanks for sharing your feedback.');
} catch {
Alert.alert('Feedback failed', 'Unable to submit feedback right now. Please try again later.');
} finally {
setIsSubmitting(false);
}
}
return (
<ScrollView contentContainerStyle={styles.container}>
<Text style={styles.title}>Settings</Text>
<Text style={styles.subtitle}>Manage account and share feedback from mobile.</Text>
<View style={styles.card}>
<Text style={styles.cardTitle}>Account</Text>
<Text style={styles.metaLabel}>Signed in as</Text>
<Text style={styles.metaValue}>{email ?? 'Unknown account'}</Text>
<Pressable
accessibilityLabel="Sign out"
style={styles.secondaryButton}
onPress={() => {
signOut();
router.replace('/auth');
}}
>
<Text style={styles.secondaryButtonText}>Sign out</Text>
</Pressable>
</View>
<View style={styles.card}>
<Text style={styles.cardTitle}>Send feedback</Text>
<View style={styles.typeRow}>
{(['bug', 'feature', 'praise', 'other'] as const).map((type) => {
const isActive = type === feedbackType;
return (
<Pressable
accessibilityLabel={`Select ${type} feedback type`}
key={type}
style={[styles.typeChip, isActive ? styles.typeChipActive : null]}
onPress={() => setFeedbackType(type)}
>
<Text style={[styles.typeChipText, isActive ? styles.typeChipTextActive : null]}>{type}</Text>
</Pressable>
);
})}
</View>
<TextInput
value={feedbackTitle}
onChangeText={setFeedbackTitle}
placeholder="Feedback title"
placeholderTextColor={colors.textTertiary}
style={styles.input}
/>
<TextInput
value={feedbackBody}
onChangeText={setFeedbackBody}
placeholder="Details (optional)"
placeholderTextColor={colors.textTertiary}
style={[styles.input, styles.bodyInput]}
multiline
textAlignVertical="top"
/>
<Pressable
accessibilityLabel={isSubmitting ? 'Submitting feedback' : 'Submit feedback'}
style={[styles.primaryButton, feedbackTitle.trim().length === 0 || isSubmitting ? styles.buttonDisabled : null]}
disabled={feedbackTitle.trim().length === 0 || isSubmitting}
onPress={() => {
void submitFeedback();
}}
>
<Text style={styles.primaryButtonText}>{isSubmitting ? 'Submitting…' : 'Submit feedback'}</Text>
</Pressable>
</View>
</ScrollView>
);
}
const styles = StyleSheet.create({
container: {
backgroundColor: colors.bgCanvas,
minHeight: '100%',
padding: 20,
gap: 14,
},
title: {
color: colors.textPrimary,
fontSize: 28,
fontWeight: '700',
},
subtitle: {
color: colors.textSecondary,
fontSize: 15,
lineHeight: 21,
},
card: {
borderRadius: 16,
borderWidth: 1,
borderColor: colors.borderDefault,
backgroundColor: colors.surfaceCard,
padding: 16,
gap: 10,
},
cardTitle: {
color: colors.textPrimary,
fontSize: 17,
fontWeight: '700',
},
metaLabel: {
color: colors.textSecondary,
fontSize: 13,
textTransform: 'uppercase',
letterSpacing: 0.8,
},
metaValue: {
color: colors.textPrimary,
fontSize: 15,
fontWeight: '600',
},
typeRow: {
flexDirection: 'row',
flexWrap: 'wrap',
gap: 8,
},
typeChip: {
borderRadius: 999,
borderWidth: 1,
borderColor: colors.borderDefault,
backgroundColor: colors.bgElevated,
paddingHorizontal: 10,
paddingVertical: 6,
},
typeChipActive: {
borderColor: colors.accentPrimary,
backgroundColor: colors.accentPrimary,
},
typeChipText: {
color: colors.textSecondary,
fontSize: 12,
fontWeight: '600',
textTransform: 'capitalize',
},
typeChipTextActive: {
color: colors.textPrimary,
},
input: {
borderRadius: 12,
borderWidth: 1,
borderColor: colors.borderDefault,
backgroundColor: colors.bgElevated,
color: colors.textPrimary,
paddingHorizontal: 12,
paddingVertical: 10,
},
bodyInput: {
minHeight: 96,
},
primaryButton: {
borderRadius: 10,
backgroundColor: colors.accentPrimary,
paddingHorizontal: 14,
paddingVertical: 10,
alignItems: 'center',
},
primaryButtonText: {
color: colors.textPrimary,
fontWeight: '700',
},
secondaryButton: {
borderRadius: 10,
borderWidth: 1,
borderColor: colors.borderDefault,
paddingHorizontal: 14,
paddingVertical: 10,
alignItems: 'center',
},
secondaryButtonText: {
color: colors.textSecondary,
fontWeight: '600',
},
buttonDisabled: {
opacity: 0.6,
},
});