web: allow editing group and channel descriptions in chat info
Some checks failed
CI / test (push) Failing after 2m11s

This commit is contained in:
2026-03-08 22:56:07 +03:00
parent 0bc7760eee
commit bf7b4fa3c0
2 changed files with 22 additions and 5 deletions

View File

@@ -29,10 +29,10 @@ Legend:
20. GIF - `DONE` (web GIF picker with provider search, preset fallback, and favorites) 20. GIF - `DONE` (web GIF picker with provider search, preset fallback, and favorites)
21. Message History/Search - `DONE` (history/pagination/chat+global search) 21. Message History/Search - `DONE` (history/pagination/chat+global search)
22. Text Formatting - `DONE` (bold/italic/underline/spoiler/mono/links + strikethrough + quote/code block; web composer supports keyboard shortcuts `Ctrl/Cmd+B/I/U/K`, `Ctrl/Cmd+Shift+X`, `Ctrl/Cmd+Shift+\``) 22. Text Formatting - `DONE` (bold/italic/underline/spoiler/mono/links + strikethrough + quote/code block; web composer supports keyboard shortcuts `Ctrl/Cmd+B/I/U/K`, `Ctrl/Cmd+Shift+X`, `Ctrl/Cmd+Shift+\``)
23. Groups - `DONE` (create/add/remove/invite link with role-aware moderation flows in web Chat Info and covered invite/join behavior) 23. Groups - `DONE` (create/add/remove/invite link with role-aware moderation flows in web Chat Info, including owner/admin editing of title and description)
24. Roles - `DONE` (owner/admin/member) 24. Roles - `DONE` (owner/admin/member)
25. Admin Rights - `DONE` (delete/pin/edit info + ban/unban/member role management with role-gated UI and APIs for groups/channels; member role no longer sees participants lists in group/channel chat info) 25. Admin Rights - `DONE` (delete/pin/edit info + ban/unban/member role management with role-gated UI and APIs for groups/channels; member role no longer sees participants lists in group/channel chat info)
26. Channels - `DONE` (create/post/edit/delete/subscribe/unsubscribe with role-aware channel behavior and invite-link flows) 26. Channels - `DONE` (create/post/edit/delete/subscribe/unsubscribe with role-aware channel behavior and invite-link flows; owner/admin can edit channel title and description in web Chat Info)
27. Channel Types - `DONE` (public/private) 27. Channel Types - `DONE` (public/private)
28. Notifications - `DONE` (browser notifications + mute/settings/sound; mention override for muted chats, realtime sync for mute state, notification click deep-link (`/?chat=..&message=..`) restores chat/message focus after auth) 28. Notifications - `DONE` (browser notifications + mute/settings/sound; mention override for muted chats, realtime sync for mute state, notification click deep-link (`/?chat=..&message=..`) restores chat/message focus after auth)
29. Archive - `DONE` 29. Archive - `DONE`

View File

@@ -48,6 +48,7 @@ export function ChatInfoPanel({ chatId, open, onClose }: Props) {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const [titleDraft, setTitleDraft] = useState(""); const [titleDraft, setTitleDraft] = useState("");
const [descriptionDraft, setDescriptionDraft] = useState("");
const [savingTitle, setSavingTitle] = useState(false); const [savingTitle, setSavingTitle] = useState(false);
const [chatAvatarUploading, setChatAvatarUploading] = useState(false); const [chatAvatarUploading, setChatAvatarUploading] = useState(false);
const [searchQuery, setSearchQuery] = useState(""); const [searchQuery, setSearchQuery] = useState("");
@@ -210,6 +211,7 @@ export function ChatInfoPanel({ chatId, open, onClose }: Props) {
const detail = await getChatDetail(targetChatId); const detail = await getChatDetail(targetChatId);
setChat(detail); setChat(detail);
setTitleDraft(detail.title ?? ""); setTitleDraft(detail.title ?? "");
setDescriptionDraft(detail.description ?? "");
const notificationSettings = await getChatNotificationSettings(targetChatId); const notificationSettings = await getChatNotificationSettings(targetChatId);
setMuted(notificationSettings.muted); setMuted(notificationSettings.muted);
if (detail.type === "private" && !detail.is_saved && detail.counterpart_user_id) { if (detail.type === "private" && !detail.is_saved && detail.counterpart_user_id) {
@@ -487,23 +489,38 @@ export function ChatInfoPanel({ chatId, open, onClose }: Props) {
value={titleDraft} value={titleDraft}
onChange={(e) => setTitleDraft(e.target.value)} onChange={(e) => setTitleDraft(e.target.value)}
/> />
<p className="mt-2 text-xs text-slate-400">Description</p>
<textarea
className="mt-1 min-h-[88px] w-full resize-y rounded bg-slate-800 px-3 py-2 text-sm outline-none"
maxLength={512}
onChange={(e) => setDescriptionDraft(e.target.value)}
placeholder="Enter chat description"
value={descriptionDraft}
/>
<p className="mt-1 text-[11px] text-slate-500">{descriptionDraft.length}/512</p>
<button <button
className="mt-2 w-full rounded bg-sky-500 px-3 py-2 text-sm font-semibold text-slate-950 disabled:opacity-60" className="mt-2 w-full rounded bg-sky-500 px-3 py-2 text-sm font-semibold text-slate-950 disabled:opacity-60"
disabled={savingTitle || !titleDraft.trim() || chatAvatarUploading} disabled={savingTitle || !titleDraft.trim() || chatAvatarUploading}
onClick={async () => { onClick={async () => {
setSavingTitle(true); setSavingTitle(true);
try { try {
const updated = await updateChatProfile(chatId, { title: titleDraft.trim() }); const normalizedDescription = descriptionDraft.trim();
const updated = await updateChatProfile(chatId, {
title: titleDraft.trim(),
description: normalizedDescription.length ? normalizedDescription : null,
});
setChat((prev) => (prev ? { ...prev, ...updated } : prev)); setChat((prev) => (prev ? { ...prev, ...updated } : prev));
setDescriptionDraft(updated.description ?? "");
await loadChats(); await loadChats();
showToast("Chat profile updated");
} catch { } catch {
setError("Failed to update title"); setError("Failed to update chat profile");
} finally { } finally {
setSavingTitle(false); setSavingTitle(false);
} }
}} }}
> >
Save title Save profile
</button> </button>
</> </>
) : null} ) : null}