feat(messages): support forwarding to multiple chats

This commit is contained in:
2026-03-08 09:55:39 +03:00
parent 8cdcd9531d
commit 7c4a5f990d
4 changed files with 99 additions and 8 deletions

View File

@@ -1,6 +1,6 @@
import { useEffect, useMemo, useState } from "react";
import { createPortal } from "react-dom";
import { deleteMessage, forwardMessage, listMessageReactions, pinMessage, toggleMessageReaction } from "../api/chats";
import { deleteMessage, forwardMessageBulk, listMessageReactions, pinMessage, toggleMessageReaction } from "../api/chats";
import type { Message, MessageReaction } from "../chat/types";
import { useAuthStore } from "../store/authStore";
import { useChatStore } from "../store/chatStore";
@@ -40,6 +40,7 @@ export function MessageList() {
const [forwardQuery, setForwardQuery] = useState("");
const [forwardError, setForwardError] = useState<string | null>(null);
const [isForwarding, setIsForwarding] = useState(false);
const [forwardSelectedChatIds, setForwardSelectedChatIds] = useState<Set<number>>(new Set());
const [deleteMessageId, setDeleteMessageId] = useState<number | null>(null);
const [deleteError, setDeleteError] = useState<string | null>(null);
const [selectedIds, setSelectedIds] = useState<Set<number>>(new Set());
@@ -86,6 +87,7 @@ export function MessageList() {
}
setCtx(null);
setForwardMessageId(null);
setForwardSelectedChatIds(new Set());
setDeleteMessageId(null);
setSelectedIds(new Set());
};
@@ -98,6 +100,7 @@ export function MessageList() {
setCtx(null);
setDeleteMessageId(null);
setForwardMessageId(null);
setForwardSelectedChatIds(new Set());
setReactionsByMessage({});
}, [activeChatId]);
@@ -127,13 +130,19 @@ export function MessageList() {
}
const chatId = activeChatId;
async function handleForward(targetChatId: number) {
async function handleForwardSubmit() {
if (!forwardMessageId) return;
const targetChatIds = [...forwardSelectedChatIds];
if (!targetChatIds.length) {
setForwardError("Select at least one chat");
return;
}
setIsForwarding(true);
setForwardError(null);
try {
await forwardMessage(forwardMessageId, targetChatId);
await forwardMessageBulk(forwardMessageId, targetChatIds);
setForwardMessageId(null);
setForwardSelectedChatIds(new Set());
setForwardQuery("");
} catch {
setForwardError("Failed to forward message");
@@ -414,6 +423,7 @@ export function MessageList() {
setForwardMessageId(ctx.messageId);
setForwardQuery("");
setForwardError(null);
setForwardSelectedChatIds(new Set());
setCtx(null);
}}
>
@@ -459,10 +469,20 @@ export function MessageList() {
<div className="tg-scrollbar max-h-56 space-y-1 overflow-auto">
{forwardTargets.map((chat) => (
<button
className="block w-full rounded-lg bg-slate-800 px-3 py-2 text-left text-sm hover:bg-slate-700 disabled:opacity-60"
className={`block w-full rounded-lg px-3 py-2 text-left text-sm disabled:opacity-60 ${forwardSelectedChatIds.has(chat.id) ? "bg-sky-500/30" : "bg-slate-800 hover:bg-slate-700"}`}
disabled={isForwarding}
key={chat.id}
onClick={() => void handleForward(chat.id)}
onClick={() => {
setForwardSelectedChatIds((prev) => {
const next = new Set(prev);
if (next.has(chat.id)) {
next.delete(chat.id);
} else {
next.add(chat.id);
}
return next;
});
}}
>
<p className="truncate font-semibold">{chatLabel(chat)}</p>
<p className="truncate text-xs text-slate-400">{chat.handle ? `@${chat.handle}` : chat.type}</p>
@@ -471,9 +491,14 @@ export function MessageList() {
{forwardTargets.length === 0 ? <p className="px-1 py-2 text-xs text-slate-400">No chats found</p> : null}
</div>
{forwardError ? <p className="mt-2 text-xs text-red-400">{forwardError}</p> : null}
<button className="mt-3 w-full rounded bg-slate-700 px-3 py-2 text-sm" onClick={() => setForwardMessageId(null)}>
Cancel
</button>
<div className="mt-3 flex gap-2">
<button className="w-full rounded bg-sky-500 px-3 py-2 text-sm font-semibold text-slate-950" onClick={() => void handleForwardSubmit()}>
Forward ({forwardSelectedChatIds.size})
</button>
<button className="w-full rounded bg-slate-700 px-3 py-2 text-sm" onClick={() => setForwardMessageId(null)}>
Cancel
</button>
</div>
</div>
</div>
) : null}