diff --git a/apps/mobile/app/(app)/[workspace]/more/favorites.tsx b/apps/mobile/app/(app)/[workspace]/more/favorites.tsx
deleted file mode 100644
index 5596466a1..000000000
--- a/apps/mobile/app/(app)/[workspace]/more/favorites.tsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import { View } from "react-native";
-import { Text } from "@/components/ui/text";
-
-/**
- * Favorites placeholder. Real implementation deferred — list of pinned
- * issues / projects / views, mirroring the web Favorites surface.
- */
-export default function FavoritesPage() {
- return (
-
-
- Favorites coming soon.
-
-
- );
-}
diff --git a/apps/mobile/app/(app)/[workspace]/more/initiatives.tsx b/apps/mobile/app/(app)/[workspace]/more/initiatives.tsx
deleted file mode 100644
index 5fd2c50aa..000000000
--- a/apps/mobile/app/(app)/[workspace]/more/initiatives.tsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import { View } from "react-native";
-import { Text } from "@/components/ui/text";
-
-/**
- * Initiatives placeholder. Read-only list of workspace initiatives, filled
- * in a later phase to mirror the web Initiatives surface.
- */
-export default function InitiativesPage() {
- return (
-
-
- Initiatives coming soon.
-
-
- );
-}
diff --git a/apps/mobile/app/(app)/[workspace]/more/notifications.tsx b/apps/mobile/app/(app)/[workspace]/more/notifications.tsx
deleted file mode 100644
index ed9f3eb40..000000000
--- a/apps/mobile/app/(app)/[workspace]/more/notifications.tsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import { View } from "react-native";
-import { Text } from "@/components/ui/text";
-
-export default function NotificationsPage() {
- return (
-
-
- Notification preferences coming soon.
-
-
- );
-}
diff --git a/apps/mobile/app/(app)/[workspace]/more/pins.tsx b/apps/mobile/app/(app)/[workspace]/more/pins.tsx
deleted file mode 100644
index ae528ca08..000000000
--- a/apps/mobile/app/(app)/[workspace]/more/pins.tsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import { View } from "react-native";
-import { Text } from "@/components/ui/text";
-
-export default function PinsPage() {
- return (
-
-
- Pins coming soon.
-
-
- );
-}
diff --git a/apps/mobile/app/(app)/[workspace]/more/teams.tsx b/apps/mobile/app/(app)/[workspace]/more/teams.tsx
deleted file mode 100644
index 86a321037..000000000
--- a/apps/mobile/app/(app)/[workspace]/more/teams.tsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import { View } from "react-native";
-import { Text } from "@/components/ui/text";
-
-/**
- * Teams placeholder. Workspace team list, filled in a later phase to mirror
- * the web Teams surface.
- */
-export default function TeamsPage() {
- return (
-
-
- Teams coming soon.
-
-
- );
-}
diff --git a/apps/mobile/app/(app)/[workspace]/more/views.tsx b/apps/mobile/app/(app)/[workspace]/more/views.tsx
deleted file mode 100644
index 79d205c6b..000000000
--- a/apps/mobile/app/(app)/[workspace]/more/views.tsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import { View } from "react-native";
-import { Text } from "@/components/ui/text";
-
-/**
- * Views placeholder. Saved-filter views surface, filled in a later phase to
- * mirror the web Views surface.
- */
-export default function ViewsPage() {
- return (
-
-
- Views coming soon.
-
-
- );
-}
diff --git a/apps/mobile/components/nav/global-nav-menu.tsx b/apps/mobile/components/nav/global-nav-menu.tsx
index add450b6f..bb6b4108d 100644
--- a/apps/mobile/components/nav/global-nav-menu.tsx
+++ b/apps/mobile/components/nav/global-nav-menu.tsx
@@ -1,7 +1,6 @@
/**
- * GlobalNavMenu — top-right `…` popover that lets the user jump to any
- * top-level destination (Inbox, My Issues, Favorites, Projects, Initiatives,
- * Views, Teams, Settings, Search) and switch workspace.
+ * GlobalNavMenu — bottom-right popover anchored above the More tab. Three
+ * sections: user identity card → workspace switcher → real feature entries.
*
* Why a popover and not a tab: the iOS HIG treats tab-bar items as
* destinations, not action triggers, so "More" was an anti-pattern. Linear /
@@ -11,16 +10,30 @@
* Reanimated v3 and the mobile app is on Reanimated v4. Same Modal+Pressable
* pattern as status-picker-sheet.tsx etc. — keeps the dependency surface
* untouched.
+ *
+ * Composition mirrors web's sidebar dropdown (packages/views/layout/
+ * app-sidebar.tsx:496-511): user info row (avatar + name + email) sits above
+ * the workspace list. On mobile the row is a tappable card that pushes into
+ * the existing settings page, since there isn't enough screen real estate to
+ * inline account / workspaces / sign-out the way web does.
*/
import { useMemo, useState } from "react";
-import { ActivityIndicator, Modal, Pressable, ScrollView, View } from "react-native";
+import {
+ ActivityIndicator,
+ Image,
+ Modal,
+ Pressable,
+ ScrollView,
+ View,
+} from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { Ionicons } from "@expo/vector-icons";
import { router, usePathname } from "expo-router";
import { useQuery } from "@tanstack/react-query";
-import type { Workspace } from "@multica/core/types";
+import type { User, Workspace } from "@multica/core/types";
import { Text } from "@/components/ui/text";
import { workspaceListOptions } from "@/data/queries/workspaces";
+import { useAuthStore } from "@/data/auth-store";
import { useWorkspaceStore } from "@/data/workspace-store";
import { cn } from "@/lib/utils";
@@ -31,16 +44,11 @@ interface NavItem {
path: string;
}
+// Inbox / My Issues / Chat live on the bottom tab bar; Settings is reached
+// via the user card at the top of this popover. Only entries that are NOT
+// covered by either of those surfaces belong here.
const NAV_ITEMS: NavItem[] = [
- { label: "Inbox", icon: "mail-outline", path: "/inbox" },
- { label: "My Issues", icon: "list-outline", path: "/my-issues" },
- { label: "Favorites", icon: "star-outline", path: "/more/favorites" },
{ label: "Projects", icon: "cube-outline", path: "/more/projects" },
- { label: "Initiatives", icon: "navigate-outline", path: "/more/initiatives" },
- { label: "Views", icon: "layers-outline", path: "/more/views" },
- { label: "Teams", icon: "people-outline", path: "/more/teams" },
- { label: "Settings", icon: "settings-outline", path: "/more/settings" },
- { label: "Search", icon: "search-outline", path: "/search" },
];
const ICON_COLOR = "#3f3f46";
@@ -54,6 +62,7 @@ interface Props {
export function GlobalNavMenu({ visible, onClose }: Props) {
const insets = useSafeAreaInsets();
const slug = useWorkspaceStore((s) => s.currentWorkspaceSlug);
+ const user = useAuthStore((s) => s.user);
const pathname = usePathname();
const [showWorkspaces, setShowWorkspaces] = useState(false);
@@ -75,6 +84,13 @@ export function GlobalNavMenu({ visible, onClose }: Props) {
router.push(`/${slug}${path}`);
};
+ const onOpenSettings = () => {
+ if (!slug) return;
+ onClose();
+ setShowWorkspaces(false);
+ router.push(`/${slug}/more/settings`);
+ };
+
return (
{}}>
+ {/* User identity card — tap pushes into settings, where
+ account info, workspace list, and sign out already live. */}
+
+
{/* Workspace switcher header */}
setShowWorkspaces((v) => !v)}
@@ -240,3 +261,46 @@ function useCurrentWorkspace(slug: string | null): Workspace | undefined {
[data, slug],
);
}
+
+function UserCard({
+ user,
+ onPress,
+}: {
+ user: User | null;
+ onPress: () => void;
+}) {
+ const initial = (user?.name ?? user?.email ?? "U").charAt(0).toUpperCase();
+ return (
+
+ {user?.avatar_url ? (
+
+ ) : (
+
+
+ {initial}
+
+
+ )}
+
+
+ {user?.name ?? "—"}
+
+ {user?.email ? (
+
+ {user.email}
+
+ ) : null}
+
+
+
+ );
+}