diff --git a/app/messages/router.py b/app/messages/router.py index 009cbc2..ebc7743 100644 --- a/app/messages/router.py +++ b/app/messages/router.py @@ -13,6 +13,7 @@ from app.messages.schemas import ( MessageStatusUpdateRequest, MessageUpdateRequest, ) +from app.messages.repository import get_message_by_id from app.messages.service import ( create_chat_message, delete_message, @@ -87,7 +88,10 @@ async def remove_message( current_user: User = Depends(get_current_user), ) -> None: if for_all: + message = await get_message_by_id(db, message_id) await delete_message_for_all(db, message_id=message_id, user_id=current_user.id) + if message: + await realtime_gateway.publish_chat_updated(chat_id=message.chat_id) return await delete_message(db, message_id=message_id, user_id=current_user.id) diff --git a/app/messages/service.py b/app/messages/service.py index facc4d0..add62d2 100644 --- a/app/messages/service.py +++ b/app/messages/service.py @@ -206,6 +206,11 @@ async def delete_message(db: AsyncSession, *, message_id: int, user_id: int) -> membership = await chats_repository.get_chat_member(db, chat_id=message.chat_id, user_id=user_id) if not membership: raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="You are not a member of this chat") + if chat.type == ChatType.CHANNEL and not chat.is_saved: + raise HTTPException( + status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, + detail="Channel messages can only be deleted for everyone", + ) # Telegram-like default: delete only for current user. hidden = await repository.get_hidden_message(db, message_id=message.id, user_id=user_id) if not hidden: diff --git a/web/src/components/ChatList.tsx b/web/src/components/ChatList.tsx index 9946071..dab8c61 100644 --- a/web/src/components/ChatList.tsx +++ b/web/src/components/ChatList.tsx @@ -1,6 +1,6 @@ import { useEffect, useState } from "react"; import { createPortal } from "react-dom"; -import { archiveChat, clearChat, createPrivateChat, deleteChat, getChats, getSavedMessagesChat, joinChat, pinChat, unarchiveChat, unpinChat } from "../api/chats"; +import { archiveChat, clearChat, createPrivateChat, deleteChat, getChats, getSavedMessagesChat, joinChat, leaveChat, pinChat, unarchiveChat, unpinChat } from "../api/chats"; import { globalSearch } from "../api/search"; import type { DiscoverChat, Message, UserSearchItem } from "../chat/types"; import { addContact, addContactByEmail, listContacts, removeContact, updateMyProfile } from "../api/users"; @@ -50,7 +50,16 @@ export function ChatList() { const canDeleteForEveryone = Boolean( deleteModalChat && !deleteModalChat.is_saved && - (deleteModalChat.type === "group" || deleteModalChat.type === "private") + ( + deleteModalChat.type === "group" || + deleteModalChat.type === "private" || + (deleteModalChat.type === "channel" && (deleteModalChat.my_role === "owner" || deleteModalChat.my_role === "admin")) + ) + ); + const channelMemberLeaveOnly = Boolean( + deleteModalChat && + deleteModalChat.type === "channel" && + deleteModalChat.my_role === "member" ); useEffect(() => { @@ -598,7 +607,13 @@ export function ChatList() { setDeleteForAll(false); }} > - {chats.find((c) => c.id === ctxChatId)?.is_saved ? "Clear chat" : "Delete chat"} + {(() => { + const chat = chats.find((c) => c.id === ctxChatId); + if (!chat) return "Delete chat"; + if (chat.is_saved) return "Clear chat"; + if (chat.type === "channel" && chat.my_role === "member") return "Leave channel"; + return "Delete chat"; + })()} + {!channelOnlyDeleteForAll ? ( + + ) : null} {canDeleteAllForSelection ? ( + {!channelOnlyDeleteForAll ? ( + + ) : null} {canDeleteForEveryone(messagesMap.get(deleteMessageId), activeChat, me?.id) ? (