mirror of
https://github.com/multica-ai/multica.git
synced 2026-08-12 10:59:06 +02:00
Mobile can now actually create issues. Phase 1 left submit as a
console.log stub; this iteration wires Manual mode end-to-end so an
issue typed on a phone lands in the backend and appears in the user's
my-issues list on next refresh.
Wire-up:
- api.createIssue(body) — POST /api/issues, mirroring server route at
server/cmd/server/router.go:320. Matches the CreateIssueRequest type
exported from @multica/core/types so payload shape agrees across
clients.
- useCreateIssue() mutation in data/mutations/issues.ts — no optimistic
insert (the my-issues list is status-bucketed + scope-filtered, so
optimism needs bucket+scope decisions; invalidation is simpler and
hosted-backend latency is sub-300ms). onSuccess invalidates myAll
and inbox query keys.
- new-issue.tsx Manual panel: submit ↑ calls mutateAsync, dismisses on
success, surfaces errors via Alert.alert with the form state preserved
so the user can retry. Button shows a spinner during the in-flight
request and all inputs are disabled.
@ mention in description (members + agents):
- Mirrors comment-composer.tsx pattern exactly — selection tracking,
tokenAtCursor on every change/selection event, MentionSuggestionBar
rendered above the chip row, insertMention on pick, markers list
appended.
- Title input stays plain (web doesn't allow mentions in title; we
mirror that).
- Wire format on submit: serializeMentions(description, markers) →
`[@name](mention://type/id)` markdown. Recognised by:
* server/internal/util/mention.go ParseMentions
* packages/views/editor/extensions/mention-extension.ts (web Tiptap)
* apps/mobile/components/issue/mention-chip.tsx (mobile timeline)
- Backend does NOT trigger inbox notifications for mentions in issue
descriptions (only on comments — see server/internal/handler/comment.go
ParseMentions call). Mobile doesn't need to send a separate mentioned_*
field; the markdown alone is sufficient.
Header polish:
- SubmitIssueButton accepts a `loading` prop; renders ActivityIndicator
in place of the ↑ glyph while pending. Defends against double-tap.
- ModalCloseButton's earlier "Cancel" text is now a ✕ icon in a circle
to match the new-issue / search modal visual reference (Linear-style).
Agent mode unchanged — still a placeholder that console.logs and
dismisses. Phase 3 will wire the real agent picker, apiClient
.quickCreateIssue, and the daemon version gate.
Explicitly NOT in this commit (later phases):
- Markdown formatting toolbar (Phase 2C)
- Project / Labels / Due date / Parent chips (Phase 2D)
- Image / file attachments (Phase 2E)
- #MUL-42 issue references, @all mention
- Draft persistence, "Create Another" toggle
- Pre-fill from sub-issue entry, optimistic list insert
- Success toast (success path = silent dismiss; mobile has no toast
component yet)
Verified: pnpm --filter @multica/mobile typecheck passes; lint shows
only pre-existing issues unrelated to this change.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
47 lines
1.4 KiB
TypeScript
47 lines
1.4 KiB
TypeScript
/**
|
|
* ↑ submit icon button rendered in the new-issue modal's Stack header
|
|
* (headerRight slot). Visually pairs with ModalCloseButton on the left:
|
|
* same size/shape circle, brand accent when active vs dimmed disabled
|
|
* state. Shows a spinner instead of the arrow while the mutation is
|
|
* in-flight, so the user can't double-tap.
|
|
*/
|
|
import { ActivityIndicator, Pressable, View } from "react-native";
|
|
import { Ionicons } from "@expo/vector-icons";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
interface Props {
|
|
disabled: boolean;
|
|
onPress: () => void;
|
|
loading?: boolean;
|
|
}
|
|
|
|
export function SubmitIssueButton({ disabled, onPress, loading }: Props) {
|
|
const interactive = !disabled && !loading;
|
|
return (
|
|
<Pressable
|
|
onPress={interactive ? onPress : undefined}
|
|
hitSlop={8}
|
|
accessibilityLabel="Create issue"
|
|
accessibilityState={{ disabled: !interactive, busy: loading }}
|
|
className={cn(interactive && "active:opacity-60")}
|
|
>
|
|
<View
|
|
className={cn(
|
|
"size-7 items-center justify-center rounded-full",
|
|
interactive ? "bg-brand" : "bg-secondary",
|
|
)}
|
|
>
|
|
{loading ? (
|
|
<ActivityIndicator size="small" color="#ffffff" />
|
|
) : (
|
|
<Ionicons
|
|
name="arrow-up"
|
|
size={18}
|
|
color={interactive ? "#ffffff" : "#a1a1aa"}
|
|
/>
|
|
)}
|
|
</View>
|
|
</Pressable>
|
|
);
|
|
}
|