fix(profile/faq): demographics-hint auto-expand + FAQ-chevron-direction

Profile:
- Hint "fuelle deine anonymen Daten aus" oeffnet das DemographicsAccordion
  jetzt automatisch via expanded-Prop + useEffect mit LayoutAnimation.
  Vorher: scrollte hin, liess es geschlossen, User musste nochmal tappen.
- DemographicsAccordion: expanded-Prop fuer external-trigger; interner
  expandedLocal-State, Toggle-Button bleibt unabhaengig functional.

ProtectionDetailsSheet FAQ:
- chevron-forward (0deg→90deg Rotation, sah aus wie Nav-Link) → chevron-down
  (0deg→180deg). Geschlossen=runter, offen=hoch. State-Toggling war schon
  korrekt, nur visuelle Affordance war falsch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
chahinebrini 2026-05-07 21:11:41 +02:00
parent 756947549f
commit 2f3b19f71b
3 changed files with 23 additions and 5 deletions

View File

@ -107,6 +107,7 @@ export default function ProfileScreen() {
const insets = useSafeAreaInsets(); const insets = useSafeAreaInsets();
const [bannerDismissed, setBannerDismissed] = useState(false); const [bannerDismissed, setBannerDismissed] = useState(false);
const [demographics, setDemographics] = useState<Demographics>(DUMMY_DEMOGRAPHICS); const [demographics, setDemographics] = useState<Demographics>(DUMMY_DEMOGRAPHICS);
const [demographicsExpanded, setDemographicsExpanded] = useState(false);
const { me } = useMe(); const { me } = useMe();
const { user } = useAuthStore(); const { user } = useAuthStore();
@ -148,6 +149,11 @@ export default function ProfileScreen() {
); );
} }
function openDemographics() {
setDemographicsExpanded(true);
scrollToDemographics();
}
return ( return (
<View style={{ flex: 1, backgroundColor: '#ffffff' }}> <View style={{ flex: 1, backgroundColor: '#ffffff' }}>
<AppHeader showBack title="Profil" /> <AppHeader showBack title="Profil" />
@ -165,7 +171,7 @@ export default function ProfileScreen() {
memberSince={profile.memberSince} memberSince={profile.memberSince}
provider={profile.provider} provider={profile.provider}
showDemographicsHint={!demoComplete} showDemographicsHint={!demoComplete}
onDemographicsHintPress={scrollToDemographics} onDemographicsHintPress={openDemographics}
onEditAvatar={() => { onEditAvatar={() => {
// TODO Phase C: AvatarPickerSheet (preset-grid + custom-upload via expo-image-picker) // TODO Phase C: AvatarPickerSheet (preset-grid + custom-upload via expo-image-picker)
Alert.alert( Alert.alert(
@ -241,6 +247,7 @@ export default function ProfileScreen() {
<DemographicsAccordion <DemographicsAccordion
demographics={demographics} demographics={demographics}
plan={profile.plan} plan={profile.plan}
expanded={demographicsExpanded}
onChange={(next) => { onChange={(next) => {
// TODO Phase C: PATCH /api/profile/me/demographics — Body: next // TODO Phase C: PATCH /api/profile/me/demographics — Body: next
// Endpoint: profile.demographics_consent_at = NOW() bei erstem Save (DSGVO-Audit-Trail). // Endpoint: profile.demographics_consent_at = NOW() bei erstem Save (DSGVO-Audit-Trail).

View File

@ -653,7 +653,7 @@ function FaqItem({ question, answer }: { question: string; answer: string }) {
const rotate = rotateAnim.interpolate({ const rotate = rotateAnim.interpolate({
inputRange: [0, 1], inputRange: [0, 1],
outputRange: ['0deg', '90deg'], outputRange: ['0deg', '180deg'],
}); });
return ( return (
@ -692,7 +692,7 @@ function FaqItem({ question, answer }: { question: string; answer: string }) {
transform: [{ rotate }], transform: [{ rotate }],
}} }}
> >
<Ionicons name="chevron-forward" size={16} color="#525252" /> <Ionicons name="chevron-down" size={16} color="#525252" />
</Animated.View> </Animated.View>
</Pressable> </Pressable>
{open && ( {open && (

View File

@ -30,6 +30,7 @@ export type Demographics = {
type Props = { type Props = {
demographics: Demographics; demographics: Demographics;
plan: Plan; plan: Plan;
expanded?: boolean;
defaultExpanded?: boolean; defaultExpanded?: boolean;
onChange?: (next: Demographics) => void; onChange?: (next: Demographics) => void;
onRevokeConsent?: () => void; onRevokeConsent?: () => void;
@ -109,11 +110,21 @@ function mockPersist(_next: Demographics) {
export function DemographicsAccordion({ export function DemographicsAccordion({
demographics, demographics,
plan, plan,
expanded: expandedProp,
defaultExpanded = false, defaultExpanded = false,
onChange, onChange,
onRevokeConsent, onRevokeConsent,
}: Props) { }: Props) {
const [expanded, setExpanded] = useState(defaultExpanded); const [expandedLocal, setExpandedLocal] = useState(defaultExpanded);
useEffect(() => {
if (expandedProp) {
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
setExpandedLocal(true);
}
}, [expandedProp]);
const expanded = expandedLocal;
const [local, setLocal] = useState<Demographics>(demographics); const [local, setLocal] = useState<Demographics>(demographics);
// Select-Sheet-State // Select-Sheet-State
@ -128,7 +139,7 @@ export function DemographicsAccordion({
function toggle() { function toggle() {
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut); LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
setExpanded((v) => !v); setExpandedLocal((v) => !v);
} }
function persist(next: Demographics) { function persist(next: Demographics) {