frontend client for gemstone. decentralised workplace app

refactor: move top bar file and modal

serenity 246027f9 990dddad

Changed files
+156 -43
src
components
-43
src/components/Navigation/TopBar.tsx
···
-
import { GmstnLogoColor } from "@/components/icons/gmstn/GmstnLogoColor";
-
import { useFacet } from "@/lib/facet";
-
import { useProfile } from "@/providers/authed/ProfileProvider";
-
import { useCurrentPalette } from "@/providers/ThemeProvider";
-
import { Image } from "expo-image";
-
import { Link } from "expo-router";
-
import { View } from "react-native";
-
-
export const TopBar = () => {
-
const { atoms } = useFacet();
-
const { profile } = useProfile();
-
const { semantic } = useCurrentPalette();
-
return (
-
<View
-
style={{
-
flexDirection: "row",
-
justifyContent: "space-between",
-
alignItems: "center",
-
paddingRight: 16,
-
paddingVertical: 4,
-
backgroundColor: semantic.backgroundDark,
-
boxShadow: atoms.boxShadows.sm,
-
zIndex: 2,
-
}}
-
>
-
<Link href="/">
-
<View style={{ padding: 8, paddingLeft: 12, paddingTop: 12 }}>
-
<GmstnLogoColor height={36} width={36} />
-
</View>
-
</Link>
-
{profile?.avatar && (
-
<Image
-
style={{
-
width: 42,
-
height: 42,
-
borderRadius: atoms.radii.full,
-
}}
-
source={{ uri: profile.avatar }}
-
/>
-
)}
-
</View>
-
);
-
};
+75
src/components/Navigation/TopBar/ProfileModalContent.tsx
···
+
import { Text } from "@/components/primitives/Text";
+
import { useFacet } from "@/lib/facet";
+
import { useCurrentPalette } from "@/providers/ThemeProvider";
+
import { Link } from "expo-router";
+
import type { StyleProp, TextStyle } from "react-native";
+
import { Pressable, View } from "react-native";
+
+
export const ProfileModalContent = () => {
+
const { semantic } = useCurrentPalette();
+
const { atoms } = useFacet();
+
+
const listItemStyles: StyleProp<TextStyle> = { paddingHorizontal: 16 };
+
+
return (
+
<View
+
style={{
+
marginTop: 64,
+
marginRight: 16,
+
backgroundColor: semantic.surface,
+
borderRadius: atoms.radii.lg,
+
display: "flex",
+
paddingVertical: 16,
+
gap: 4,
+
}}
+
>
+
<Link href="/profile" style={listItemStyles} asChild>
+
<LinkText label="Profile" />
+
</Link>
+
<Link href="/shards" style={listItemStyles}>
+
<LinkText label="Shards" />
+
</Link>
+
<Link href="/lattices" style={listItemStyles}>
+
<LinkText label="Lattices" />
+
</Link>
+
<Link href="/settings" style={listItemStyles}>
+
<LinkText label="Settings" />
+
</Link>
+
<Link href="/preferences" style={listItemStyles}>
+
<LinkText label="Preferences" />
+
</Link>
+
<Link href="/logout" style={listItemStyles}>
+
<LinkText
+
label="Log out"
+
style={{ color: semantic.negative }}
+
/>
+
</Link>
+
</View>
+
);
+
};
+
+
const LinkText = ({
+
label,
+
style,
+
...props
+
}: {
+
label: string;
+
style?: StyleProp<TextStyle>;
+
}) => {
+
return (
+
<Pressable {...props}>
+
{({ hovered }) => (
+
<Text
+
style={[
+
{
+
textDecorationLine: hovered ? "underline" : "none",
+
},
+
style,
+
]}
+
>
+
{label}
+
</Text>
+
)}
+
</Pressable>
+
);
+
};
+81
src/components/Navigation/TopBar/index.tsx
···
+
import { GmstnLogoColor } from "@/components/icons/gmstn/GmstnLogoColor";
+
import { ProfileModalContent } from "@/components/Navigation/TopBar/ProfileModalContent";
+
import { useFacet } from "@/lib/facet";
+
import { useProfile } from "@/providers/authed/ProfileProvider";
+
import { useCurrentPalette } from "@/providers/ThemeProvider";
+
import { Image } from "expo-image";
+
import { Link } from "expo-router";
+
import { useState } from "react";
+
import { Modal, Pressable, View } from "react-native";
+
+
export const TopBar = () => {
+
const { atoms } = useFacet();
+
const { profile } = useProfile();
+
const { semantic } = useCurrentPalette();
+
const [showProfileModal, setShowProfileModal] = useState(false);
+
+
return (
+
<View
+
style={{
+
flexDirection: "row",
+
justifyContent: "space-between",
+
alignItems: "center",
+
paddingRight: 16,
+
paddingVertical: 4,
+
backgroundColor: semantic.backgroundDark,
+
boxShadow: atoms.boxShadows.sm,
+
zIndex: 2,
+
}}
+
>
+
<Link href="/">
+
<View style={{ padding: 8, paddingLeft: 12, paddingTop: 12 }}>
+
<GmstnLogoColor height={36} width={36} />
+
</View>
+
</Link>
+
<Modal
+
visible={showProfileModal}
+
transparent
+
onRequestClose={() => {
+
setShowProfileModal(!showProfileModal);
+
}}
+
>
+
<Pressable
+
style={{ flex: 1, cursor: "auto" }}
+
onPress={() => {
+
setShowProfileModal(false);
+
}}
+
>
+
<Pressable
+
style={{
+
flex: 0,
+
cursor: "auto",
+
alignItems: "flex-end",
+
}}
+
onPress={(e) => {
+
e.stopPropagation();
+
}}
+
>
+
<ProfileModalContent />
+
</Pressable>
+
</Pressable>
+
</Modal>
+
<Pressable
+
hitSlop={20}
+
onPress={() => {
+
setShowProfileModal(true);
+
}}
+
>
+
{profile?.avatar && (
+
<Image
+
style={{
+
width: 42,
+
height: 42,
+
borderRadius: atoms.radii.full,
+
}}
+
source={{ uri: profile.avatar }}
+
/>
+
)}
+
</Pressable>
+
</View>
+
);
+
};