Files
multica/apps/mobile
Naiyuan Qing fe62f7f5b2 feat(mobile): More tab opens dropdown popover anchored above the tab
Tapping the More tab now opens a small DropdownMenu popover containing
the user card, workspace switcher, and secondary nav (Issues/Projects)
— anchored directly above the tab button. Replaces the previous
listeners.tabPress that pushed /menu as an iOS formSheet, which felt
heavy for a quick switch.

Implementation:

- Add @rn-primitives/dropdown-menu and a shadcn-style wrapper at
  components/ui/dropdown-menu.tsx (Root/Trigger/Portal/Overlay/Content/
  Item/Label/Separator using semantic tokens — bg-popover, accent,
  border — matching the existing button.tsx pattern).
- New MoreTabDropdownAnchor (components/nav/more-tab-dropdown.tsx)
  mounts as a sibling to <Tabs> at the workspace tabs layout. It is
  absolute-positioned over the More tab's screen rect (right 25%,
  bottom = safe-area inset, height = 49) with pointerEvents="box-none"
  so taps pass straight through to the real tab button. The Trigger
  inside is an invisible Pressable; opened imperatively via
  TriggerRef.open() from listeners.tabPress on the More tab. The
  @rn-primitives Trigger measures its own rect inside open(), so the
  popover anchors correctly without manual screen-width math.
- The /menu formSheet route stays registered in [workspace]/_layout.tsx
  as a dead path for now (reversibility); to be removed once the
  popover bakes in.

Rejected alternative: replacing the More tab's tabBarButton with a
custom DropdownMenuTrigger wrapper. RN's BottomTabItem wraps the
returned button in <View style={{flex:1}}> and expects a single
Pressable; introducing the DropdownMenu Root as an extra wrapping View
broke the flex layout and stripped the "More" label. The Option B
pattern here leaves the real tab button entirely untouched.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 15:03:48 +08:00
..

Multica Mobile (iOS)

Expo + React Native iOS client for Multica. Independent from web/desktop — shares only types from @multica/core/. See CLAUDE.md for the locked tech-stack baseline and import rules.

Scripts

Command What it does Backend
pnpm dev:mobile Metro only (reuse existing dev install) local (.env.development.local)
pnpm dev:mobile:staging Metro only (reuse existing dev install) staging (.env.staging)
pnpm ios:mobile:device Full rebuild + install on USB iPhone, Debug local
pnpm ios:mobile:device:staging Full rebuild + install on USB iPhone, Debug staging
pnpm ios:mobile:device:staging:release Full rebuild + install, Release (standalone) staging

dev:* runs Metro only — assumes a Debug build of the matching variant is already installed on the device. ios:device:* does a full native rebuild + install onto a USB-connected iPhone.

Bundle identifier and display name switch on APP_ENV (see app.config.ts), so Dev / Staging / Production variants can coexist on the same device.

Build your own version onto your iPhone

Two paths, depending on what you want to do:

Day-to-day development (you have the Mac in front of you)

pnpm ios:mobile:device:staging

Produces a Debug build with expo-dev-launcher embedded. Every launch the app probes Metro on your Mac and pulls fresh JS — perfect for hot-reload, painful when the Mac is asleep or you're on a different WiFi.

Standalone / "just use it" (you want to walk away from the Mac)

pnpm ios:mobile:device:staging:release

Produces a Release build. No expo-dev-launcher, no Metro probe, no "Downloading…" screen. Splash → app, exactly like an App Store install. The trade-off: you cannot hot-reload — every JS change requires re-running this command.

Both paths share the same prerequisites: Mac with Xcode, free Apple ID added under Xcode → Settings → Accounts, iPhone connected via USB with Developer Mode enabled. Follow Expo's Set up your environment — pick Development build → iOS Device — if any of that is missing.

First build of either variant downloads CocoaPods + compiles React Native from source — expect 10-20 minutes. Subsequent builds reuse Xcode's DerivedData cache.

7-day signing limit

A free Apple ID signs builds for 7 days only, Debug and Release both. After that the app refuses to launch. Plug back into the Mac and re-run the corresponding ios:* script to re-sign. The only workaround is an Apple Developer Program account ($99/yr), which extends to 1 year.

Pointing at a different backend

Edit EXPO_PUBLIC_API_URL in .env.staging (or .env.development.local). Then:

  • For an installed Debug build: restart Metro (pnpm dev:mobile:staging) so the next JS bundle it serves picks up the new value.
  • For an installed Release build: re-run the ios:*:release command — the value is baked into the embedded bundle at build time.

For local backend testing, use your Mac's LAN IP (ipconfig getifaddr en0), not localhost.