feat: Introduce Alerts, Clips, and Settings tabs, update theme colors, and enhance ThemedView, ThemedText, and IconSymbol components.
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import { Tabs } from 'expo-router';
|
import { Tabs } from 'expo-router';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { Platform } from 'react-native';
|
||||||
|
|
||||||
import { HapticTab } from '@/components/haptic-tab';
|
import { HapticTab } from '@/components/haptic-tab';
|
||||||
import { IconSymbol } from '@/components/ui/icon-symbol';
|
import { IconSymbol } from '@/components/ui/icon-symbol';
|
||||||
@@ -13,8 +14,18 @@ export default function TabLayout() {
|
|||||||
<Tabs
|
<Tabs
|
||||||
screenOptions={{
|
screenOptions={{
|
||||||
tabBarActiveTintColor: Colors[colorScheme ?? 'light'].tint,
|
tabBarActiveTintColor: Colors[colorScheme ?? 'light'].tint,
|
||||||
|
tabBarInactiveTintColor: Colors[colorScheme ?? 'light'].tabIconDefault,
|
||||||
headerShown: false,
|
headerShown: false,
|
||||||
tabBarButton: HapticTab,
|
tabBarButton: HapticTab,
|
||||||
|
tabBarStyle: Platform.select({
|
||||||
|
default: {
|
||||||
|
backgroundColor: Colors[colorScheme ?? 'light'].background,
|
||||||
|
borderTopColor: Colors[colorScheme ?? 'light'].border,
|
||||||
|
borderTopWidth: 1,
|
||||||
|
elevation: 0,
|
||||||
|
paddingTop: 8,
|
||||||
|
},
|
||||||
|
}),
|
||||||
}}>
|
}}>
|
||||||
<Tabs.Screen
|
<Tabs.Screen
|
||||||
name="index"
|
name="index"
|
||||||
@@ -24,10 +35,24 @@ export default function TabLayout() {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Tabs.Screen
|
<Tabs.Screen
|
||||||
name="explore"
|
name="alerts"
|
||||||
options={{
|
options={{
|
||||||
title: 'Explore',
|
title: 'Alerts',
|
||||||
tabBarIcon: ({ color }) => <IconSymbol size={28} name="paperplane.fill" color={color} />,
|
tabBarIcon: ({ color }) => <IconSymbol size={28} name="bell.fill" color={color} />,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Tabs.Screen
|
||||||
|
name="clips"
|
||||||
|
options={{
|
||||||
|
title: 'Clips',
|
||||||
|
tabBarIcon: ({ color }) => <IconSymbol size={28} name="film.fill" color={color} />,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Tabs.Screen
|
||||||
|
name="settings"
|
||||||
|
options={{
|
||||||
|
title: 'Settings',
|
||||||
|
tabBarIcon: ({ color }) => <IconSymbol size={28} name="gearshape.fill" color={color} />,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|||||||
65
MobileApp/app/(tabs)/alerts.tsx
Normal file
65
MobileApp/app/(tabs)/alerts.tsx
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
import { StyleSheet } from 'react-native';
|
||||||
|
|
||||||
|
import { ThemedText } from '@/components/themed-text';
|
||||||
|
import { ThemedView } from '@/components/themed-view';
|
||||||
|
import { IconSymbol } from '@/components/ui/icon-symbol';
|
||||||
|
import { Colors } from '@/constants/theme';
|
||||||
|
import { useColorScheme } from '@/hooks/use-color-scheme';
|
||||||
|
|
||||||
|
export default function AlertsScreen() {
|
||||||
|
const colorScheme = useColorScheme();
|
||||||
|
const theme = colorScheme ?? 'light';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ThemedView style={styles.container}>
|
||||||
|
<ThemedView style={styles.header}>
|
||||||
|
<ThemedText type="title">Alerts</ThemedText>
|
||||||
|
<ThemedText type="secondary">Recent security activity</ThemedText>
|
||||||
|
</ThemedView>
|
||||||
|
|
||||||
|
<ThemedView
|
||||||
|
style={[styles.card, {
|
||||||
|
shadowColor: Colors[theme].border,
|
||||||
|
borderColor: Colors[theme].border
|
||||||
|
}]}
|
||||||
|
variant="card"
|
||||||
|
>
|
||||||
|
<IconSymbol size={48} name="bell.fill" color={Colors[theme].icon} />
|
||||||
|
<ThemedText type="subtitle" style={styles.cardTitle}>No Alerts</ThemedText>
|
||||||
|
<ThemedText style={styles.cardDescription} type="secondary">
|
||||||
|
You have no new security alerts.
|
||||||
|
</ThemedText>
|
||||||
|
</ThemedView>
|
||||||
|
</ThemedView>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
padding: 20,
|
||||||
|
paddingTop: 80,
|
||||||
|
},
|
||||||
|
header: {
|
||||||
|
marginBottom: 32,
|
||||||
|
},
|
||||||
|
card: {
|
||||||
|
padding: 32,
|
||||||
|
borderRadius: 16,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
borderWidth: 1,
|
||||||
|
shadowOffset: { width: 0, height: 4 },
|
||||||
|
shadowOpacity: 0.1,
|
||||||
|
shadowRadius: 12,
|
||||||
|
elevation: 2,
|
||||||
|
},
|
||||||
|
cardTitle: {
|
||||||
|
marginTop: 16,
|
||||||
|
marginBottom: 8,
|
||||||
|
},
|
||||||
|
cardDescription: {
|
||||||
|
textAlign: 'center',
|
||||||
|
maxWidth: 240,
|
||||||
|
},
|
||||||
|
});
|
||||||
65
MobileApp/app/(tabs)/clips.tsx
Normal file
65
MobileApp/app/(tabs)/clips.tsx
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
import { StyleSheet } from 'react-native';
|
||||||
|
|
||||||
|
import { ThemedText } from '@/components/themed-text';
|
||||||
|
import { ThemedView } from '@/components/themed-view';
|
||||||
|
import { IconSymbol } from '@/components/ui/icon-symbol';
|
||||||
|
import { Colors } from '@/constants/theme';
|
||||||
|
import { useColorScheme } from '@/hooks/use-color-scheme';
|
||||||
|
|
||||||
|
export default function ClipsScreen() {
|
||||||
|
const colorScheme = useColorScheme();
|
||||||
|
const theme = colorScheme ?? 'light';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ThemedView style={styles.container}>
|
||||||
|
<ThemedView style={styles.header}>
|
||||||
|
<ThemedText type="title">Clips</ThemedText>
|
||||||
|
<ThemedText type="secondary">Recorded footage</ThemedText>
|
||||||
|
</ThemedView>
|
||||||
|
|
||||||
|
<ThemedView
|
||||||
|
style={[styles.card, {
|
||||||
|
shadowColor: Colors[theme].border,
|
||||||
|
borderColor: Colors[theme].border
|
||||||
|
}]}
|
||||||
|
variant="card"
|
||||||
|
>
|
||||||
|
<IconSymbol size={48} name="film.fill" color={Colors[theme].icon} />
|
||||||
|
<ThemedText type="subtitle" style={styles.cardTitle}>No Clips Available</ThemedText>
|
||||||
|
<ThemedText style={styles.cardDescription} type="secondary">
|
||||||
|
Recorded clips will appear here.
|
||||||
|
</ThemedText>
|
||||||
|
</ThemedView>
|
||||||
|
</ThemedView>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
padding: 20,
|
||||||
|
paddingTop: 80,
|
||||||
|
},
|
||||||
|
header: {
|
||||||
|
marginBottom: 32,
|
||||||
|
},
|
||||||
|
card: {
|
||||||
|
padding: 32,
|
||||||
|
borderRadius: 16,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
borderWidth: 1,
|
||||||
|
shadowOffset: { width: 0, height: 4 },
|
||||||
|
shadowOpacity: 0.1,
|
||||||
|
shadowRadius: 12,
|
||||||
|
elevation: 2,
|
||||||
|
},
|
||||||
|
cardTitle: {
|
||||||
|
marginTop: 16,
|
||||||
|
marginBottom: 8,
|
||||||
|
},
|
||||||
|
cardDescription: {
|
||||||
|
textAlign: 'center',
|
||||||
|
maxWidth: 240,
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -1,112 +0,0 @@
|
|||||||
import { Image } from 'expo-image';
|
|
||||||
import { Platform, StyleSheet } from 'react-native';
|
|
||||||
|
|
||||||
import { Collapsible } from '@/components/ui/collapsible';
|
|
||||||
import { ExternalLink } from '@/components/external-link';
|
|
||||||
import ParallaxScrollView from '@/components/parallax-scroll-view';
|
|
||||||
import { ThemedText } from '@/components/themed-text';
|
|
||||||
import { ThemedView } from '@/components/themed-view';
|
|
||||||
import { IconSymbol } from '@/components/ui/icon-symbol';
|
|
||||||
import { Fonts } from '@/constants/theme';
|
|
||||||
|
|
||||||
export default function TabTwoScreen() {
|
|
||||||
return (
|
|
||||||
<ParallaxScrollView
|
|
||||||
headerBackgroundColor={{ light: '#D0D0D0', dark: '#353636' }}
|
|
||||||
headerImage={
|
|
||||||
<IconSymbol
|
|
||||||
size={310}
|
|
||||||
color="#808080"
|
|
||||||
name="chevron.left.forwardslash.chevron.right"
|
|
||||||
style={styles.headerImage}
|
|
||||||
/>
|
|
||||||
}>
|
|
||||||
<ThemedView style={styles.titleContainer}>
|
|
||||||
<ThemedText
|
|
||||||
type="title"
|
|
||||||
style={{
|
|
||||||
fontFamily: Fonts.rounded,
|
|
||||||
}}>
|
|
||||||
Explore
|
|
||||||
</ThemedText>
|
|
||||||
</ThemedView>
|
|
||||||
<ThemedText>This app includes example code to help you get started.</ThemedText>
|
|
||||||
<Collapsible title="File-based routing">
|
|
||||||
<ThemedText>
|
|
||||||
This app has two screens:{' '}
|
|
||||||
<ThemedText type="defaultSemiBold">app/(tabs)/index.tsx</ThemedText> and{' '}
|
|
||||||
<ThemedText type="defaultSemiBold">app/(tabs)/explore.tsx</ThemedText>
|
|
||||||
</ThemedText>
|
|
||||||
<ThemedText>
|
|
||||||
The layout file in <ThemedText type="defaultSemiBold">app/(tabs)/_layout.tsx</ThemedText>{' '}
|
|
||||||
sets up the tab navigator.
|
|
||||||
</ThemedText>
|
|
||||||
<ExternalLink href="https://docs.expo.dev/router/introduction">
|
|
||||||
<ThemedText type="link">Learn more</ThemedText>
|
|
||||||
</ExternalLink>
|
|
||||||
</Collapsible>
|
|
||||||
<Collapsible title="Android, iOS, and web support">
|
|
||||||
<ThemedText>
|
|
||||||
You can open this project on Android, iOS, and the web. To open the web version, press{' '}
|
|
||||||
<ThemedText type="defaultSemiBold">w</ThemedText> in the terminal running this project.
|
|
||||||
</ThemedText>
|
|
||||||
</Collapsible>
|
|
||||||
<Collapsible title="Images">
|
|
||||||
<ThemedText>
|
|
||||||
For static images, you can use the <ThemedText type="defaultSemiBold">@2x</ThemedText> and{' '}
|
|
||||||
<ThemedText type="defaultSemiBold">@3x</ThemedText> suffixes to provide files for
|
|
||||||
different screen densities
|
|
||||||
</ThemedText>
|
|
||||||
<Image
|
|
||||||
source={require('@/assets/images/react-logo.png')}
|
|
||||||
style={{ width: 100, height: 100, alignSelf: 'center' }}
|
|
||||||
/>
|
|
||||||
<ExternalLink href="https://reactnative.dev/docs/images">
|
|
||||||
<ThemedText type="link">Learn more</ThemedText>
|
|
||||||
</ExternalLink>
|
|
||||||
</Collapsible>
|
|
||||||
<Collapsible title="Light and dark mode components">
|
|
||||||
<ThemedText>
|
|
||||||
This template has light and dark mode support. The{' '}
|
|
||||||
<ThemedText type="defaultSemiBold">useColorScheme()</ThemedText> hook lets you inspect
|
|
||||||
what the user's current color scheme is, and so you can adjust UI colors accordingly.
|
|
||||||
</ThemedText>
|
|
||||||
<ExternalLink href="https://docs.expo.dev/develop/user-interface/color-themes/">
|
|
||||||
<ThemedText type="link">Learn more</ThemedText>
|
|
||||||
</ExternalLink>
|
|
||||||
</Collapsible>
|
|
||||||
<Collapsible title="Animations">
|
|
||||||
<ThemedText>
|
|
||||||
This template includes an example of an animated component. The{' '}
|
|
||||||
<ThemedText type="defaultSemiBold">components/HelloWave.tsx</ThemedText> component uses
|
|
||||||
the powerful{' '}
|
|
||||||
<ThemedText type="defaultSemiBold" style={{ fontFamily: Fonts.mono }}>
|
|
||||||
react-native-reanimated
|
|
||||||
</ThemedText>{' '}
|
|
||||||
library to create a waving hand animation.
|
|
||||||
</ThemedText>
|
|
||||||
{Platform.select({
|
|
||||||
ios: (
|
|
||||||
<ThemedText>
|
|
||||||
The <ThemedText type="defaultSemiBold">components/ParallaxScrollView.tsx</ThemedText>{' '}
|
|
||||||
component provides a parallax effect for the header image.
|
|
||||||
</ThemedText>
|
|
||||||
),
|
|
||||||
})}
|
|
||||||
</Collapsible>
|
|
||||||
</ParallaxScrollView>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
headerImage: {
|
|
||||||
color: '#808080',
|
|
||||||
bottom: -90,
|
|
||||||
left: -35,
|
|
||||||
position: 'absolute',
|
|
||||||
},
|
|
||||||
titleContainer: {
|
|
||||||
flexDirection: 'row',
|
|
||||||
gap: 8,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -1,98 +1,67 @@
|
|||||||
import { Image } from 'expo-image';
|
import { StyleSheet } from 'react-native';
|
||||||
import { Platform, StyleSheet } from 'react-native';
|
|
||||||
|
|
||||||
import { HelloWave } from '@/components/hello-wave';
|
|
||||||
import ParallaxScrollView from '@/components/parallax-scroll-view';
|
|
||||||
import { ThemedText } from '@/components/themed-text';
|
import { ThemedText } from '@/components/themed-text';
|
||||||
import { ThemedView } from '@/components/themed-view';
|
import { ThemedView } from '@/components/themed-view';
|
||||||
import { Link } from 'expo-router';
|
import { IconSymbol } from '@/components/ui/icon-symbol';
|
||||||
|
import { Colors } from '@/constants/theme';
|
||||||
|
import { useColorScheme } from '@/hooks/use-color-scheme';
|
||||||
|
|
||||||
export default function HomeScreen() {
|
export default function HomeScreen() {
|
||||||
return (
|
const colorScheme = useColorScheme();
|
||||||
<ParallaxScrollView
|
const theme = colorScheme ?? 'light';
|
||||||
headerBackgroundColor={{ light: '#A1CEDC', dark: '#1D3D47' }}
|
|
||||||
headerImage={
|
|
||||||
<Image
|
|
||||||
source={require('@/assets/images/partial-react-logo.png')}
|
|
||||||
style={styles.reactLogo}
|
|
||||||
/>
|
|
||||||
}>
|
|
||||||
<ThemedView style={styles.titleContainer}>
|
|
||||||
<ThemedText type="title">Welcome!</ThemedText>
|
|
||||||
<HelloWave />
|
|
||||||
</ThemedView>
|
|
||||||
<ThemedView style={styles.stepContainer}>
|
|
||||||
<ThemedText type="subtitle">Step 1: Try it</ThemedText>
|
|
||||||
<ThemedText>
|
|
||||||
Edit <ThemedText type="defaultSemiBold">app/(tabs)/index.tsx</ThemedText> to see changes.
|
|
||||||
Press{' '}
|
|
||||||
<ThemedText type="defaultSemiBold">
|
|
||||||
{Platform.select({
|
|
||||||
ios: 'cmd + d',
|
|
||||||
android: 'cmd + m',
|
|
||||||
web: 'F12',
|
|
||||||
})}
|
|
||||||
</ThemedText>{' '}
|
|
||||||
to open developer tools.
|
|
||||||
</ThemedText>
|
|
||||||
</ThemedView>
|
|
||||||
<ThemedView style={styles.stepContainer}>
|
|
||||||
<Link href="/modal">
|
|
||||||
<Link.Trigger>
|
|
||||||
<ThemedText type="subtitle">Step 2: Explore</ThemedText>
|
|
||||||
</Link.Trigger>
|
|
||||||
<Link.Preview />
|
|
||||||
<Link.Menu>
|
|
||||||
<Link.MenuAction title="Action" icon="cube" onPress={() => alert('Action pressed')} />
|
|
||||||
<Link.MenuAction
|
|
||||||
title="Share"
|
|
||||||
icon="square.and.arrow.up"
|
|
||||||
onPress={() => alert('Share pressed')}
|
|
||||||
/>
|
|
||||||
<Link.Menu title="More" icon="ellipsis">
|
|
||||||
<Link.MenuAction
|
|
||||||
title="Delete"
|
|
||||||
icon="trash"
|
|
||||||
destructive
|
|
||||||
onPress={() => alert('Delete pressed')}
|
|
||||||
/>
|
|
||||||
</Link.Menu>
|
|
||||||
</Link.Menu>
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
<ThemedText>
|
return (
|
||||||
{`Tap the Explore tab to learn more about what's included in this starter app.`}
|
<ThemedView style={styles.container}>
|
||||||
|
<ThemedView style={styles.header}>
|
||||||
|
<ThemedText type="title">Your Cameras</ThemedText>
|
||||||
|
<ThemedText type="secondary">Manage your indoor security devices</ThemedText>
|
||||||
|
</ThemedView>
|
||||||
|
|
||||||
|
<ThemedView
|
||||||
|
style={[styles.card, {
|
||||||
|
shadowColor: Colors[theme].border,
|
||||||
|
borderColor: Colors[theme].border
|
||||||
|
}]}
|
||||||
|
variant="card"
|
||||||
|
>
|
||||||
|
<IconSymbol size={48} name="house.fill" color={Colors[theme].icon} />
|
||||||
|
<ThemedText type="subtitle" style={styles.cardTitle}>No Cameras Added</ThemedText>
|
||||||
|
<ThemedText style={styles.cardDescription} type="secondary">
|
||||||
|
Add a camera to start monitoring your home.
|
||||||
</ThemedText>
|
</ThemedText>
|
||||||
</ThemedView>
|
</ThemedView>
|
||||||
<ThemedView style={styles.stepContainer}>
|
</ThemedView>
|
||||||
<ThemedText type="subtitle">Step 3: Get a fresh start</ThemedText>
|
|
||||||
<ThemedText>
|
|
||||||
{`When you're ready, run `}
|
|
||||||
<ThemedText type="defaultSemiBold">npm run reset-project</ThemedText> to get a fresh{' '}
|
|
||||||
<ThemedText type="defaultSemiBold">app</ThemedText> directory. This will move the current{' '}
|
|
||||||
<ThemedText type="defaultSemiBold">app</ThemedText> to{' '}
|
|
||||||
<ThemedText type="defaultSemiBold">app-example</ThemedText>.
|
|
||||||
</ThemedText>
|
|
||||||
</ThemedView>
|
|
||||||
</ParallaxScrollView>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
titleContainer: {
|
container: {
|
||||||
flexDirection: 'row',
|
flex: 1,
|
||||||
alignItems: 'center',
|
padding: 20,
|
||||||
gap: 8,
|
paddingTop: 80,
|
||||||
},
|
},
|
||||||
stepContainer: {
|
header: {
|
||||||
gap: 8,
|
marginBottom: 32,
|
||||||
|
},
|
||||||
|
card: {
|
||||||
|
padding: 32,
|
||||||
|
borderRadius: 16,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
borderWidth: 1,
|
||||||
|
// iOS Shadow
|
||||||
|
shadowOffset: { width: 0, height: 4 },
|
||||||
|
shadowOpacity: 0.1,
|
||||||
|
shadowRadius: 12,
|
||||||
|
// Android Shadow
|
||||||
|
elevation: 2,
|
||||||
|
},
|
||||||
|
cardTitle: {
|
||||||
|
marginTop: 16,
|
||||||
marginBottom: 8,
|
marginBottom: 8,
|
||||||
},
|
},
|
||||||
reactLogo: {
|
cardDescription: {
|
||||||
height: 178,
|
textAlign: 'center',
|
||||||
width: 290,
|
maxWidth: 240,
|
||||||
bottom: 0,
|
|
||||||
left: 0,
|
|
||||||
position: 'absolute',
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
78
MobileApp/app/(tabs)/settings.tsx
Normal file
78
MobileApp/app/(tabs)/settings.tsx
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
import { StyleSheet } from 'react-native';
|
||||||
|
|
||||||
|
import { ThemedText } from '@/components/themed-text';
|
||||||
|
import { ThemedView } from '@/components/themed-view';
|
||||||
|
import { Colors } from '@/constants/theme';
|
||||||
|
import { useColorScheme } from '@/hooks/use-color-scheme';
|
||||||
|
|
||||||
|
export default function SettingsScreen() {
|
||||||
|
const colorScheme = useColorScheme();
|
||||||
|
const theme = colorScheme ?? 'light';
|
||||||
|
|
||||||
|
const cardStyle = [
|
||||||
|
styles.card,
|
||||||
|
{
|
||||||
|
shadowColor: Colors[theme].border,
|
||||||
|
borderColor: Colors[theme].border
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ThemedView style={styles.container}>
|
||||||
|
<ThemedView style={styles.header}>
|
||||||
|
<ThemedText type="title">Settings</ThemedText>
|
||||||
|
<ThemedText type="secondary">App preferences</ThemedText>
|
||||||
|
</ThemedView>
|
||||||
|
|
||||||
|
<ThemedView style={styles.sectionHeader}>
|
||||||
|
<ThemedText type="subtitle">Account</ThemedText>
|
||||||
|
</ThemedView>
|
||||||
|
<ThemedView style={cardStyle} variant="card">
|
||||||
|
<ThemedText style={styles.item}>Profile</ThemedText>
|
||||||
|
<ThemedText style={styles.item}>Notifications</ThemedText>
|
||||||
|
<ThemedText style={[styles.item, styles.lastItem]}>Privacy</ThemedText>
|
||||||
|
</ThemedView>
|
||||||
|
|
||||||
|
<ThemedView style={styles.sectionHeader}>
|
||||||
|
<ThemedText type="subtitle">General</ThemedText>
|
||||||
|
</ThemedView>
|
||||||
|
<ThemedView style={cardStyle} variant="card">
|
||||||
|
<ThemedText style={styles.item}>About</ThemedText>
|
||||||
|
<ThemedText style={styles.item}>Help & Support</ThemedText>
|
||||||
|
<ThemedText style={[styles.item, styles.lastItem]}>Sign Out</ThemedText>
|
||||||
|
</ThemedView>
|
||||||
|
</ThemedView>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
padding: 20,
|
||||||
|
paddingTop: 80,
|
||||||
|
},
|
||||||
|
header: {
|
||||||
|
marginBottom: 32,
|
||||||
|
},
|
||||||
|
sectionHeader: {
|
||||||
|
marginBottom: 12,
|
||||||
|
marginTop: 24,
|
||||||
|
},
|
||||||
|
card: {
|
||||||
|
borderRadius: 16,
|
||||||
|
borderWidth: 1,
|
||||||
|
shadowOffset: { width: 0, height: 2 },
|
||||||
|
shadowOpacity: 0.05,
|
||||||
|
shadowRadius: 8,
|
||||||
|
elevation: 2,
|
||||||
|
overflow: 'hidden',
|
||||||
|
},
|
||||||
|
item: {
|
||||||
|
padding: 16,
|
||||||
|
borderBottomWidth: StyleSheet.hairlineWidth,
|
||||||
|
borderBottomColor: 'rgba(0,0,0,0.05)',
|
||||||
|
},
|
||||||
|
lastItem: {
|
||||||
|
borderBottomWidth: 0,
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -5,7 +5,7 @@ import { useThemeColor } from '@/hooks/use-theme-color';
|
|||||||
export type ThemedTextProps = TextProps & {
|
export type ThemedTextProps = TextProps & {
|
||||||
lightColor?: string;
|
lightColor?: string;
|
||||||
darkColor?: string;
|
darkColor?: string;
|
||||||
type?: 'default' | 'title' | 'defaultSemiBold' | 'subtitle' | 'link';
|
type?: 'default' | 'title' | 'defaultSemiBold' | 'subtitle' | 'link' | 'secondary';
|
||||||
};
|
};
|
||||||
|
|
||||||
export function ThemedText({
|
export function ThemedText({
|
||||||
@@ -15,7 +15,10 @@ export function ThemedText({
|
|||||||
type = 'default',
|
type = 'default',
|
||||||
...rest
|
...rest
|
||||||
}: ThemedTextProps) {
|
}: ThemedTextProps) {
|
||||||
const color = useThemeColor({ light: lightColor, dark: darkColor }, 'text');
|
const color = useThemeColor(
|
||||||
|
{ light: lightColor, dark: darkColor },
|
||||||
|
type === 'secondary' ? 'textSecondary' : 'text'
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Text
|
<Text
|
||||||
@@ -26,6 +29,7 @@ export function ThemedText({
|
|||||||
type === 'defaultSemiBold' ? styles.defaultSemiBold : undefined,
|
type === 'defaultSemiBold' ? styles.defaultSemiBold : undefined,
|
||||||
type === 'subtitle' ? styles.subtitle : undefined,
|
type === 'subtitle' ? styles.subtitle : undefined,
|
||||||
type === 'link' ? styles.link : undefined,
|
type === 'link' ? styles.link : undefined,
|
||||||
|
type === 'secondary' ? styles.secondary : undefined,
|
||||||
style,
|
style,
|
||||||
]}
|
]}
|
||||||
{...rest}
|
{...rest}
|
||||||
@@ -55,6 +59,10 @@ const styles = StyleSheet.create({
|
|||||||
link: {
|
link: {
|
||||||
lineHeight: 30,
|
lineHeight: 30,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
color: '#0a7ea4',
|
color: '#635bff', // Stripe blurple
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
fontSize: 14,
|
||||||
|
lineHeight: 22,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,10 +5,11 @@ import { useThemeColor } from '@/hooks/use-theme-color';
|
|||||||
export type ThemedViewProps = ViewProps & {
|
export type ThemedViewProps = ViewProps & {
|
||||||
lightColor?: string;
|
lightColor?: string;
|
||||||
darkColor?: string;
|
darkColor?: string;
|
||||||
|
variant?: 'default' | 'card';
|
||||||
};
|
};
|
||||||
|
|
||||||
export function ThemedView({ style, lightColor, darkColor, ...otherProps }: ThemedViewProps) {
|
export function ThemedView({ style, lightColor, darkColor, variant = 'default', ...otherProps }: ThemedViewProps) {
|
||||||
const backgroundColor = useThemeColor({ light: lightColor, dark: darkColor }, 'background');
|
const backgroundColor = useThemeColor({ light: lightColor, dark: darkColor }, variant === 'card' ? 'card' : 'background');
|
||||||
|
|
||||||
return <View style={[{ backgroundColor }, style]} {...otherProps} />;
|
return <View style={[{ backgroundColor }, style]} {...otherProps} />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// Fallback for using MaterialIcons on Android and web.
|
// Fallback for using MaterialIcons on Android and web.
|
||||||
|
|
||||||
import MaterialIcons from '@expo/vector-icons/MaterialIcons';
|
import MaterialIcons from '@expo/vector-icons/MaterialIcons';
|
||||||
import { SymbolWeight, SymbolViewProps } from 'expo-symbols';
|
import { SymbolViewProps, SymbolWeight } from 'expo-symbols';
|
||||||
import { ComponentProps } from 'react';
|
import { ComponentProps } from 'react';
|
||||||
import { OpaqueColorValue, type StyleProp, type TextStyle } from 'react-native';
|
import { OpaqueColorValue, type StyleProp, type TextStyle } from 'react-native';
|
||||||
|
|
||||||
@@ -18,6 +18,9 @@ const MAPPING = {
|
|||||||
'paperplane.fill': 'send',
|
'paperplane.fill': 'send',
|
||||||
'chevron.left.forwardslash.chevron.right': 'code',
|
'chevron.left.forwardslash.chevron.right': 'code',
|
||||||
'chevron.right': 'chevron-right',
|
'chevron.right': 'chevron-right',
|
||||||
|
'bell.fill': 'notifications',
|
||||||
|
'film.fill': 'video-library',
|
||||||
|
'gearshape.fill': 'settings',
|
||||||
} as IconMapping;
|
} as IconMapping;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -5,25 +5,31 @@
|
|||||||
|
|
||||||
import { Platform } from 'react-native';
|
import { Platform } from 'react-native';
|
||||||
|
|
||||||
const tintColorLight = '#0a7ea4';
|
const tintColorLight = '#635bff';
|
||||||
const tintColorDark = '#fff';
|
const tintColorDark = '#fff';
|
||||||
|
|
||||||
export const Colors = {
|
export const Colors = {
|
||||||
light: {
|
light: {
|
||||||
text: '#11181C',
|
text: '#0a2540',
|
||||||
background: '#fff',
|
textSecondary: '#425466',
|
||||||
|
background: '#f6f9fc',
|
||||||
tint: tintColorLight,
|
tint: tintColorLight,
|
||||||
icon: '#687076',
|
icon: '#8898aa',
|
||||||
tabIconDefault: '#687076',
|
tabIconDefault: '#8898aa',
|
||||||
tabIconSelected: tintColorLight,
|
tabIconSelected: tintColorLight,
|
||||||
|
card: '#ffffff',
|
||||||
|
border: '#e6ebf1',
|
||||||
},
|
},
|
||||||
dark: {
|
dark: {
|
||||||
text: '#ECEDEE',
|
text: '#ECEDEE',
|
||||||
|
textSecondary: '#9BA1A6',
|
||||||
background: '#151718',
|
background: '#151718',
|
||||||
tint: tintColorDark,
|
tint: tintColorDark,
|
||||||
icon: '#9BA1A6',
|
icon: '#9BA1A6',
|
||||||
tabIconDefault: '#9BA1A6',
|
tabIconDefault: '#9BA1A6',
|
||||||
tabIconSelected: tintColorDark,
|
tabIconSelected: tintColorDark,
|
||||||
|
card: '#232526', // Fallback for dark mode
|
||||||
|
border: '#333333',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user