fix(native/mail): duplicate add-button in empty state + intro hints in ConnectMailSheet
- mail.tsx: hide section-header "+" button when accounts.length === 0 — MailEmptyState's CTA is the sole add trigger; also replaces Pressable with TouchableOpacity - MailEmptyState: Pressable → TouchableOpacity (no-Pressable rule) - SheetFieldStack: add optional `intro?: ReactNode` prop rendered in a flexShrink:1 ScrollView above chips/active-input so it compresses gracefully when the keyboard is up - ConnectMailSheet: move app-password guide + green AES block into `intro` prop so they're visible from the start, before the user types anything Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
3eaf3f098a
commit
a3f892ddac
@ -5,6 +5,7 @@ import {
|
|||||||
Pressable,
|
Pressable,
|
||||||
ScrollView,
|
ScrollView,
|
||||||
Text,
|
Text,
|
||||||
|
TouchableOpacity,
|
||||||
View,
|
View,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import { useBottomTabBarHeight } from 'react-native-bottom-tabs';
|
import { useBottomTabBarHeight } from 'react-native-bottom-tabs';
|
||||||
@ -173,62 +174,59 @@ export default function MailScreen() {
|
|||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Section header with prominent + button */}
|
{/* Section header with prominent + button — hidden in empty state (CTA lives there) */}
|
||||||
<View
|
{accounts.length > 0 && (
|
||||||
style={{
|
<View
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
marginBottom: 10,
|
|
||||||
paddingHorizontal: 2,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<View style={{ flex: 1, marginRight: 10 }}>
|
|
||||||
<Text
|
|
||||||
style={{
|
|
||||||
fontSize: 11,
|
|
||||||
fontFamily: 'Nunito_700Bold',
|
|
||||||
color: colors.textMuted,
|
|
||||||
textTransform: 'uppercase',
|
|
||||||
letterSpacing: 0.8,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{t('mail.section_accounts')}
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
style={{
|
|
||||||
fontSize: 11,
|
|
||||||
fontFamily: 'Nunito_400Regular',
|
|
||||||
color: colors.textMuted,
|
|
||||||
marginTop: 2,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{maxAccounts === Infinity
|
|
||||||
? t('mail.section_accounts_count_unlimited', { used: accounts.length })
|
|
||||||
: t('mail.section_accounts_count', {
|
|
||||||
used: accounts.length,
|
|
||||||
max: maxAccounts,
|
|
||||||
})}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<Pressable
|
|
||||||
onPress={handleAddPress}
|
|
||||||
disabled={limitReached}
|
|
||||||
android_ripple={{ color: '#0066cc' }}
|
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: limitReached ? colors.surfaceElevated : '#007AFF',
|
flexDirection: 'row',
|
||||||
borderRadius: 12,
|
alignItems: 'center',
|
||||||
opacity: limitReached ? 0.7 : 1,
|
marginBottom: 10,
|
||||||
shadowColor: '#007AFF',
|
paddingHorizontal: 2,
|
||||||
shadowOffset: { width: 0, height: 4 },
|
|
||||||
shadowOpacity: limitReached ? 0 : 0.25,
|
|
||||||
shadowRadius: 8,
|
|
||||||
elevation: limitReached ? 0 : 4,
|
|
||||||
}}
|
}}
|
||||||
accessibilityLabel={t('mail.add_account_a11y')}
|
|
||||||
>
|
>
|
||||||
<View
|
<View style={{ flex: 1, marginRight: 10 }}>
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
fontSize: 11,
|
||||||
|
fontFamily: 'Nunito_700Bold',
|
||||||
|
color: colors.textMuted,
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
letterSpacing: 0.8,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('mail.section_accounts')}
|
||||||
|
</Text>
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
fontSize: 11,
|
||||||
|
fontFamily: 'Nunito_400Regular',
|
||||||
|
color: colors.textMuted,
|
||||||
|
marginTop: 2,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{maxAccounts === Infinity
|
||||||
|
? t('mail.section_accounts_count_unlimited', { used: accounts.length })
|
||||||
|
: t('mail.section_accounts_count', {
|
||||||
|
used: accounts.length,
|
||||||
|
max: maxAccounts,
|
||||||
|
})}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<TouchableOpacity
|
||||||
|
onPress={handleAddPress}
|
||||||
|
disabled={limitReached}
|
||||||
|
activeOpacity={limitReached ? 1 : 0.8}
|
||||||
|
accessibilityLabel={t('mail.add_account_a11y')}
|
||||||
style={{
|
style={{
|
||||||
|
backgroundColor: limitReached ? colors.surfaceElevated : '#007AFF',
|
||||||
|
borderRadius: 12,
|
||||||
|
opacity: limitReached ? 0.7 : 1,
|
||||||
|
shadowColor: '#007AFF',
|
||||||
|
shadowOffset: { width: 0, height: 4 },
|
||||||
|
shadowOpacity: limitReached ? 0 : 0.25,
|
||||||
|
shadowRadius: 8,
|
||||||
|
elevation: limitReached ? 0 : 4,
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
paddingHorizontal: 14,
|
paddingHorizontal: 14,
|
||||||
@ -250,9 +248,9 @@ export default function MailScreen() {
|
|||||||
>
|
>
|
||||||
{t('mail.add_account')}
|
{t('mail.add_account')}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</TouchableOpacity>
|
||||||
</Pressable>
|
</View>
|
||||||
</View>
|
)}
|
||||||
|
|
||||||
{/* Account cards or empty */}
|
{/* Account cards or empty */}
|
||||||
{accounts.length === 0 ? (
|
{accounts.length === 0 ? (
|
||||||
|
|||||||
@ -27,6 +27,13 @@ export type SheetField = {
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
fields: SheetField[];
|
fields: SheetField[];
|
||||||
|
/**
|
||||||
|
* Immer sichtbarer Bereich über Chips + aktivem Input — für Hinweise, die der User
|
||||||
|
* sehen soll BEVOR er tippt. Wird in einer eigenen ScrollView mit `flexShrink:1`
|
||||||
|
* gerendert, sodass er bei kleinem verfügbaren Platz (Tastatur offen) schrumpft,
|
||||||
|
* der Eingabebereich aber nie weggedrückt wird.
|
||||||
|
*/
|
||||||
|
intro?: ReactNode;
|
||||||
/** Rendert sich nach dem letzten Feld — sichtbar sobald alle Felder ausgefüllt sind. */
|
/** Rendert sich nach dem letzten Feld — sichtbar sobald alle Felder ausgefüllt sind. */
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
onComplete?: () => void;
|
onComplete?: () => void;
|
||||||
@ -41,7 +48,7 @@ type Props = {
|
|||||||
*
|
*
|
||||||
* Wird als `children` von `<FormSheet>` benutzt.
|
* Wird als `children` von `<FormSheet>` benutzt.
|
||||||
*/
|
*/
|
||||||
export function SheetFieldStack({ fields, children, onComplete }: Props) {
|
export function SheetFieldStack({ fields, intro, children, onComplete }: Props) {
|
||||||
const colors = useColors();
|
const colors = useColors();
|
||||||
const [activeIndex, setActiveIndex] = useState(0);
|
const [activeIndex, setActiveIndex] = useState(0);
|
||||||
const [fieldErrors, setFieldErrors] = useState<Record<string, string>>({});
|
const [fieldErrors, setFieldErrors] = useState<Record<string, string>>({});
|
||||||
@ -84,142 +91,157 @@ export function SheetFieldStack({ fields, children, onComplete }: Props) {
|
|||||||
const isLast = activeIndex === fields.length - 1;
|
const isLast = activeIndex === fields.length - 1;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollView
|
<View style={{ flex: 1 }}>
|
||||||
style={{ flex: 1 }}
|
{/* Intro: immer sichtbar, schrumpft bei wenig Platz (Tastatur offen) */}
|
||||||
contentContainerStyle={{ padding: 16, gap: 10 }}
|
{intro != null && (
|
||||||
keyboardShouldPersistTaps="handled"
|
<ScrollView
|
||||||
showsVerticalScrollIndicator={false}
|
style={{ flexShrink: 1 }}
|
||||||
>
|
contentContainerStyle={{ padding: 16, paddingBottom: 0 }}
|
||||||
{/* Abgeschlossene Felder als Chips */}
|
keyboardShouldPersistTaps="handled"
|
||||||
{fields.slice(0, activeIndex).map((field, index) => (
|
showsVerticalScrollIndicator={false}
|
||||||
<TouchableOpacity
|
|
||||||
key={field.key}
|
|
||||||
activeOpacity={0.7}
|
|
||||||
onPress={() => goToField(index)}
|
|
||||||
style={{
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
backgroundColor: colors.surface,
|
|
||||||
borderWidth: 1,
|
|
||||||
borderColor: colors.border,
|
|
||||||
borderRadius: 12,
|
|
||||||
paddingHorizontal: 14,
|
|
||||||
paddingVertical: 10,
|
|
||||||
gap: 10,
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<View style={{ flex: 1 }}>
|
{intro}
|
||||||
<Text
|
</ScrollView>
|
||||||
style={{ fontSize: 11, fontFamily: 'Nunito_600SemiBold', color: colors.textMuted }}
|
)}
|
||||||
>
|
|
||||||
{field.label}
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
style={{ fontSize: 14, fontFamily: 'Nunito_400Regular', color: colors.text, marginTop: 1 }}
|
|
||||||
numberOfLines={1}
|
|
||||||
>
|
|
||||||
{field.secureTextEntry ? '••••••••' : field.value}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
<Ionicons name="pencil-outline" size={14} color={colors.textMuted} />
|
|
||||||
</TouchableOpacity>
|
|
||||||
))}
|
|
||||||
|
|
||||||
{/* Aktives Feld */}
|
{/* Chips + aktives Feld + Post-Completion-Inhalt */}
|
||||||
{!allDone && (
|
<ScrollView
|
||||||
<View>
|
style={{ flex: 1 }}
|
||||||
<Text
|
contentContainerStyle={{ padding: 16, paddingTop: intro != null ? 10 : 16, gap: 10 }}
|
||||||
|
keyboardShouldPersistTaps="handled"
|
||||||
|
showsVerticalScrollIndicator={false}
|
||||||
|
>
|
||||||
|
{/* Abgeschlossene Felder als Chips */}
|
||||||
|
{fields.slice(0, activeIndex).map((field, index) => (
|
||||||
|
<TouchableOpacity
|
||||||
|
key={field.key}
|
||||||
|
activeOpacity={0.7}
|
||||||
|
onPress={() => goToField(index)}
|
||||||
style={{
|
style={{
|
||||||
fontSize: 12,
|
flexDirection: 'row',
|
||||||
fontFamily: 'Nunito_600SemiBold',
|
alignItems: 'center',
|
||||||
color: colors.textMuted,
|
backgroundColor: colors.surface,
|
||||||
marginBottom: 6,
|
borderWidth: 1,
|
||||||
|
borderColor: colors.border,
|
||||||
|
borderRadius: 12,
|
||||||
|
paddingHorizontal: 14,
|
||||||
|
paddingVertical: 10,
|
||||||
|
gap: 10,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{fields[activeIndex].label}
|
<View style={{ flex: 1 }}>
|
||||||
</Text>
|
<Text
|
||||||
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
|
style={{ fontSize: 11, fontFamily: 'Nunito_600SemiBold', color: colors.textMuted }}
|
||||||
<View
|
>
|
||||||
style={{
|
{field.label}
|
||||||
flex: 1,
|
</Text>
|
||||||
flexDirection: 'row',
|
<Text
|
||||||
alignItems: 'center',
|
style={{ fontSize: 14, fontFamily: 'Nunito_400Regular', color: colors.text, marginTop: 1 }}
|
||||||
backgroundColor: colors.surfaceElevated,
|
numberOfLines={1}
|
||||||
borderRadius: 12,
|
>
|
||||||
paddingHorizontal: 14,
|
{field.secureTextEntry ? '••••••••' : field.value}
|
||||||
borderWidth: fieldErrors[fields[activeIndex].key] ? 1 : 0,
|
</Text>
|
||||||
borderColor: '#dc2626',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<TextInput
|
|
||||||
autoFocus
|
|
||||||
value={fields[activeIndex].value}
|
|
||||||
onChangeText={(v) => {
|
|
||||||
fields[activeIndex].onChangeText(v);
|
|
||||||
if (fieldErrors[fields[activeIndex].key]) {
|
|
||||||
setFieldErrors((prev) => {
|
|
||||||
const next = { ...prev };
|
|
||||||
delete next[fields[activeIndex].key];
|
|
||||||
return next;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
placeholder={fields[activeIndex].placeholder}
|
|
||||||
placeholderTextColor={colors.textMuted}
|
|
||||||
keyboardType={fields[activeIndex].keyboardType ?? 'default'}
|
|
||||||
secureTextEntry={fields[activeIndex].secureTextEntry}
|
|
||||||
autoCapitalize={fields[activeIndex].autoCapitalize ?? 'sentences'}
|
|
||||||
autoCorrect={fields[activeIndex].autoCorrect ?? true}
|
|
||||||
returnKeyType={isLast ? 'done' : 'next'}
|
|
||||||
onSubmitEditing={advanceOrFinish}
|
|
||||||
blurOnSubmit={false}
|
|
||||||
style={{
|
|
||||||
flex: 1,
|
|
||||||
paddingVertical: 12,
|
|
||||||
fontSize: 15,
|
|
||||||
fontFamily: 'Nunito_400Regular',
|
|
||||||
color: colors.text,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{fields[activeIndex].suffix}
|
|
||||||
</View>
|
</View>
|
||||||
|
<Ionicons name="pencil-outline" size={14} color={colors.textMuted} />
|
||||||
|
</TouchableOpacity>
|
||||||
|
))}
|
||||||
|
|
||||||
<TouchableOpacity
|
{/* Aktives Feld */}
|
||||||
activeOpacity={0.8}
|
{!allDone && (
|
||||||
onPress={advanceOrFinish}
|
<View>
|
||||||
style={{
|
|
||||||
width: 44,
|
|
||||||
height: 44,
|
|
||||||
borderRadius: 22,
|
|
||||||
backgroundColor: colors.brandOrange,
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Ionicons
|
|
||||||
name={isLast ? 'checkmark' : 'arrow-forward'}
|
|
||||||
size={20}
|
|
||||||
color="#fff"
|
|
||||||
/>
|
|
||||||
</TouchableOpacity>
|
|
||||||
</View>
|
|
||||||
{fieldErrors[fields[activeIndex].key] && (
|
|
||||||
<Text
|
<Text
|
||||||
style={{
|
style={{
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
fontFamily: 'Nunito_400Regular',
|
fontFamily: 'Nunito_600SemiBold',
|
||||||
color: '#dc2626',
|
color: colors.textMuted,
|
||||||
marginTop: 4,
|
marginBottom: 6,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{fieldErrors[fields[activeIndex].key]}
|
{fields[activeIndex].label}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
|
||||||
</View>
|
<View
|
||||||
)}
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
backgroundColor: colors.surfaceElevated,
|
||||||
|
borderRadius: 12,
|
||||||
|
paddingHorizontal: 14,
|
||||||
|
borderWidth: fieldErrors[fields[activeIndex].key] ? 1 : 0,
|
||||||
|
borderColor: '#dc2626',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<TextInput
|
||||||
|
autoFocus
|
||||||
|
value={fields[activeIndex].value}
|
||||||
|
onChangeText={(v) => {
|
||||||
|
fields[activeIndex].onChangeText(v);
|
||||||
|
if (fieldErrors[fields[activeIndex].key]) {
|
||||||
|
setFieldErrors((prev) => {
|
||||||
|
const next = { ...prev };
|
||||||
|
delete next[fields[activeIndex].key];
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
placeholder={fields[activeIndex].placeholder}
|
||||||
|
placeholderTextColor={colors.textMuted}
|
||||||
|
keyboardType={fields[activeIndex].keyboardType ?? 'default'}
|
||||||
|
secureTextEntry={fields[activeIndex].secureTextEntry}
|
||||||
|
autoCapitalize={fields[activeIndex].autoCapitalize ?? 'sentences'}
|
||||||
|
autoCorrect={fields[activeIndex].autoCorrect ?? true}
|
||||||
|
returnKeyType={isLast ? 'done' : 'next'}
|
||||||
|
onSubmitEditing={advanceOrFinish}
|
||||||
|
blurOnSubmit={false}
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
paddingVertical: 12,
|
||||||
|
fontSize: 15,
|
||||||
|
fontFamily: 'Nunito_400Regular',
|
||||||
|
color: colors.text,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{fields[activeIndex].suffix}
|
||||||
|
</View>
|
||||||
|
|
||||||
{/* Rest des Formulars — sichtbar wenn alle Felder durch */}
|
<TouchableOpacity
|
||||||
{allDone && children}
|
activeOpacity={0.8}
|
||||||
</ScrollView>
|
onPress={advanceOrFinish}
|
||||||
|
style={{
|
||||||
|
width: 44,
|
||||||
|
height: 44,
|
||||||
|
borderRadius: 22,
|
||||||
|
backgroundColor: colors.brandOrange,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Ionicons
|
||||||
|
name={isLast ? 'checkmark' : 'arrow-forward'}
|
||||||
|
size={20}
|
||||||
|
color="#fff"
|
||||||
|
/>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
{fieldErrors[fields[activeIndex].key] && (
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
fontSize: 12,
|
||||||
|
fontFamily: 'Nunito_400Regular',
|
||||||
|
color: '#dc2626',
|
||||||
|
marginTop: 4,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{fieldErrors[fields[activeIndex].key]}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Rest des Formulars — sichtbar wenn alle Felder durch */}
|
||||||
|
{allDone && children}
|
||||||
|
</ScrollView>
|
||||||
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -189,92 +189,93 @@ export function ConnectMailSheet({ visible, onClose, onSuccess }: Props) {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
onComplete={() => setFieldsComplete(true)}
|
intro={
|
||||||
>
|
<View style={{ gap: 10 }}>
|
||||||
{/* App-Password-Guide — über den Datenschutz-Hinweis */}
|
{/* App-Password-Guide — provider-spezifisch, nicht für 'other' */}
|
||||||
{selectedProvider && selectedProvider.id !== 'other' && (
|
{selectedProvider && selectedProvider.id !== 'other' && (
|
||||||
<View
|
<View
|
||||||
style={{
|
|
||||||
flexDirection: 'row',
|
|
||||||
gap: 10,
|
|
||||||
padding: 12,
|
|
||||||
backgroundColor: '#f0f7ff',
|
|
||||||
borderRadius: 12,
|
|
||||||
borderWidth: 1,
|
|
||||||
borderColor: '#bfdbfe',
|
|
||||||
marginBottom: 10,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Ionicons
|
|
||||||
name="information-circle"
|
|
||||||
size={18}
|
|
||||||
color="#1d4ed8"
|
|
||||||
style={{ marginTop: 1 }}
|
|
||||||
/>
|
|
||||||
<View style={{ flex: 1, gap: 4 }}>
|
|
||||||
<Text
|
|
||||||
style={{ fontSize: 12, fontFamily: 'Nunito_600SemiBold', color: '#1e3a8a' }}
|
|
||||||
>
|
|
||||||
{t('mail.app_password_required_title')}
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
style={{
|
style={{
|
||||||
fontSize: 12,
|
flexDirection: 'row',
|
||||||
fontFamily: 'Nunito_400Regular',
|
gap: 10,
|
||||||
color: '#1d4ed8',
|
padding: 12,
|
||||||
lineHeight: 17,
|
backgroundColor: '#f0f7ff',
|
||||||
|
borderRadius: 12,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: '#bfdbfe',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t(selectedProvider.guideKey)}
|
<Ionicons
|
||||||
</Text>
|
name="information-circle"
|
||||||
{selectedProvider.guideUrl.length > 0 && (
|
size={18}
|
||||||
<TouchableOpacity
|
color="#1d4ed8"
|
||||||
activeOpacity={0.7}
|
style={{ marginTop: 1 }}
|
||||||
onPress={() => Linking.openURL(selectedProvider.guideUrl)}
|
/>
|
||||||
>
|
<View style={{ flex: 1, gap: 4 }}>
|
||||||
|
<Text
|
||||||
|
style={{ fontSize: 12, fontFamily: 'Nunito_600SemiBold', color: '#1e3a8a' }}
|
||||||
|
>
|
||||||
|
{t('mail.app_password_required_title')}
|
||||||
|
</Text>
|
||||||
<Text
|
<Text
|
||||||
style={{
|
style={{
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
fontFamily: 'Nunito_600SemiBold',
|
fontFamily: 'Nunito_400Regular',
|
||||||
color: '#007AFF',
|
color: '#1d4ed8',
|
||||||
marginTop: 2,
|
lineHeight: 17,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('mail.app_password_open_link')} →
|
{t(selectedProvider.guideKey)}
|
||||||
</Text>
|
</Text>
|
||||||
</TouchableOpacity>
|
{selectedProvider.guideUrl.length > 0 && (
|
||||||
)}
|
<TouchableOpacity
|
||||||
|
activeOpacity={0.7}
|
||||||
|
onPress={() => Linking.openURL(selectedProvider.guideUrl)}
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
fontSize: 12,
|
||||||
|
fontFamily: 'Nunito_600SemiBold',
|
||||||
|
color: '#007AFF',
|
||||||
|
marginTop: 2,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('mail.app_password_open_link')} →
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Datenschutz-Zusicherung — immer sichtbar */}
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flexDirection: 'row',
|
||||||
|
gap: 8,
|
||||||
|
padding: 12,
|
||||||
|
backgroundColor: '#f0fdf4',
|
||||||
|
borderRadius: 12,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: '#bbf7d0',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Ionicons name="shield-checkmark" size={16} color="#16a34a" style={{ marginTop: 1 }} />
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
fontSize: 12,
|
||||||
|
fontFamily: 'Nunito_400Regular',
|
||||||
|
color: '#166534',
|
||||||
|
lineHeight: 17,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('mail.form_privacy_note')}
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
)}
|
}
|
||||||
|
onComplete={() => setFieldsComplete(true)}
|
||||||
{/* Datenschutz-Hinweis */}
|
>
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
flexDirection: 'row',
|
|
||||||
gap: 8,
|
|
||||||
padding: 12,
|
|
||||||
backgroundColor: '#f0fdf4',
|
|
||||||
borderRadius: 12,
|
|
||||||
borderWidth: 1,
|
|
||||||
borderColor: '#bbf7d0',
|
|
||||||
marginBottom: 10,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Ionicons name="shield-checkmark" size={16} color="#16a34a" style={{ marginTop: 1 }} />
|
|
||||||
<Text
|
|
||||||
style={{
|
|
||||||
flex: 1,
|
|
||||||
fontSize: 12,
|
|
||||||
fontFamily: 'Nunito_400Regular',
|
|
||||||
color: '#166534',
|
|
||||||
lineHeight: 17,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{t('mail.form_privacy_note')}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{/* Fehler */}
|
{/* Fehler */}
|
||||||
{(formError ?? (connectError ? t(humanizeMailError(connectError)) : null)) && (
|
{(formError ?? (connectError ? t(humanizeMailError(connectError)) : null)) && (
|
||||||
<Text
|
<Text
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { Pressable, Text, View } from 'react-native';
|
import { Text, TouchableOpacity, View } from 'react-native';
|
||||||
import { Ionicons } from '@expo/vector-icons';
|
import { Ionicons } from '@expo/vector-icons';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useColors } from '../../lib/theme';
|
import { useColors } from '../../lib/theme';
|
||||||
@ -81,12 +81,10 @@ export function MailEmptyState({ onConnectPress }: Props) {
|
|||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* CTA */}
|
{/* CTA */}
|
||||||
<Pressable
|
<TouchableOpacity
|
||||||
onPress={onConnectPress}
|
onPress={onConnectPress}
|
||||||
style={({ pressed }) => ({
|
activeOpacity={0.85}
|
||||||
opacity: pressed ? 0.85 : 1,
|
style={{ alignSelf: 'stretch' }}
|
||||||
alignSelf: 'stretch',
|
|
||||||
})}
|
|
||||||
>
|
>
|
||||||
<View style={{
|
<View style={{
|
||||||
backgroundColor: '#007AFF',
|
backgroundColor: '#007AFF',
|
||||||
@ -99,7 +97,7 @@ export function MailEmptyState({ onConnectPress }: Props) {
|
|||||||
{t('mail.empty_state_cta')}
|
{t('mail.empty_state_cta')}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</Pressable>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user