fix(web): enforce channel read-only and admin delete rules
This commit is contained in:
@@ -611,7 +611,7 @@ export function ChatInfoPanel({ chatId, open, onClose }: Props) {
|
||||
}
|
||||
}}
|
||||
>
|
||||
Leave chat
|
||||
{chat.type === "channel" ? "Leave channel" : "Leave chat"}
|
||||
</button>
|
||||
) : null}
|
||||
</>
|
||||
|
||||
@@ -9,6 +9,7 @@ type RecordingState = "idle" | "recording" | "locked";
|
||||
|
||||
export function MessageComposer() {
|
||||
const activeChatId = useChatStore((s) => s.activeChatId);
|
||||
const chats = useChatStore((s) => s.chats);
|
||||
const me = useAuthStore((s) => s.me);
|
||||
const draftsByChat = useChatStore((s) => s.draftsByChat);
|
||||
const setDraft = useChatStore((s) => s.setDraft);
|
||||
@@ -52,6 +53,12 @@ export function MessageComposer() {
|
||||
const [recordSeconds, setRecordSeconds] = useState(0);
|
||||
const [dragHint, setDragHint] = useState<"idle" | "lock" | "cancel">("idle");
|
||||
const hasTextToSend = text.trim().length > 0;
|
||||
const activeChat = chats.find((chat) => chat.id === activeChatId);
|
||||
const canSendInActiveChat = Boolean(
|
||||
activeChatId &&
|
||||
activeChat &&
|
||||
(activeChat.type !== "channel" || activeChat.my_role === "owner" || activeChat.my_role === "admin" || activeChat.is_saved)
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
recordingStateRef.current = recordingState;
|
||||
@@ -146,7 +153,7 @@ export function MessageComposer() {
|
||||
}
|
||||
|
||||
async function handleSend() {
|
||||
if (!activeChatId || !text.trim() || !me) {
|
||||
if (!activeChatId || !text.trim() || !me || !canSendInActiveChat) {
|
||||
return;
|
||||
}
|
||||
const clientMessageId = makeClientMessageId();
|
||||
@@ -205,7 +212,7 @@ export function MessageComposer() {
|
||||
messageType: "file" | "image" | "video" | "audio" | "voice" | "circle_video" = "file",
|
||||
waveformPoints?: number[] | null
|
||||
) {
|
||||
if (!activeChatId || !me) {
|
||||
if (!activeChatId || !me || !canSendInActiveChat) {
|
||||
return;
|
||||
}
|
||||
setIsUploading(true);
|
||||
@@ -303,7 +310,7 @@ export function MessageComposer() {
|
||||
}
|
||||
|
||||
async function startRecord() {
|
||||
if (recordingState !== "idle") {
|
||||
if (recordingState !== "idle" || !canSendInActiveChat) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
@@ -694,11 +701,17 @@ export function MessageComposer() {
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{!canSendInActiveChat && activeChat?.type === "channel" ? (
|
||||
<div className="mb-2 rounded-xl border border-amber-500/30 bg-amber-500/10 px-3 py-2 text-xs text-amber-200">
|
||||
Read-only channel: only owners and admins can post.
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<div className="mb-2 flex items-end gap-2">
|
||||
<div className="relative">
|
||||
<button
|
||||
className="h-[42px] min-w-[42px] rounded-full bg-slate-700/85 px-3 text-sm font-semibold text-slate-200 hover:bg-slate-700 disabled:opacity-60"
|
||||
disabled={isUploading || recordingState !== "idle"}
|
||||
disabled={isUploading || recordingState !== "idle" || !canSendInActiveChat}
|
||||
onClick={() => setShowAttachMenu((v) => !v)}
|
||||
type="button"
|
||||
>
|
||||
@@ -728,7 +741,7 @@ export function MessageComposer() {
|
||||
type="file"
|
||||
multiple
|
||||
accept="image/*,video/*"
|
||||
disabled={isUploading || recordingState !== "idle"}
|
||||
disabled={isUploading || recordingState !== "idle" || !canSendInActiveChat}
|
||||
onChange={(event) => {
|
||||
const files = event.target.files ? Array.from(event.target.files) : [];
|
||||
if (files.length) {
|
||||
@@ -742,7 +755,7 @@ export function MessageComposer() {
|
||||
className="hidden"
|
||||
type="file"
|
||||
multiple
|
||||
disabled={isUploading || recordingState !== "idle"}
|
||||
disabled={isUploading || recordingState !== "idle" || !canSendInActiveChat}
|
||||
onChange={(event) => {
|
||||
const files = event.target.files ? Array.from(event.target.files) : [];
|
||||
if (files.length) {
|
||||
@@ -758,6 +771,7 @@ export function MessageComposer() {
|
||||
onClick={() => setShowFormatMenu((v) => !v)}
|
||||
type="button"
|
||||
title="Text formatting"
|
||||
disabled={!canSendInActiveChat}
|
||||
>
|
||||
Aa
|
||||
</button>
|
||||
@@ -765,9 +779,10 @@ export function MessageComposer() {
|
||||
<textarea
|
||||
ref={textareaRef}
|
||||
className="min-h-[42px] max-h-40 flex-1 resize-y rounded-2xl border border-slate-700/80 bg-slate-800/80 px-4 py-2.5 text-sm outline-none placeholder:text-slate-400 focus:border-sky-500"
|
||||
placeholder="Write a message..."
|
||||
placeholder={canSendInActiveChat ? "Write a message..." : "Read-only channel"}
|
||||
rows={1}
|
||||
value={text}
|
||||
disabled={!canSendInActiveChat}
|
||||
onKeyDown={onComposerKeyDown}
|
||||
onChange={(event) => {
|
||||
const next = event.target.value;
|
||||
@@ -783,7 +798,7 @@ export function MessageComposer() {
|
||||
{hasTextToSend ? (
|
||||
<button
|
||||
className="h-[42px] w-[56px] rounded-full bg-sky-500 px-3 text-sm font-semibold text-slate-950 hover:bg-sky-400 disabled:opacity-60"
|
||||
disabled={recordingState !== "idle" || !activeChatId}
|
||||
disabled={recordingState !== "idle" || !activeChatId || !canSendInActiveChat}
|
||||
onClick={handleSend}
|
||||
type="button"
|
||||
title="Send message"
|
||||
@@ -795,7 +810,7 @@ export function MessageComposer() {
|
||||
className={`h-[42px] w-[56px] rounded-full px-3 text-sm font-semibold ${
|
||||
recordingState === "idle" ? "bg-emerald-500 text-slate-950 hover:bg-emerald-400" : "bg-slate-700 text-slate-300"
|
||||
}`}
|
||||
disabled={isUploading || !activeChatId}
|
||||
disabled={isUploading || !activeChatId || !canSendInActiveChat}
|
||||
onPointerDown={onMicPointerDown}
|
||||
type="button"
|
||||
title="Hold to record voice"
|
||||
|
||||
@@ -1025,6 +1025,9 @@ function canDeleteForEveryone(
|
||||
if (chat.type === "channel") {
|
||||
return chat.my_role === "owner" || chat.my_role === "admin";
|
||||
}
|
||||
if (chat.type === "group" && (chat.my_role === "owner" || chat.my_role === "admin")) {
|
||||
return true;
|
||||
}
|
||||
return message.sender_id === meId;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user