style(frontend): Prettier config + one-time repo-wide format pass
Prettier 3 (pinned), printWidth 100, double quotes — chosen to match the existing hand-formatting and minimise reflow. This commit is ONLY the mechanical format pass (`prettier --write`) plus the config/ignore/scripts, isolated so it never pollutes a feature diff. Verified behaviour-neutral: typecheck, eslint (0 errors), and vitest all green before and after. See .git-blame-ignore-revs — `git blame` skips this sha.
This commit is contained in:
@@ -13,7 +13,10 @@ import Avatar from "./Avatar";
|
||||
import KeyGate from "./KeyGate";
|
||||
import ChatThread from "./ChatThread";
|
||||
|
||||
type View = "list" | "new" | { partnerId: number; name?: string; avatar?: string | null; system?: boolean };
|
||||
type View =
|
||||
| "list"
|
||||
| "new"
|
||||
| { partnerId: number; name?: string; avatar?: string | null; system?: boolean };
|
||||
|
||||
export default function Messages({ meId }: { meId: number }) {
|
||||
// Sub-views live in browser history, so Back returns to the conversation list before leaving
|
||||
@@ -35,12 +38,24 @@ export default function Messages({ meId }: { meId: number }) {
|
||||
);
|
||||
}
|
||||
if (view === "new") {
|
||||
return <Directory onPick={(u) => open({ partnerId: u.id, name: u.name, avatar: u.avatar_url })} onBack={back} />;
|
||||
return (
|
||||
<Directory
|
||||
onPick={(u) => open({ partnerId: u.id, name: u.name, avatar: u.avatar_url })}
|
||||
onBack={back}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<ConversationList
|
||||
meId={meId}
|
||||
onOpen={(c) => open({ partnerId: c.partner.id, name: c.partner.name, avatar: c.partner.avatar_url, system: c.partner.id === SYSTEM_ID })}
|
||||
onOpen={(c) =>
|
||||
open({
|
||||
partnerId: c.partner.id,
|
||||
name: c.partner.name,
|
||||
avatar: c.partner.avatar_url,
|
||||
system: c.partner.id === SYSTEM_ID,
|
||||
})
|
||||
}
|
||||
onNew={() => open("new")}
|
||||
/>
|
||||
);
|
||||
@@ -58,7 +73,9 @@ function ConversationList({
|
||||
const { t } = useTranslation();
|
||||
const keyState = useKeyState();
|
||||
const ready = keyState === "ready";
|
||||
const q = useLiveQuery<{ items: Conversation[] }>(["conversations"], api.conversations, { intervalMs: POLL_MS });
|
||||
const q = useLiveQuery<{ items: Conversation[] }>(["conversations"], api.conversations, {
|
||||
intervalMs: POLL_MS,
|
||||
});
|
||||
const items = q.data?.items ?? [];
|
||||
const [previews, setPreviews] = useState<Record<number, string>>({});
|
||||
|
||||
@@ -99,7 +116,9 @@ function ConversationList({
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="text-xs uppercase tracking-wide text-muted">{t("messages.conversations")}</div>
|
||||
<div className="text-xs uppercase tracking-wide text-muted">
|
||||
{t("messages.conversations")}
|
||||
</div>
|
||||
{ready && (
|
||||
<button
|
||||
onClick={onNew}
|
||||
@@ -120,16 +139,36 @@ function ConversationList({
|
||||
{items.map((c) => {
|
||||
const isSystem = c.partner.id === SYSTEM_ID;
|
||||
return (
|
||||
<div key={c.partner.id} className="group flex items-center gap-1 rounded-xl hover:bg-card transition">
|
||||
<button onClick={() => onOpen(c)} className="flex items-center gap-3 p-2.5 text-left min-w-0 flex-1">
|
||||
<Avatar src={c.partner.avatar_url} fallback={c.partner.name} className="w-10 h-10 rounded-full shrink-0 text-sm" />
|
||||
<div
|
||||
key={c.partner.id}
|
||||
className="group flex items-center gap-1 rounded-xl hover:bg-card transition"
|
||||
>
|
||||
<button
|
||||
onClick={() => onOpen(c)}
|
||||
className="flex items-center gap-3 p-2.5 text-left min-w-0 flex-1"
|
||||
>
|
||||
<Avatar
|
||||
src={c.partner.avatar_url}
|
||||
fallback={c.partner.name}
|
||||
className="w-10 h-10 rounded-full shrink-0 text-sm"
|
||||
/>
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className={`truncate ${c.unread > 0 ? "font-semibold" : "font-medium"}`}>{c.partner.name}</span>
|
||||
<span className="ml-auto text-[11px] text-muted shrink-0">{relativeTime(c.last_message.created_at)}</span>
|
||||
<span
|
||||
className={`truncate ${c.unread > 0 ? "font-semibold" : "font-medium"}`}
|
||||
>
|
||||
{c.partner.name}
|
||||
</span>
|
||||
<span className="ml-auto text-[11px] text-muted shrink-0">
|
||||
{relativeTime(c.last_message.created_at)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className={`truncate text-sm ${c.unread > 0 ? "text-fg" : "text-muted"}`}>{previewText(c)}</span>
|
||||
<span
|
||||
className={`truncate text-sm ${c.unread > 0 ? "text-fg" : "text-muted"}`}
|
||||
>
|
||||
{previewText(c)}
|
||||
</span>
|
||||
{c.unread > 0 && (
|
||||
<span className="ml-auto shrink-0 min-w-5 h-5 px-1.5 grid place-items-center rounded-full bg-accent text-accent-fg text-[11px] font-semibold">
|
||||
{c.unread}
|
||||
@@ -161,11 +200,16 @@ function Directory({ onPick, onBack }: { onPick: (u: MessageUser) => void; onBac
|
||||
const { t } = useTranslation();
|
||||
const [filter, setFilter] = useState("");
|
||||
const q = useQuery({ queryKey: ["message-users"], queryFn: api.messageUsers });
|
||||
const users = (q.data ?? []).filter((u) => u.name.toLowerCase().includes(filter.trim().toLowerCase()));
|
||||
const users = (q.data ?? []).filter((u) =>
|
||||
u.name.toLowerCase().includes(filter.trim().toLowerCase()),
|
||||
);
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<button onClick={onBack} className="p-1.5 rounded-lg text-muted hover:text-fg hover:bg-card transition">
|
||||
<button
|
||||
onClick={onBack}
|
||||
className="p-1.5 rounded-lg text-muted hover:text-fg hover:bg-card transition"
|
||||
>
|
||||
<ArrowLeft className="w-4 h-4" />
|
||||
</button>
|
||||
<div className="text-xs uppercase tracking-wide text-muted">{t("messages.newTitle")}</div>
|
||||
@@ -179,7 +223,9 @@ function Directory({ onPick, onBack }: { onPick: (u: MessageUser) => void; onBac
|
||||
{q.isLoading ? (
|
||||
<div className="p-8 text-muted text-center">{t("common.loading")}</div>
|
||||
) : users.length === 0 ? (
|
||||
<div className="glass rounded-2xl p-10 text-center text-muted">{t("messages.noPeople")}</div>
|
||||
<div className="glass rounded-2xl p-10 text-center text-muted">
|
||||
{t("messages.noPeople")}
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-col gap-1">
|
||||
{users.map((u) => (
|
||||
@@ -188,7 +234,11 @@ function Directory({ onPick, onBack }: { onPick: (u: MessageUser) => void; onBac
|
||||
onClick={() => onPick(u)}
|
||||
className="flex items-center gap-3 p-2.5 rounded-xl text-left hover:bg-card transition"
|
||||
>
|
||||
<Avatar src={u.avatar_url} fallback={u.name} className="w-9 h-9 rounded-full shrink-0 text-sm" />
|
||||
<Avatar
|
||||
src={u.avatar_url}
|
||||
fallback={u.name}
|
||||
className="w-9 h-9 rounded-full shrink-0 text-sm"
|
||||
/>
|
||||
<span className="truncate font-medium">{u.name}</span>
|
||||
</button>
|
||||
))}
|
||||
@@ -219,12 +269,24 @@ function PageThread({
|
||||
return (
|
||||
<div className="flex flex-col h-[65vh] min-h-80">
|
||||
<div className="flex items-center gap-3 pb-3 border-b border-border">
|
||||
<button onClick={onBack} className="p-1.5 rounded-lg text-muted hover:text-fg hover:bg-card transition">
|
||||
<button
|
||||
onClick={onBack}
|
||||
className="p-1.5 rounded-lg text-muted hover:text-fg hover:bg-card transition"
|
||||
>
|
||||
<ArrowLeft className="w-4 h-4" />
|
||||
</button>
|
||||
<Avatar src={seedAvatar} fallback={name} className="w-9 h-9 rounded-full shrink-0 text-sm" />
|
||||
<Avatar
|
||||
src={seedAvatar}
|
||||
fallback={name}
|
||||
className="w-9 h-9 rounded-full shrink-0 text-sm"
|
||||
/>
|
||||
<span className="font-semibold truncate">{name}</span>
|
||||
{!isSystem && <ShieldCheck className="w-4 h-4 text-muted shrink-0" aria-label={t("messages.encryptedHint")} />}
|
||||
{!isSystem && (
|
||||
<ShieldCheck
|
||||
className="w-4 h-4 text-muted shrink-0"
|
||||
aria-label={t("messages.encryptedHint")}
|
||||
/>
|
||||
)}
|
||||
<div className="flex-1" />
|
||||
{!isSystem && ready && (
|
||||
<button
|
||||
|
||||
Reference in New Issue
Block a user