e5b81f46b0
Closes the remaining store-compliance gaps before App Store / Play submission: - Account deletion (App Store guideline 5.1.1): DELETE /me removes the user row (usage cascades). The app exposes it through one quiet 'Account' caption link under the chat, visible only when signed in, driving two native dialogs (Sign out / Delete account with a destructive confirm) — no new views, no menus, minimalism intact. Deletion signs out and resets the app; copy notes that store subscriptions are cancelled in App Store / Play settings. - docs/store/privacy-policy.md: the complete data inventory (matching the actual schema), transient OpenAI processing with no training, no profiling or ads, GDPR legal bases and rights, in-app erasure. - docs/store/terms-of-service.md: not-therapy positioning with crisis guidance, AI-generated-content caveat, 18+ eligibility, auto-renewal/cancellation terms, liability, Swedish governing law. - docs/store/listing.md: App Store and Play copy written to the honest-claims rule (subtitle 'Think Beyond Thought', keywords, descriptions), plus App Privacy and Data safety questionnaire mappings. - LAUNCH.md updated: host the policy/terms, set store URLs, use the prepared listing copy. - Tests: 30 passing (adds DELETE /me coverage). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0118DaxZR36RpnY524vRqx3z
63 lines
2.0 KiB
TypeScript
63 lines
2.0 KiB
TypeScript
import { useEffect, useState } from 'react';
|
|
import { StatusBar } from 'expo-status-bar';
|
|
import type { ChatMessage } from './src/api/client';
|
|
import { getAccessToken } from './src/auth/session';
|
|
import { ChatScreen } from './src/screens/ChatScreen';
|
|
import { PaywallScreen } from './src/screens/PaywallScreen';
|
|
|
|
/**
|
|
* Two views, no navigation library. The user lands directly in the chat —
|
|
* no registration before the first question. The backend decides the paywall
|
|
* moment (the analysis is ready); after unlock the conversation continues
|
|
* with the full analysis delivered immediately.
|
|
*/
|
|
export default function App() {
|
|
const [view, setView] = useState<'chat' | 'paywall'>('chat');
|
|
const [messages, setMessages] = useState<ChatMessage[]>([]);
|
|
const [paywalled, setPaywalled] = useState(false);
|
|
const [deliverAnalysis, setDeliverAnalysis] = useState(false);
|
|
const [signedIn, setSignedIn] = useState(false);
|
|
|
|
useEffect(() => {
|
|
getAccessToken().then((token) => setSignedIn(token !== null));
|
|
}, []);
|
|
|
|
function resetToSignedOut() {
|
|
setSignedIn(false);
|
|
setMessages([]);
|
|
setPaywalled(false);
|
|
setDeliverAnalysis(false);
|
|
setView('chat');
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<StatusBar style="dark" />
|
|
{view === 'chat' && (
|
|
<ChatScreen
|
|
messages={messages}
|
|
setMessages={setMessages}
|
|
paywalled={paywalled}
|
|
onPaywallTriggered={() => setPaywalled(true)}
|
|
onContinueWithPremium={() => setView('paywall')}
|
|
deliverAnalysis={deliverAnalysis}
|
|
onAnalysisDelivered={() => setDeliverAnalysis(false)}
|
|
signedIn={signedIn}
|
|
onSignedOut={resetToSignedOut}
|
|
/>
|
|
)}
|
|
{view === 'paywall' && (
|
|
<PaywallScreen
|
|
onSubscribed={() => {
|
|
setSignedIn(true);
|
|
setPaywalled(false);
|
|
setDeliverAnalysis(true);
|
|
setView('chat');
|
|
}}
|
|
onDismiss={() => setView('chat')}
|
|
/>
|
|
)}
|
|
</>
|
|
);
|
|
}
|