mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-26 12:35:35 +02:00
Fill the Chat tab placeholder. UX is mobile-native (top bar with tap-title sheet, message list, bottom composer — no two-layer nav); logic is at parity with web (API/events/has_unread/optimistic sequence/permissions/ enums all mirrored). Includes: - data layer: 8 chat API methods + zod schemas with .catch() enum drift fallback; queries / mutations (optimistic delete + markRead); per- session drafts store - two-tier realtime: listing-level hook mounted in workspace _layout (chat:session_* + chat:done for has_unread), per-record hook mounted in the chat screen (chat:message/done + 5 task:* events, all filtered by chat_session_id, scoped reconnect invalidates); ws-updaters carry an invalidate fallback for pre-#2123 servers that omit chat:done payload - rule mirrors: canAssignAgent, failureReasonLabel, agent availability three-state hook (mirror-not-import per apps/mobile/CLAUDE.md) - UI: ChatHeader (tap title → SessionSheet) + ChatMessageList (FlatList, destructive bubble on failure_reason) + ChatComposer (mention + markdown toolbar minus file/image) + StatusPill (Thinking · Ns) + SessionSheet (with agent avatars + long-press delete) + AgentPickerSheet + NoAgentBanner v1 cuts (deferred to v2): file upload, rename, Chat tab unread badge, agent presence dot, task tool_use detail expansion, focus mode route anchor, starter prompts, history pagination, mobile test infra. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
43 lines
1.6 KiB
TypeScript
43 lines
1.6 KiB
TypeScript
/**
|
|
* Mobile-owned three-state availability for "does the current user have any
|
|
* agent they can chat with in this workspace?".
|
|
*
|
|
* Mirror of `packages/core/agents/use-workspace-agent-availability.ts` —
|
|
* see there for the design rationale on why this is a three-state
|
|
* `"loading" | "none" | "available"` instead of a boolean.
|
|
*
|
|
* The chat NoAgentBanner uses this: only `"none"` triggers the banner +
|
|
* input-disable; `"loading"` stays neutral to avoid a fake-empty flash on
|
|
* mount.
|
|
*/
|
|
import { useQuery } from "@tanstack/react-query";
|
|
import { useAuthStore } from "@/data/auth-store";
|
|
import { useWorkspaceStore } from "@/data/workspace-store";
|
|
import { agentListOptions } from "@/data/queries/agents";
|
|
import { memberListOptions } from "@/data/queries/members";
|
|
import { canAssignAgent } from "./can-assign-agent";
|
|
|
|
export type WorkspaceAgentAvailability = "loading" | "none" | "available";
|
|
|
|
export function useWorkspaceAgentAvailability(): WorkspaceAgentAvailability {
|
|
const wsId = useWorkspaceStore((s) => s.currentWorkspaceId);
|
|
const userId = useAuthStore((s) => s.user?.id);
|
|
|
|
const { data: agents, isFetched: agentsFetched } = useQuery(
|
|
agentListOptions(wsId),
|
|
);
|
|
const { data: members, isFetched: membersFetched } = useQuery(
|
|
memberListOptions(wsId),
|
|
);
|
|
|
|
if (!agentsFetched || !membersFetched) return "loading";
|
|
|
|
const memberRole = members?.find((m) => m.user_id === userId)?.role;
|
|
|
|
const hasVisibleAgent = (agents ?? []).some(
|
|
(a) => !a.archived_at && canAssignAgent(a, userId, memberRole),
|
|
);
|
|
|
|
return hasVisibleAgent ? "available" : "none";
|
|
}
|