Harte Paywall

This commit is contained in:
2026-04-29 21:16:16 +02:00
committed by Timo Knuth
parent 0f933da3c9
commit d37b49f1f6
10 changed files with 305 additions and 129 deletions

View File

@@ -1,26 +1,28 @@
import React, { useEffect, useState } from 'react';
import {
View,
Text,
TextInput,
import {
View,
Text,
TextInput,
TouchableOpacity,
StyleSheet,
KeyboardAvoidingView,
Platform,
ActivityIndicator,
ScrollView,
Image,
} from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import { router } from 'expo-router';
Image,
} from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { Ionicons } from '@expo/vector-icons';
import { router } from 'expo-router';
import { useApp } from '../../context/AppContext';
import { useColors } from '../../constants/Colors';
import { ThemeBackdrop } from '../../components/ThemeBackdrop';
import { ThemeBackdrop } from '../../components/ThemeBackdrop';
import { AuthService } from '../../services/authService';
import * as AppleAuthentication from 'expo-apple-authentication';
import Constants from 'expo-constants';
import { usePostHog } from 'posthog-react-native';
export default function LoginScreen() {
export default function LoginScreen() {
const { isDarkMode, colorPalette, hydrateSession, t } = useApp();
const colors = useColors(isDarkMode, colorPalette);
const posthog = usePostHog();
@@ -31,8 +33,13 @@ export default function LoginScreen() {
const [appleAvailable, setAppleAvailable] = useState(false);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const isExpoGo = Constants.appOwnership === 'expo';
useEffect(() => {
if (isExpoGo) {
setAppleAvailable(false);
return;
}
let mounted = true;
AppleAuthentication.isAvailableAsync()
.then((available) => {
@@ -44,7 +51,7 @@ export default function LoginScreen() {
return () => {
mounted = false;
};
}, []);
}, [isExpoGo]);
const handleLogin = async () => {
if (!email.trim() || !password) {
@@ -56,7 +63,7 @@ export default function LoginScreen() {
try {
const session = await AuthService.login(email, password);
await hydrateSession(session);
router.replace('/profile/billing');
router.replace('/(tabs)');
} catch (e: any) {
if (e.message === 'USER_NOT_FOUND') {
setError(t.errUserNotFound);
@@ -101,8 +108,11 @@ export default function LoginScreen() {
name: fullName || undefined,
});
await hydrateSession(session);
if (session.isNewUser) {
await AsyncStorage.setItem('greenlens_show_tour', 'true');
}
posthog.capture('apple_login_succeeded', { surface: 'login' });
router.replace('/profile/billing');
router.replace(session.isNewUser ? '/profile/billing' : '/(tabs)');
} catch (e: any) {
if (e?.code === 'ERR_REQUEST_CANCELED') {
return;

View File

@@ -19,6 +19,7 @@ import { ThemeBackdrop } from '../../components/ThemeBackdrop';
import { AuthService } from '../../services/authService';
import AsyncStorage from '@react-native-async-storage/async-storage';
import * as AppleAuthentication from 'expo-apple-authentication';
import Constants from 'expo-constants';
import { usePostHog } from 'posthog-react-native';
export default function SignupScreen() {
@@ -36,8 +37,13 @@ export default function SignupScreen() {
const [appleAvailable, setAppleAvailable] = useState(false);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const isExpoGo = Constants.appOwnership === 'expo';
useEffect(() => {
if (isExpoGo) {
setAppleAvailable(false);
return;
}
let mounted = true;
AppleAuthentication.isAvailableAsync()
.then((available) => {
@@ -49,7 +55,7 @@ export default function SignupScreen() {
return () => {
mounted = false;
};
}, []);
}, [isExpoGo]);
const validate = (): string | null => {
if (!name.trim()) return t.errNameRequired;
@@ -121,7 +127,7 @@ export default function SignupScreen() {
await hydrateSession(session);
await AsyncStorage.setItem('greenlens_show_tour', 'true');
posthog.capture('apple_login_succeeded', { surface: 'signup' });
router.replace('/profile/billing');
router.replace(session.isNewUser ? '/profile/billing' : '/(tabs)');
} catch (e: any) {
if (e?.code === 'ERR_REQUEST_CANCELED') {
return;