Make native web tabbar

This commit is contained in:
Zoe Roux 2026-02-16 12:01:59 +01:00
parent 2f5423073e
commit 637505dde9
No known key found for this signature in database
5 changed files with 109 additions and 76 deletions

View File

@ -1,14 +1,21 @@
import Browse from "@material-symbols/svg-400/rounded/browse-fill.svg";
// import Downloading from "@material-symbols/svg-400/rounded/downloading-fill.svg";
import Home from "@material-symbols/svg-400/rounded/home-fill.svg";
import { Tabs } from "expo-router";
import { Slot, Tabs } from "expo-router";
import { useTranslation } from "react-i18next";
import { Platform } from "react-native";
import { Icon } from "~/primitives";
import { cn } from "~/utils";
export const unstable_settings = {
initialRouteName: "index",
};
export default function TabsLayout() {
const { t } = useTranslation();
if (Platform.OS === "web") return <Slot />;
return (
<Tabs
screenOptions={{

View File

@ -1,10 +1,14 @@
import { Stack } from "expo-router";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { useCSSVariable, useResolveClassNames } from "uniwind";
import { NavbarRight, NavbarTitle } from "~/ui/navbar";
import { NavbarLeft, NavbarRight } from "~/ui/navbar";
export { ErrorBoundary } from "~/ui/error-bondary";
export const unstable_settings = {
initialRouteName: "(tabs)",
};
export default function Layout() {
const insets = useSafeAreaInsets();
const accent = useCSSVariable("--color-accent");
@ -13,7 +17,7 @@ export default function Layout() {
return (
<Stack
screenOptions={{
headerTitle: () => <NavbarTitle />,
headerTitle: () => <NavbarLeft />,
headerRight: () => <NavbarRight />,
contentStyle: {
paddingLeft: insets.left,

View File

@ -6,6 +6,10 @@ import "../global.css";
import { Tooltip, useMobileHover } from "~/primitives";
import "~/fonts.web.css";
export const unstable_settings = {
initialRouteName: "(app)",
};
export default function Layout() {
useMobileHover();

View File

@ -5,11 +5,11 @@ import {
Platform,
Pressable,
type PressableProps,
Text,
type TextProps,
} from "react-native";
import { useResolveClassNames } from "uniwind";
import { cn } from "~/utils";
import { P } from "./text";
export function useLinkTo({
href,
@ -57,7 +57,7 @@ export const A = ({
const linkProps = useLinkTo({ href, replace });
return (
<Text
<P
{...linkProps}
className={cn(
"select-text text-accent hover:underline focus:underline",
@ -66,7 +66,7 @@ export const A = ({
{...props}
>
{children}
</Text>
</P>
);
};

View File

@ -45,6 +45,24 @@ import { useAccount, useAccounts } from "~/providers/account-context";
import { logout } from "~/ui/login/logic";
import { cn } from "~/utils";
export const NavbarLeft = () => {
const { t } = useTranslation();
if (Platform.OS !== "web") return <NavbarTitle />;
return (
<View className="flex-row items-center gap-2">
<NavbarTitle />
<A
href="/browse"
className="font-headers text-lg text-slate-200 uppercase dark:text-slate-200"
>
{t("navbar.browse")}
</A>
</View>
);
};
export const NavbarTitle = ({
className,
...props
@ -64,68 +82,24 @@ export const NavbarTitle = ({
);
};
const getDisplayUrl = (url: string) => {
url = url.replace(/\/api$/, "");
url = url.replace(/https?:\/\//, "");
return url;
};
export const NavbarProfile = () => {
export const NavbarRight = () => {
const { t } = useTranslation();
const account = useAccount();
const accounts = useAccounts();
const isAdmin = false; //useHasPermission(AdminPage.requiredPermissions);
return (
<Menu
Trigger={Avatar<PressableProps>}
as={PressableFeedback}
src={account?.logo}
placeholder={account?.username}
alt={t("navbar.login")}
className="m-2"
{...tooltip(account?.username ?? t("navbar.login"))}
>
{accounts?.map((x) => (
<Menu.Item
key={x.id}
label={
Platform.OS === "web"
? x.username
: `${x.username} - ${getDisplayUrl(x.apiUrl)}`
}
left={
<Avatar placeholder={x.username} src={x.logo} className="mx-2" />
}
selected={x.selected}
onSelect={() => x.select()}
<View className="shrink flex-row items-center">
<SearchBar />
{isAdmin && (
<IconButton
icon={Admin}
as={Link}
href={"/admin"}
iconClassName="fill-slate-200 dark:fill-slate-200"
{...tooltip(t("navbar.admin"))}
/>
))}
{accounts.length > 0 && <HR />}
<Menu.Item label={t("misc.settings")} icon={Settings} href="/settings" />
{!account ? (
<>
<Menu.Item label={t("login.login")} icon={Login} href="/login" />
<Menu.Item
label={t("login.register")}
icon={Register}
href="/register"
/>
</>
) : (
<>
<Menu.Item
label={t("login.add-account")}
icon={Login}
href="/login"
/>
<Menu.Item
label={t("login.logout")}
icon={Logout}
onSelect={logout}
/>
</>
)}
</Menu>
<NavbarProfile />
</View>
);
};
@ -217,24 +191,68 @@ const SearchBar = () => {
);
};
export const NavbarRight = () => {
const getDisplayUrl = (url: string) => {
url = url.replace(/\/api$/, "");
url = url.replace(/https?:\/\//, "");
return url;
};
export const NavbarProfile = () => {
const { t } = useTranslation();
const isAdmin = false; //useHasPermission(AdminPage.requiredPermissions);
const account = useAccount();
const accounts = useAccounts();
return (
<View className="shrink flex-row items-center">
<SearchBar />
{isAdmin && (
<IconButton
icon={Admin}
as={Link}
href={"/admin"}
iconClassName="fill-slate-200 dark:fill-slate-200"
{...tooltip(t("navbar.admin"))}
<Menu
Trigger={Avatar<PressableProps>}
as={PressableFeedback}
src={account?.logo}
placeholder={account?.username}
alt={t("navbar.login")}
className="m-2"
{...tooltip(account?.username ?? t("navbar.login"))}
>
{accounts?.map((x) => (
<Menu.Item
key={x.id}
label={
Platform.OS === "web"
? x.username
: `${x.username} - ${getDisplayUrl(x.apiUrl)}`
}
left={
<Avatar placeholder={x.username} src={x.logo} className="mx-2" />
}
selected={x.selected}
onSelect={() => x.select()}
/>
))}
{accounts.length > 0 && <HR />}
<Menu.Item label={t("misc.settings")} icon={Settings} href="/settings" />
{!account ? (
<>
<Menu.Item label={t("login.login")} icon={Login} href="/login" />
<Menu.Item
label={t("login.register")}
icon={Register}
href="/register"
/>
</>
) : (
<>
<Menu.Item
label={t("login.add-account")}
icon={Login}
href="/login"
/>
<Menu.Item
label={t("login.logout")}
icon={Logout}
onSelect={logout}
/>
</>
)}
<NavbarProfile />
</View>
</Menu>
);
};