Mobile: redesign, onboarding, QR codes, logo, Spotify setup
- Redesign matching the web: album-art backdrops, Reanimated equalizer, spinning live ring, drifting ambient background, gradient buttons with spring press feedback and haptics, Space Grotesk headings, animated feed (enter + layout transitions); fix invisible tab labels - Four-step onboarding for new accounts: which app you listen with (sets where songs open), the matching share setup, find friends (search, scan, invite), and a featured song. Skippable; stored server-side - QR codes: My code (share link) and an in-app scanner (camera only, no microphone permission) - Spotify setup via Last.fm or ListenBrainz with guided linking; Android shares Spotify automatically through the phone reader - New logo as app icon, adaptive/monochrome icon and splash - app.config.ts allows cleartext only for local http:// test builds Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MzFywujuH9TiuXbr9vjoX8
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { SpaceGrotesk_500Medium, SpaceGrotesk_700Bold, useFonts } from '@expo-google-fonts/space-grotesk';
|
||||
import { DarkTheme, Stack, ThemeProvider } from 'expo-router';
|
||||
import * as SplashScreen from 'expo-splash-screen';
|
||||
import { StatusBar } from 'expo-status-bar';
|
||||
@@ -33,17 +34,20 @@ export default function RootLayout() {
|
||||
function AppNavigator() {
|
||||
const { status, me, retry } = useSession();
|
||||
const signedIn = status === 'signedIn';
|
||||
const onboarded = Boolean(me?.onboardedAt);
|
||||
const [fontsLoaded, fontError] = useFonts({ SpaceGrotesk_500Medium, SpaceGrotesk_700Bold });
|
||||
const ready = status !== 'loading' && (fontsLoaded || Boolean(fontError));
|
||||
|
||||
useEffect(() => {
|
||||
if (status !== 'loading') SplashScreen.hideAsync();
|
||||
}, [status]);
|
||||
if (ready) SplashScreen.hideAsync();
|
||||
}, [ready]);
|
||||
|
||||
// Keep the Android listener pointed at this account (no-op elsewhere).
|
||||
useEffect(() => {
|
||||
if (signedIn) syncDevice().catch(() => {});
|
||||
}, [signedIn]);
|
||||
|
||||
if (status === 'loading') return null;
|
||||
if (!ready) return null;
|
||||
if (status === 'offline') {
|
||||
return (
|
||||
<View style={styles.offline}>
|
||||
@@ -75,10 +79,20 @@ function AppNavigator() {
|
||||
headerShadowVisible: false,
|
||||
contentStyle: { backgroundColor: colors.bg },
|
||||
}}>
|
||||
<Stack.Protected guard={signedIn}>
|
||||
<Stack.Protected guard={signedIn && onboarded}>
|
||||
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
|
||||
<Stack.Screen name="u/[username]" options={{ title: '' }} />
|
||||
<Stack.Screen name="share-setup" options={{ headerShown: false, presentation: 'modal' }} />
|
||||
<Stack.Screen name="qr" options={{ title: 'My code', presentation: 'modal' }} />
|
||||
<Stack.Screen name="spotify-setup" options={{ headerShown: false, presentation: 'modal' }} />
|
||||
</Stack.Protected>
|
||||
{/* New accounts go through onboarding first (finishing flips onboardedAt and lands on the feed). */}
|
||||
<Stack.Protected guard={signedIn && !onboarded}>
|
||||
<Stack.Screen name="onboarding" options={{ headerShown: false, gestureEnabled: false }} />
|
||||
</Stack.Protected>
|
||||
<Stack.Protected guard={signedIn}>
|
||||
{/* Reachable from onboarding too (scanning a friend's code, viewing a profile). */}
|
||||
<Stack.Screen name="u/[username]" options={{ title: '' }} />
|
||||
<Stack.Screen name="scan" options={{ title: 'Scan a code', presentation: 'modal' }} />
|
||||
</Stack.Protected>
|
||||
<Stack.Protected guard={!signedIn}>
|
||||
<Stack.Screen name="login" options={{ headerShown: false }} />
|
||||
|
||||
Reference in New Issue
Block a user