mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-20 01:21:24 +02:00
Move inactive projects out of active workspace: - bauntown (community website) - maerchenzauber (AI story generation) - memoro (voice memo app) - news (news aggregation) - nutriphi (nutrition tracking) - reader (reading app) - uload (URL shortener) - wisekeep (AI wisdom extraction) Update CLAUDE.md documentation: - Add presi to active projects - Document archived projects section - Update workspace configuration Archived apps can be re-activated by moving back to apps/ 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1224 lines
29 KiB
TypeScript
1224 lines
29 KiB
TypeScript
import React, { useState, useEffect } from 'react';
|
|
import {
|
|
View,
|
|
StyleSheet,
|
|
ScrollView,
|
|
Pressable,
|
|
Image,
|
|
ActivityIndicator,
|
|
Linking,
|
|
Alert,
|
|
useWindowDimensions,
|
|
} from 'react-native';
|
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
|
import { StatusBar } from 'expo-status-bar';
|
|
import { Stack, useRouter } from 'expo-router';
|
|
import { theme } from '../src/theme/theme';
|
|
import Button from '../components/atoms/Button';
|
|
import Text from '../components/atoms/Text';
|
|
import { supabase } from '../src/utils/supabase';
|
|
import { Ionicons } from '@expo/vector-icons';
|
|
import { useManaBalance } from '../hooks/useManaBalance';
|
|
import { useAuth } from '../src/contexts/AuthContext';
|
|
import CommonHeader from '../components/molecules/CommonHeader';
|
|
import ManaIcon from '../components/icons/ManaIcon';
|
|
import {
|
|
getSubscriptionData,
|
|
purchaseSubscription,
|
|
purchaseManaPackage,
|
|
restorePurchases,
|
|
} from '../src/features/subscription/revenueCatManager';
|
|
import type {
|
|
RevenueCatSubscriptionPlan,
|
|
RevenueCatManaPackage,
|
|
} from '../src/features/subscription/subscriptionTypes';
|
|
import { ParentalGate } from '../src/components/ParentalGate';
|
|
import { useParentalGate } from '../src/hooks/useParentalGate';
|
|
|
|
type SubscriptionPlan = 'free' | 'mini' | 'plus' | 'pro' | 'giant';
|
|
type PackageSize =
|
|
| 'small'
|
|
| 'medium'
|
|
| 'large'
|
|
| 'xlarge'
|
|
| 'team-small'
|
|
| 'team-medium'
|
|
| 'team-large';
|
|
|
|
interface SubscriptionOption {
|
|
id: SubscriptionPlan;
|
|
name: string;
|
|
price: number;
|
|
mana: number;
|
|
}
|
|
|
|
interface ManaPackage {
|
|
id: PackageSize;
|
|
name: string;
|
|
manaAmount: number;
|
|
price: number;
|
|
isTeamPackage?: boolean;
|
|
}
|
|
|
|
const subscriptionOptions: SubscriptionOption[] = [
|
|
{
|
|
id: 'free',
|
|
name: 'Mana-Tropfen',
|
|
price: 0,
|
|
mana: 150,
|
|
},
|
|
{
|
|
id: 'mini',
|
|
name: 'Kleiner Mana Stream',
|
|
price: 5.99,
|
|
mana: 600,
|
|
},
|
|
{
|
|
id: 'plus',
|
|
name: 'Mittlerer Mana Stream',
|
|
price: 14.99,
|
|
mana: 1500,
|
|
},
|
|
{
|
|
id: 'pro',
|
|
name: 'Großer Mana Stream',
|
|
price: 29.99,
|
|
mana: 3000,
|
|
},
|
|
{
|
|
id: 'giant',
|
|
name: 'Riesiger Mana Stream',
|
|
price: 49.99,
|
|
mana: 5000,
|
|
},
|
|
];
|
|
|
|
const manaPackages: ManaPackage[] = [
|
|
{
|
|
id: 'small',
|
|
name: 'Kleiner Mana Trank',
|
|
manaAmount: 350,
|
|
price: 4.99,
|
|
},
|
|
{
|
|
id: 'medium',
|
|
name: 'Mittlerer Mana Trank',
|
|
manaAmount: 700,
|
|
price: 9.99,
|
|
},
|
|
{
|
|
id: 'large',
|
|
name: 'Großer Mana Trank',
|
|
manaAmount: 1400,
|
|
price: 19.99,
|
|
},
|
|
{
|
|
id: 'xlarge',
|
|
name: 'Riesiger Mana Trank',
|
|
manaAmount: 2800,
|
|
price: 39.99,
|
|
},
|
|
];
|
|
|
|
export default function SubscriptionScreen() {
|
|
const router = useRouter();
|
|
const { user } = useAuth();
|
|
const { manaBalance, userData, loading, error, refetch } = useManaBalance();
|
|
const { width: screenWidth } = useWindowDimensions();
|
|
const {
|
|
isVisible: isParentalGateVisible,
|
|
config: parentalGateConfig,
|
|
setIsVisible: setParentalGateVisible,
|
|
openEmailWithGate,
|
|
openExternalLinkWithGate,
|
|
requestParentalPermission,
|
|
} = useParentalGate();
|
|
|
|
// Responsive layout detection
|
|
const isTablet = screenWidth >= 768;
|
|
const isWideScreen = screenWidth >= 1024;
|
|
|
|
// RevenueCat subscription data
|
|
const [rcSubscriptions, setRcSubscriptions] = useState<RevenueCatSubscriptionPlan[]>([]);
|
|
const [rcPackages, setRcPackages] = useState<RevenueCatManaPackage[]>([]);
|
|
const [loadingSubscriptions, setLoadingSubscriptions] = useState(true);
|
|
const [purchasing, setPurchasing] = useState(false);
|
|
const [selectedBillingCycle, setSelectedBillingCycle] = useState<'monthly' | 'yearly'>('monthly');
|
|
|
|
// Load subscription data from RevenueCat
|
|
useEffect(() => {
|
|
loadSubscriptionData();
|
|
}, []);
|
|
|
|
const loadSubscriptionData = async () => {
|
|
try {
|
|
setLoadingSubscriptions(true);
|
|
const data = await getSubscriptionData();
|
|
console.log('[Subscription] Loaded data:', {
|
|
subscriptions: data.subscriptions.length,
|
|
packages: data.packages.length,
|
|
isFromRevenueCat: data.isFromRevenueCat,
|
|
});
|
|
setRcSubscriptions(data.subscriptions);
|
|
setRcPackages(data.packages);
|
|
} catch (error) {
|
|
console.error('[Subscription] Error loading data:', error);
|
|
Alert.alert('Fehler', 'Abonnements konnten nicht geladen werden');
|
|
} finally {
|
|
setLoadingSubscriptions(false);
|
|
}
|
|
};
|
|
|
|
const handleSubscribe = async (subscriptionId: string, billingCycle: 'monthly' | 'yearly') => {
|
|
// Require parental permission before purchase
|
|
const granted = await requestParentalPermission({
|
|
title: 'Abonnement kaufen',
|
|
message: 'Um ein Abonnement zu kaufen, löse bitte diese Rechenaufgabe:',
|
|
});
|
|
|
|
if (!granted) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
setPurchasing(true);
|
|
console.log(`[Subscription] Purchasing: ${subscriptionId} (${billingCycle})`);
|
|
|
|
const customerInfo = await purchaseSubscription(subscriptionId, billingCycle);
|
|
console.log('[Subscription] Purchase successful:', customerInfo);
|
|
|
|
// Refresh mana balance
|
|
await refetch();
|
|
|
|
Alert.alert('Erfolgreich!', 'Dein Abonnement wurde aktiviert.', [
|
|
{ text: 'OK', onPress: () => router.back() },
|
|
]);
|
|
} catch (error: any) {
|
|
console.error('[Subscription] Purchase error:', error);
|
|
if (!error.userCancelled) {
|
|
Alert.alert(
|
|
'Fehler',
|
|
error.message || 'Der Kauf konnte nicht abgeschlossen werden. Bitte versuche es erneut.'
|
|
);
|
|
}
|
|
} finally {
|
|
setPurchasing(false);
|
|
}
|
|
};
|
|
|
|
const handleBuyPackage = async (packageId: string) => {
|
|
// Require parental permission before purchase
|
|
const granted = await requestParentalPermission({
|
|
title: 'Mana kaufen',
|
|
message: 'Um Mana zu kaufen, löse bitte diese Rechenaufgabe:',
|
|
});
|
|
|
|
if (!granted) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
setPurchasing(true);
|
|
console.log(`[Subscription] Buying package: ${packageId}`);
|
|
|
|
const customerInfo = await purchaseManaPackage(packageId);
|
|
console.log('[Subscription] Package purchase successful:', customerInfo);
|
|
|
|
// Refresh mana balance
|
|
await refetch();
|
|
|
|
Alert.alert('Erfolgreich!', 'Dein Mana wurde aufgeladen.', [
|
|
{ text: 'OK', onPress: () => router.back() },
|
|
]);
|
|
} catch (error: any) {
|
|
console.error('[Subscription] Package purchase error:', error);
|
|
if (!error.userCancelled) {
|
|
Alert.alert(
|
|
'Fehler',
|
|
error.message || 'Der Kauf konnte nicht abgeschlossen werden. Bitte versuche es erneut.'
|
|
);
|
|
}
|
|
} finally {
|
|
setPurchasing(false);
|
|
}
|
|
};
|
|
|
|
const handleRestorePurchases = async () => {
|
|
try {
|
|
setPurchasing(true);
|
|
Alert.alert('Käufe werden wiederhergestellt...', 'Bitte warten');
|
|
|
|
const customerInfo = await restorePurchases();
|
|
console.log('[Subscription] Purchases restored:', customerInfo);
|
|
|
|
await refetch();
|
|
|
|
Alert.alert('Erfolgreich!', 'Deine Käufe wurden wiederhergestellt.');
|
|
} catch (error: any) {
|
|
console.error('[Subscription] Restore error:', error);
|
|
Alert.alert(
|
|
'Fehler',
|
|
'Käufe konnten nicht wiederhergestellt werden. Bitte versuche es erneut.'
|
|
);
|
|
} finally {
|
|
setPurchasing(false);
|
|
}
|
|
};
|
|
|
|
const formatPrice = (price: number) => {
|
|
return price.toFixed(2).replace('.', ',') + ' €';
|
|
};
|
|
|
|
const renderBillingToggle = () => {
|
|
return (
|
|
<View style={styles.billingToggle}>
|
|
<Pressable
|
|
style={[
|
|
styles.billingOption,
|
|
selectedBillingCycle === 'monthly' && styles.selectedBillingOption,
|
|
]}
|
|
onPress={() => setSelectedBillingCycle('monthly')}
|
|
>
|
|
<Text
|
|
variant="body"
|
|
style={[
|
|
styles.billingText,
|
|
selectedBillingCycle === 'monthly' && styles.selectedBillingText,
|
|
]}
|
|
>
|
|
Monatlich
|
|
</Text>
|
|
</Pressable>
|
|
<Pressable
|
|
style={[
|
|
styles.billingOption,
|
|
selectedBillingCycle === 'yearly' && styles.selectedBillingOption,
|
|
]}
|
|
onPress={() => setSelectedBillingCycle('yearly')}
|
|
>
|
|
<Text
|
|
variant="body"
|
|
style={[
|
|
styles.billingText,
|
|
selectedBillingCycle === 'yearly' && styles.selectedBillingText,
|
|
]}
|
|
>
|
|
Jährlich
|
|
</Text>
|
|
<View style={styles.discountBadge}>
|
|
<Text style={styles.discountText}>Spare 17%</Text>
|
|
</View>
|
|
</Pressable>
|
|
</View>
|
|
);
|
|
};
|
|
|
|
const renderSubscriptionOptions = () => {
|
|
if (loadingSubscriptions) {
|
|
return (
|
|
<View style={styles.loadingContainer}>
|
|
<ActivityIndicator size="large" color="#4A90E2" />
|
|
<Text variant="body" color="rgba(255, 255, 255, 0.7)" style={{ marginTop: 10 }}>
|
|
Lade Abonnements...
|
|
</Text>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
// Filter subscriptions by billing cycle
|
|
const filteredSubscriptions = rcSubscriptions.filter(
|
|
(sub) => sub.billingCycle === selectedBillingCycle
|
|
);
|
|
|
|
// Extract base subscription ID (e.g., 'small' from 'small_monthly')
|
|
const getBaseSubscriptionId = (id: string) => id.split('_')[0];
|
|
|
|
// Calculate card width based on screen size
|
|
const getCardWidth = () => {
|
|
if (isWideScreen) return '32%'; // 3 columns
|
|
if (isTablet) return '48%'; // 2 columns
|
|
return '100%'; // 1 column on phone
|
|
};
|
|
|
|
return (
|
|
<View style={styles.sectionContainer}>
|
|
<View style={[styles.planList, isTablet && styles.planListHorizontal]}>
|
|
{filteredSubscriptions.map((plan) => (
|
|
<View key={plan.id} style={[styles.planCard, isTablet && { width: getCardWidth() }]}>
|
|
<View style={styles.planCardContent}>
|
|
<View style={styles.planCardLeft}>
|
|
<Text variant="subheader" color="#fff" style={styles.planName}>
|
|
{plan.name}
|
|
</Text>
|
|
<Text
|
|
variant="body"
|
|
color="rgba(255, 255, 255, 0.7)"
|
|
style={styles.planPriceLabel}
|
|
>
|
|
{plan.priceString} / {selectedBillingCycle === 'monthly' ? 'Monat' : 'Jahr'}
|
|
</Text>
|
|
</View>
|
|
|
|
<View style={styles.planCardRight}>
|
|
<View style={styles.manaDisplayContainer}>
|
|
<Text variant="header" color="#4A90E2" style={styles.manaAmount}>
|
|
{plan.mana}
|
|
</Text>
|
|
<Text variant="body" color="rgba(255, 255, 255, 0.7)" style={styles.manaLabel}>
|
|
Mana / Monat
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
|
|
<Button
|
|
title={`Kaufen für ${plan.priceString}`}
|
|
onPress={() =>
|
|
handleSubscribe(getBaseSubscriptionId(plan.id), selectedBillingCycle)
|
|
}
|
|
variant="primary"
|
|
size="md"
|
|
color="#4A9EFF"
|
|
style={styles.planButton}
|
|
disabled={purchasing}
|
|
/>
|
|
</View>
|
|
))}
|
|
</View>
|
|
</View>
|
|
);
|
|
};
|
|
|
|
const renderBalanceView = () => {
|
|
return (
|
|
<View style={styles.balanceContainer}>
|
|
<View style={styles.manaDropContainer}>
|
|
<View style={styles.manaDropIcon}>
|
|
<ManaIcon size={80} color="#4A90E2" />
|
|
</View>
|
|
<Text variant="header" color="#FFFFFF" style={styles.manaTitle}>
|
|
Dein Mana
|
|
</Text>
|
|
{loading ? (
|
|
<ActivityIndicator size="large" color="#4A90E2" style={{ marginTop: 10 }} />
|
|
) : error ? (
|
|
<View style={styles.errorContainer}>
|
|
<Text variant="subheader" color="#FF6B6B" style={styles.errorText}>
|
|
Fehler beim Laden
|
|
</Text>
|
|
<Text variant="caption" color="rgba(255, 255, 255, 0.5)" style={styles.errorDetail}>
|
|
{error}
|
|
</Text>
|
|
<Pressable style={styles.retryButton} onPress={refetch}>
|
|
<Text variant="body" color="#000" style={styles.retryText}>
|
|
Erneut versuchen
|
|
</Text>
|
|
</Pressable>
|
|
</View>
|
|
) : (
|
|
<Text variant="header" color="#4A90E2" style={styles.manaValue}>
|
|
{manaBalance !== null ? manaBalance : 0} Mana
|
|
</Text>
|
|
)}
|
|
</View>
|
|
|
|
<View style={styles.section}>
|
|
<Text variant="subheader" color="#FFFFFF" style={styles.sectionTitle}>
|
|
Was ist Mana?
|
|
</Text>
|
|
<Text variant="body" color="rgba(255, 255, 255, 0.7)" style={styles.sectionText}>
|
|
Mana ist deine kreative Energiewährung. Nutze es, um Geschichten zu generieren,
|
|
Charaktere zu erstellen und spezielle Funktionen freizuschalten.
|
|
</Text>
|
|
</View>
|
|
|
|
<View style={styles.section}>
|
|
<Text variant="subheader" color="#FFFFFF" style={styles.sectionTitle}>
|
|
Mana-Kosten
|
|
</Text>
|
|
<View style={styles.historyItem}>
|
|
<Text variant="body" color="rgba(255, 255, 255, 0.7)" style={styles.historyText}>
|
|
Story Generation
|
|
</Text>
|
|
<Text variant="body" color="#4A90E2" style={styles.historyMana}>
|
|
100 Mana
|
|
</Text>
|
|
</View>
|
|
<View style={styles.historyItem}>
|
|
<Text variant="body" color="rgba(255, 255, 255, 0.7)" style={styles.historyText}>
|
|
Charakter Erstellung
|
|
</Text>
|
|
<Text variant="body" color="#4A90E2" style={styles.historyMana}>
|
|
30 Mana
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
);
|
|
};
|
|
|
|
const renderPackageOptions = () => {
|
|
if (loadingSubscriptions) {
|
|
return (
|
|
<View style={styles.loadingContainer}>
|
|
<ActivityIndicator size="large" color="#4A90E2" />
|
|
<Text variant="body" color="rgba(255, 255, 255, 0.7)" style={{ marginTop: 10 }}>
|
|
Lade Pakete...
|
|
</Text>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
// Calculate package card width based on screen size
|
|
const getPackageCardWidth = () => {
|
|
if (isWideScreen) return '23%'; // 4 columns
|
|
if (isTablet) return '31%'; // 3 columns
|
|
return '48%'; // 2 columns on phone
|
|
};
|
|
|
|
return (
|
|
<View style={styles.sectionContainer}>
|
|
<View style={styles.packageList}>
|
|
{rcPackages.map((pkg) => (
|
|
<View key={pkg.id} style={[styles.packageCard, { width: getPackageCardWidth() }]}>
|
|
<Text variant="subheader" color="#fff" style={styles.packageName}>
|
|
{pkg.name}
|
|
</Text>
|
|
<Text variant="caption" color="rgba(255, 255, 255, 0.7)" style={styles.packageMana}>
|
|
{pkg.manaAmount} Mana
|
|
</Text>
|
|
<Text variant="subheader" color="#fff" style={styles.packagePrice}>
|
|
{pkg.priceString}
|
|
</Text>
|
|
<Button
|
|
title="Kaufen"
|
|
onPress={() => handleBuyPackage(pkg.id)}
|
|
variant="primary"
|
|
size="sm"
|
|
color="#4A9EFF"
|
|
style={styles.packageButton}
|
|
disabled={purchasing}
|
|
/>
|
|
</View>
|
|
))}
|
|
</View>
|
|
</View>
|
|
);
|
|
};
|
|
|
|
const handleContactPress = () => {
|
|
openEmailWithGate('kontakt@memoro.ai');
|
|
};
|
|
|
|
const handleFeedbackPress = () => {
|
|
router.push('/feedback');
|
|
};
|
|
|
|
const handlePrivacyPress = () => {
|
|
openExternalLinkWithGate('https://märchen-zauber.de/privacy', {
|
|
title: 'Datenschutzerklärung öffnen',
|
|
message: 'Um die Datenschutzerklärung zu öffnen, löse bitte diese Rechenaufgabe:',
|
|
});
|
|
};
|
|
|
|
const handleTermsPress = () => {
|
|
openExternalLinkWithGate('https://märchen-zauber.de/terms', {
|
|
title: 'AGB öffnen',
|
|
message: 'Um die AGB zu öffnen, löse bitte diese Rechenaufgabe:',
|
|
});
|
|
};
|
|
|
|
const renderContactSection = () => {
|
|
return (
|
|
<View style={styles.contactContainer}>
|
|
<Text variant="header" color="#FFFFFF" style={styles.contactTitle}>
|
|
Hilfe & Support
|
|
</Text>
|
|
<Text variant="body" color="rgba(255, 255, 255, 0.7)" style={styles.contactSubtitle}>
|
|
Hast du Fragen oder benötigst Hilfe? Wir sind für dich da!
|
|
</Text>
|
|
<Button
|
|
title="Käufe wiederherstellen"
|
|
onPress={handleRestorePurchases}
|
|
variant="primary"
|
|
size="md"
|
|
color="#4A9EFF"
|
|
iconName="arrow.clockwise"
|
|
iconSet="sf-symbols"
|
|
iconPosition="left"
|
|
style={styles.feedbackButton}
|
|
disabled={purchasing}
|
|
/>
|
|
<Button
|
|
title="Feedback & Wünsche"
|
|
onPress={handleFeedbackPress}
|
|
variant="primary"
|
|
size="md"
|
|
color="#4A9EFF"
|
|
iconName="bubble.left"
|
|
iconSet="sf-symbols"
|
|
iconPosition="left"
|
|
style={styles.feedbackButton}
|
|
/>
|
|
<Button
|
|
title="Support kontaktieren"
|
|
onPress={handleContactPress}
|
|
variant="primary"
|
|
size="md"
|
|
color="#333333"
|
|
iconName="envelope"
|
|
iconSet="sf-symbols"
|
|
iconPosition="left"
|
|
style={styles.contactButton}
|
|
/>
|
|
|
|
<View style={styles.legalLinksContainer}>
|
|
<Text variant="subheader" color="#FFFFFF" style={styles.legalLinksTitle}>
|
|
Rechtliches
|
|
</Text>
|
|
<Button
|
|
title="Datenschutzerklärung"
|
|
onPress={handlePrivacyPress}
|
|
variant="primary"
|
|
size="md"
|
|
color="#444444"
|
|
iconName="shield"
|
|
iconSet="sf-symbols"
|
|
iconPosition="left"
|
|
style={styles.legalLinkButton}
|
|
/>
|
|
<Button
|
|
title="Allgemeine Geschäftsbedingungen"
|
|
onPress={handleTermsPress}
|
|
variant="primary"
|
|
size="md"
|
|
color="#444444"
|
|
iconName="doc.text"
|
|
iconSet="sf-symbols"
|
|
iconPosition="left"
|
|
style={styles.legalLinkButton}
|
|
/>
|
|
</View>
|
|
|
|
<View style={styles.subscriptionInfo}>
|
|
<Text
|
|
variant="caption"
|
|
color="rgba(255, 255, 255, 0.6)"
|
|
style={styles.subscriptionInfoText}
|
|
>
|
|
Abonnements verlängern sich automatisch, sofern nicht 24 Stunden vor Ablauf des
|
|
aktuellen Zeitraums gekündigt wird. Dein Konto wird innerhalb von 24 Stunden vor Ende
|
|
des aktuellen Zeitraums für die Verlängerung belastet.
|
|
{'\n\n'}
|
|
Du kannst deine Abonnements in deinen App Store Kontoeinstellungen nach dem Kauf
|
|
verwalten und kündigen.
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
);
|
|
};
|
|
|
|
const renderOtherApps = () => {
|
|
const apps = [
|
|
{
|
|
id: 'memoro',
|
|
name: 'Memoro',
|
|
description:
|
|
'Meeting Protokoll Software und KI-Transkription. Einfach sprechen - Memoro schreibt das Protokoll. Sparen Sie 3+ Stunden pro Woche.',
|
|
icon: 'mic',
|
|
color: '#2ECC71',
|
|
url: 'https://memoro.ai/de/download',
|
|
available: true,
|
|
useImage: true,
|
|
image: require('../assets/images/Memoro-App-Icon.png'),
|
|
},
|
|
{
|
|
id: 'storyteller',
|
|
name: 'Märchenzauber',
|
|
description:
|
|
'Erstelle magische Kindergeschichten mit KI. Personalisierte Charaktere und wunderschöne Illustrationen.',
|
|
icon: 'book',
|
|
color: '#9B59B6',
|
|
url: 'https://märchen-zauber.de',
|
|
available: true,
|
|
useImage: true,
|
|
image: require('../assets/images/icon.png'),
|
|
},
|
|
{
|
|
id: 'placeholder1',
|
|
name: 'Weitere Apps',
|
|
description:
|
|
'Bald verfügbar - Entdecke weitere kreative Apps, die mit deinem Mana-Konto funktionieren.',
|
|
icon: 'apps',
|
|
color: '#3498DB',
|
|
url: null,
|
|
available: false,
|
|
useImage: false,
|
|
},
|
|
];
|
|
|
|
const handleAppPress = async (url: string | null) => {
|
|
if (url) {
|
|
try {
|
|
const supported = await Linking.canOpenURL(url);
|
|
if (supported) {
|
|
await Linking.openURL(url);
|
|
} else {
|
|
console.error(`Cannot open URL: ${url}`);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error opening URL:', error);
|
|
}
|
|
}
|
|
};
|
|
|
|
// Calculate app card width based on screen size
|
|
const getAppCardWidth = () => {
|
|
if (isWideScreen) return '32%'; // 3 columns
|
|
if (isTablet) return '48%'; // 2 columns
|
|
return '100%'; // 1 column on phone
|
|
};
|
|
|
|
return (
|
|
<View style={styles.otherAppsContainer}>
|
|
<Text variant="header" color="#FFFFFF" style={styles.otherAppsTitle}>
|
|
Ein Mana-Konto für alle Apps
|
|
</Text>
|
|
<Text variant="body" color="rgba(255, 255, 255, 0.7)" style={styles.otherAppsSubtitle}>
|
|
Nutze dein Mana in all unseren kreativen Apps
|
|
</Text>
|
|
|
|
<View style={styles.appsList}>
|
|
{apps.map((app) => (
|
|
<View key={app.id} style={[styles.appCard, { width: getAppCardWidth() }]}>
|
|
{app.useImage && app.image ? (
|
|
<View style={styles.appImageContainer}>
|
|
<Image source={app.image} style={styles.appImage} />
|
|
</View>
|
|
) : (
|
|
<View style={styles.appIconContainer}>
|
|
<Ionicons name={app.icon as any} size={48} color={app.color} />
|
|
</View>
|
|
)}
|
|
<Text variant="subheader" color="#fff" style={styles.appName}>
|
|
{app.name}
|
|
</Text>
|
|
<Text variant="body" color="rgba(255, 255, 255, 0.7)" style={styles.appDescription}>
|
|
{app.description}
|
|
</Text>
|
|
<Button
|
|
title={app.available ? 'Mehr erfahren' : 'Bald verfügbar'}
|
|
onPress={() => handleAppPress(app.url)}
|
|
variant="primary"
|
|
size="sm"
|
|
color="#4A9EFF"
|
|
disabled={!app.available}
|
|
style={styles.appButton}
|
|
/>
|
|
</View>
|
|
))}
|
|
</View>
|
|
</View>
|
|
);
|
|
};
|
|
|
|
return (
|
|
<SafeAreaView style={styles.safeArea} edges={['top']}>
|
|
<ParentalGate
|
|
visible={isParentalGateVisible}
|
|
onSuccess={() => {
|
|
setParentalGateVisible(false);
|
|
parentalGateConfig.onSuccess?.();
|
|
}}
|
|
onCancel={() => setParentalGateVisible(false)}
|
|
title={parentalGateConfig.title}
|
|
message={parentalGateConfig.message}
|
|
/>
|
|
<CommonHeader
|
|
title="Mana"
|
|
showBackButton={true}
|
|
showSettingsButton={false}
|
|
showManaButton={false}
|
|
/>
|
|
<View style={styles.container}>
|
|
<ScrollView style={styles.content} contentContainerStyle={styles.scrollContent}>
|
|
{renderBalanceView()}
|
|
|
|
<Text variant="header" color="#FFFFFF" style={styles.mainSectionTitle}>
|
|
Mana Tränke
|
|
</Text>
|
|
<Text variant="body" color="rgba(255, 255, 255, 0.6)" style={styles.sectionSubtitle}>
|
|
1 Mana = 1,4 Cent
|
|
</Text>
|
|
{renderPackageOptions()}
|
|
|
|
<View style={styles.divider} />
|
|
|
|
{renderOtherApps()}
|
|
|
|
<View style={styles.divider} />
|
|
|
|
{renderContactSection()}
|
|
</ScrollView>
|
|
</View>
|
|
</SafeAreaView>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
safeArea: {
|
|
flex: 1,
|
|
backgroundColor: theme.colors.background.primary,
|
|
},
|
|
container: {
|
|
flex: 1,
|
|
},
|
|
content: {
|
|
flex: 1,
|
|
},
|
|
scrollContent: {
|
|
padding: 16,
|
|
paddingTop: 100, // Abstand für den Header
|
|
paddingBottom: 0,
|
|
maxWidth: 1200,
|
|
alignSelf: 'center',
|
|
width: '100%',
|
|
},
|
|
billingToggle: {
|
|
flexDirection: 'row',
|
|
backgroundColor: 'rgba(255, 255, 255, 0.05)',
|
|
borderRadius: 8,
|
|
marginBottom: 24,
|
|
padding: 4,
|
|
},
|
|
billingOption: {
|
|
flex: 1,
|
|
paddingVertical: 12,
|
|
alignItems: 'center',
|
|
borderRadius: 6,
|
|
flexDirection: 'row',
|
|
justifyContent: 'center',
|
|
},
|
|
selectedBillingOption: {
|
|
backgroundColor: 'rgba(255, 255, 255, 0.1)',
|
|
},
|
|
billingText: {
|
|
color: '#fff',
|
|
fontSize: 14,
|
|
fontWeight: '500',
|
|
},
|
|
selectedBillingText: {
|
|
color: theme.colors.yellow.dark,
|
|
},
|
|
discountBadge: {
|
|
backgroundColor: theme.colors.yellow.dark,
|
|
borderRadius: 12,
|
|
paddingHorizontal: 8,
|
|
paddingVertical: 2,
|
|
marginLeft: 8,
|
|
},
|
|
discountText: {
|
|
color: '#000',
|
|
fontSize: 10,
|
|
fontWeight: 'bold',
|
|
},
|
|
planList: {
|
|
gap: 12,
|
|
marginBottom: 24,
|
|
},
|
|
planListHorizontal: {
|
|
flexDirection: 'row',
|
|
flexWrap: 'wrap',
|
|
},
|
|
planCard: {
|
|
backgroundColor: 'rgba(255, 255, 255, 0.05)',
|
|
borderRadius: 12,
|
|
padding: 16,
|
|
borderWidth: 1,
|
|
borderColor: 'rgba(255, 255, 255, 0.1)',
|
|
},
|
|
planCardContent: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
gap: 16,
|
|
},
|
|
planCardLeft: {
|
|
flex: 1,
|
|
justifyContent: 'center',
|
|
},
|
|
planCardRight: {
|
|
alignItems: 'flex-end',
|
|
justifyContent: 'center',
|
|
},
|
|
selectedPlanCard: {
|
|
borderColor: theme.colors.yellow.dark,
|
|
borderWidth: 2,
|
|
},
|
|
planName: {
|
|
color: '#fff',
|
|
fontSize: 24,
|
|
fontWeight: 'bold',
|
|
marginBottom: 8,
|
|
},
|
|
planPriceLabel: {
|
|
color: 'rgba(255, 255, 255, 0.7)',
|
|
fontSize: 20,
|
|
fontWeight: '600',
|
|
},
|
|
manaDisplayContainer: {
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
paddingVertical: 20,
|
|
paddingHorizontal: 24,
|
|
backgroundColor: 'rgba(74, 144, 226, 0.1)',
|
|
borderRadius: 12,
|
|
minWidth: 120,
|
|
},
|
|
manaAmount: {
|
|
color: '#4A90E2',
|
|
fontSize: 36,
|
|
fontWeight: 'bold',
|
|
marginBottom: 4,
|
|
},
|
|
manaLabel: {
|
|
color: 'rgba(255, 255, 255, 0.7)',
|
|
fontSize: 13,
|
|
textAlign: 'center',
|
|
},
|
|
planButton: {
|
|
marginTop: 16,
|
|
width: '100%',
|
|
},
|
|
detailIcon: {
|
|
marginRight: 12,
|
|
},
|
|
detailTextContainer: {
|
|
flex: 1,
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
},
|
|
detailLabel: {
|
|
color: 'rgba(255, 255, 255, 0.7)',
|
|
fontWeight: '400',
|
|
width: 120, // Fixed width for all labels to align values
|
|
},
|
|
detailValue: {
|
|
color: '#fff',
|
|
fontWeight: '500',
|
|
flex: 1,
|
|
},
|
|
packageSectionTitle: {
|
|
color: '#fff',
|
|
fontSize: 18,
|
|
fontWeight: 'bold',
|
|
marginBottom: 16,
|
|
marginTop: 24,
|
|
},
|
|
packageList: {
|
|
flexDirection: 'row',
|
|
flexWrap: 'wrap',
|
|
gap: 12,
|
|
marginBottom: 16,
|
|
},
|
|
packageCard: {
|
|
backgroundColor: 'rgba(255, 255, 255, 0.05)',
|
|
borderRadius: 12,
|
|
padding: 16,
|
|
borderWidth: 1,
|
|
borderColor: 'rgba(255, 255, 255, 0.1)',
|
|
alignItems: 'center',
|
|
},
|
|
teamPackageCard: {
|
|
borderColor: 'rgba(255, 255, 255, 0.2)',
|
|
backgroundColor: 'rgba(255, 255, 255, 0.08)',
|
|
},
|
|
selectedPackageCard: {
|
|
borderColor: theme.colors.yellow.dark,
|
|
borderWidth: 2,
|
|
},
|
|
packageName: {
|
|
color: '#fff',
|
|
fontSize: 16,
|
|
fontWeight: 'bold',
|
|
marginBottom: 8,
|
|
textAlign: 'center',
|
|
},
|
|
packageMana: {
|
|
color: 'rgba(255, 255, 255, 0.7)',
|
|
fontSize: 14,
|
|
marginBottom: 4,
|
|
textAlign: 'center',
|
|
},
|
|
packagePrice: {
|
|
color: '#fff',
|
|
fontSize: 18,
|
|
fontWeight: 'bold',
|
|
marginBottom: 12,
|
|
},
|
|
packageButton: {
|
|
marginTop: 8,
|
|
width: '100%',
|
|
},
|
|
actionButton: {
|
|
marginTop: 24,
|
|
},
|
|
// Balance view styles
|
|
balanceContainer: {
|
|
width: '100%',
|
|
maxWidth: 600,
|
|
alignSelf: 'center',
|
|
},
|
|
manaDropContainer: {
|
|
alignItems: 'center',
|
|
marginBottom: 40,
|
|
padding: 30,
|
|
backgroundColor: 'rgba(74, 144, 226, 0.05)',
|
|
borderRadius: 20,
|
|
},
|
|
manaDropIcon: {
|
|
marginBottom: 20,
|
|
},
|
|
manaTitle: {
|
|
fontSize: 24,
|
|
fontWeight: 'bold',
|
|
color: '#FFFFFF',
|
|
marginBottom: 10,
|
|
},
|
|
manaValue: {
|
|
fontSize: 36,
|
|
fontWeight: 'bold',
|
|
color: '#4A90E2',
|
|
},
|
|
section: {
|
|
marginBottom: 30,
|
|
},
|
|
sectionTitle: {
|
|
fontSize: 20,
|
|
fontWeight: 'bold',
|
|
color: '#FFFFFF',
|
|
marginBottom: 15,
|
|
},
|
|
sectionText: {
|
|
fontSize: 16,
|
|
color: 'rgba(255, 255, 255, 0.7)',
|
|
lineHeight: 24,
|
|
},
|
|
historyItem: {
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-between',
|
|
paddingVertical: 12,
|
|
borderBottomWidth: 1,
|
|
borderBottomColor: 'rgba(255, 255, 255, 0.1)',
|
|
},
|
|
historyText: {
|
|
fontSize: 16,
|
|
color: 'rgba(255, 255, 255, 0.7)',
|
|
},
|
|
historyMana: {
|
|
fontSize: 16,
|
|
color: '#4A90E2',
|
|
fontWeight: 'bold',
|
|
},
|
|
errorContainer: {
|
|
alignItems: 'center',
|
|
marginTop: 10,
|
|
},
|
|
errorText: {
|
|
fontSize: 16,
|
|
color: '#FF6B6B',
|
|
marginBottom: 5,
|
|
},
|
|
errorDetail: {
|
|
fontSize: 14,
|
|
color: 'rgba(255, 255, 255, 0.5)',
|
|
marginBottom: 10,
|
|
textAlign: 'center',
|
|
},
|
|
retryButton: {
|
|
backgroundColor: theme.colors.yellow.dark,
|
|
paddingVertical: 8,
|
|
paddingHorizontal: 20,
|
|
borderRadius: 5,
|
|
},
|
|
retryText: {
|
|
color: '#000',
|
|
fontSize: 14,
|
|
fontWeight: 'bold',
|
|
},
|
|
// New styles for single page layout
|
|
divider: {
|
|
height: 1,
|
|
backgroundColor: 'rgba(255, 255, 255, 0.1)',
|
|
marginVertical: 40,
|
|
},
|
|
mainSectionTitle: {
|
|
fontSize: 28,
|
|
fontWeight: 'bold',
|
|
color: '#FFFFFF',
|
|
marginBottom: 8,
|
|
textAlign: 'center',
|
|
},
|
|
sectionSubtitle: {
|
|
fontSize: 14,
|
|
color: 'rgba(255, 255, 255, 0.6)',
|
|
marginBottom: 20,
|
|
textAlign: 'center',
|
|
fontStyle: 'italic',
|
|
},
|
|
sectionContainer: {
|
|
width: '100%',
|
|
marginBottom: 20,
|
|
},
|
|
// Other apps section styles
|
|
otherAppsContainer: {
|
|
width: '100%',
|
|
marginBottom: 40,
|
|
},
|
|
otherAppsTitle: {
|
|
fontSize: 24,
|
|
fontWeight: 'bold',
|
|
color: '#FFFFFF',
|
|
marginBottom: 8,
|
|
textAlign: 'center',
|
|
},
|
|
otherAppsSubtitle: {
|
|
fontSize: 16,
|
|
color: 'rgba(255, 255, 255, 0.7)',
|
|
marginBottom: 24,
|
|
textAlign: 'center',
|
|
},
|
|
appsList: {
|
|
flexDirection: 'row',
|
|
flexWrap: 'wrap',
|
|
gap: 20,
|
|
},
|
|
appCard: {
|
|
backgroundColor: 'rgba(255, 255, 255, 0.05)',
|
|
borderRadius: 16,
|
|
padding: 24,
|
|
borderWidth: 1,
|
|
borderColor: 'rgba(255, 255, 255, 0.1)',
|
|
alignItems: 'center',
|
|
},
|
|
appIconContainer: {
|
|
width: 90,
|
|
height: 90,
|
|
borderRadius: 22,
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
marginBottom: 16,
|
|
backgroundColor: '#181818',
|
|
borderWidth: 1,
|
|
borderColor: 'rgba(255, 255, 255, 0.1)',
|
|
shadowColor: '#000',
|
|
shadowOffset: {
|
|
width: 0,
|
|
height: 2,
|
|
},
|
|
shadowOpacity: 0.25,
|
|
shadowRadius: 3.84,
|
|
elevation: 5,
|
|
},
|
|
appImageContainer: {
|
|
width: 90,
|
|
height: 90,
|
|
borderRadius: 22,
|
|
marginBottom: 16,
|
|
overflow: 'hidden',
|
|
backgroundColor: '#181818',
|
|
borderWidth: 1,
|
|
borderColor: 'rgba(255, 255, 255, 0.1)',
|
|
shadowColor: '#000',
|
|
shadowOffset: {
|
|
width: 0,
|
|
height: 2,
|
|
},
|
|
shadowOpacity: 0.25,
|
|
shadowRadius: 3.84,
|
|
elevation: 5,
|
|
padding: 5,
|
|
},
|
|
appImage: {
|
|
width: '100%',
|
|
height: '100%',
|
|
resizeMode: 'cover',
|
|
},
|
|
appName: {
|
|
fontSize: 20,
|
|
fontWeight: 'bold',
|
|
color: '#FFFFFF',
|
|
marginBottom: 8,
|
|
textAlign: 'center',
|
|
},
|
|
appDescription: {
|
|
fontSize: 14,
|
|
color: 'rgba(255, 255, 255, 0.7)',
|
|
textAlign: 'center',
|
|
lineHeight: 20,
|
|
marginBottom: 16,
|
|
},
|
|
appButton: {
|
|
width: '100%',
|
|
marginTop: 8,
|
|
},
|
|
// Contact section styles
|
|
contactContainer: {
|
|
width: '100%',
|
|
marginBottom: 40,
|
|
alignItems: 'center',
|
|
},
|
|
contactTitle: {
|
|
fontSize: 24,
|
|
fontWeight: 'bold',
|
|
color: '#FFFFFF',
|
|
marginBottom: 8,
|
|
textAlign: 'center',
|
|
},
|
|
contactSubtitle: {
|
|
fontSize: 16,
|
|
color: 'rgba(255, 255, 255, 0.7)',
|
|
marginBottom: 24,
|
|
textAlign: 'center',
|
|
},
|
|
feedbackButton: {
|
|
width: '100%',
|
|
maxWidth: 300,
|
|
marginBottom: 12,
|
|
},
|
|
contactButton: {
|
|
width: '100%',
|
|
maxWidth: 300,
|
|
},
|
|
legalLinksContainer: {
|
|
marginTop: 32,
|
|
width: '100%',
|
|
alignItems: 'center',
|
|
},
|
|
legalLinksTitle: {
|
|
marginBottom: 16,
|
|
textAlign: 'center',
|
|
},
|
|
legalLinkButton: {
|
|
width: '100%',
|
|
maxWidth: 300,
|
|
marginBottom: 12,
|
|
},
|
|
subscriptionInfo: {
|
|
marginTop: 24,
|
|
paddingHorizontal: 20,
|
|
paddingVertical: 16,
|
|
backgroundColor: 'rgba(255, 255, 255, 0.05)',
|
|
borderRadius: 12,
|
|
width: '100%',
|
|
maxWidth: 500,
|
|
},
|
|
subscriptionInfoText: {
|
|
textAlign: 'center',
|
|
lineHeight: 20,
|
|
},
|
|
loadingContainer: {
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
padding: 40,
|
|
},
|
|
});
|