Add beenvoice mobile companion app with full dark mode support.
Expo app with dashboard, time clock, invoices, and settings — native tabs, glass UI, theme-aware components, and iOS Live Activities. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
import { BlurView } from "expo-blur";
|
||||
import type { ReactNode } from "react";
|
||||
import { Platform, StyleSheet, View, type StyleProp, type ViewStyle } from "react-native";
|
||||
|
||||
import { useAppTheme } from "@/contexts/ThemeContext";
|
||||
import { blurIntensity, radius, shadowMd, shadowSm } from "@/lib/beenvoice-theme";
|
||||
|
||||
type GlassSurfaceProps = {
|
||||
children: ReactNode;
|
||||
style?: StyleProp<ViewStyle>;
|
||||
radius?: number;
|
||||
variant?: "card" | "stat";
|
||||
};
|
||||
|
||||
export function GlassSurface({
|
||||
children,
|
||||
style,
|
||||
radius: cornerRadius = radius.lg,
|
||||
variant = "card",
|
||||
}: GlassSurfaceProps) {
|
||||
const { colors, isDark } = useAppTheme();
|
||||
const flat = StyleSheet.flatten(style);
|
||||
const isStat = variant === "stat";
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
styles.shell,
|
||||
isStat ? styles.statShell : null,
|
||||
{ borderRadius: cornerRadius, borderColor: colors.borderGlass },
|
||||
isStat ? shadowMd : shadowSm,
|
||||
flat,
|
||||
Platform.OS === "android" ? { backgroundColor: colors.cardGlass } : null,
|
||||
]}
|
||||
>
|
||||
{Platform.OS === "ios" ? (
|
||||
<BlurView
|
||||
intensity={blurIntensity.card}
|
||||
tint={isDark ? "dark" : "light"}
|
||||
style={[StyleSheet.absoluteFill, { borderRadius: cornerRadius }]}
|
||||
/>
|
||||
) : null}
|
||||
<View
|
||||
pointerEvents="none"
|
||||
style={[
|
||||
styles.fill,
|
||||
{ backgroundColor: colors.cardGlass, borderRadius: cornerRadius },
|
||||
]}
|
||||
/>
|
||||
<View style={styles.content}>{children}</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
export function GlassChrome({
|
||||
children,
|
||||
style,
|
||||
radius: cornerRadius = 0,
|
||||
}: {
|
||||
children?: ReactNode;
|
||||
style?: StyleProp<ViewStyle>;
|
||||
radius?: number;
|
||||
}) {
|
||||
const { colors, isDark } = useAppTheme();
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
styles.chromeShell,
|
||||
{ borderRadius: cornerRadius, backgroundColor: colors.cardGlass },
|
||||
StyleSheet.flatten(style),
|
||||
]}
|
||||
>
|
||||
{Platform.OS === "ios" ? (
|
||||
<BlurView
|
||||
intensity={blurIntensity.chrome}
|
||||
tint={isDark ? "dark" : "light"}
|
||||
style={[StyleSheet.absoluteFill, { borderRadius: cornerRadius }]}
|
||||
/>
|
||||
) : null}
|
||||
<View
|
||||
pointerEvents="none"
|
||||
style={[
|
||||
styles.fill,
|
||||
{ backgroundColor: colors.cardGlass, borderRadius: cornerRadius },
|
||||
]}
|
||||
/>
|
||||
{children ? <View style={styles.content}>{children}</View> : null}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
shell: {
|
||||
overflow: "hidden",
|
||||
borderWidth: StyleSheet.hairlineWidth * 2,
|
||||
backgroundColor: "transparent",
|
||||
},
|
||||
statShell: {
|
||||
borderWidth: 0,
|
||||
},
|
||||
chromeShell: {
|
||||
overflow: "hidden",
|
||||
},
|
||||
fill: {
|
||||
...StyleSheet.absoluteFill,
|
||||
},
|
||||
content: {
|
||||
position: "relative",
|
||||
zIndex: 2,
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user