feat: add saved messages, public chat discovery/join, and chat delete options
All checks were successful
CI / test (push) Successful in 19s
All checks were successful
CI / test (push) Successful in 19s
- add Saved Messages system chat with dedicated API - add public group/channel metadata and discover/join endpoints - add chat delete flow with for_all option and channel-wide delete - switch message actions to context menu and improve reply/forward visuals - improve microphone permission handling for voice recording
This commit is contained in:
@@ -12,6 +12,29 @@ async def create_chat(db: AsyncSession, *, chat_type: ChatType, title: str | Non
|
||||
return chat
|
||||
|
||||
|
||||
async def create_chat_with_meta(
|
||||
db: AsyncSession,
|
||||
*,
|
||||
chat_type: ChatType,
|
||||
title: str | None,
|
||||
handle: str | None,
|
||||
description: str | None,
|
||||
is_public: bool,
|
||||
is_saved: bool = False,
|
||||
) -> Chat:
|
||||
chat = Chat(
|
||||
type=chat_type,
|
||||
title=title,
|
||||
handle=handle,
|
||||
description=description,
|
||||
is_public=is_public,
|
||||
is_saved=is_saved,
|
||||
)
|
||||
db.add(chat)
|
||||
await db.flush()
|
||||
return chat
|
||||
|
||||
|
||||
async def add_chat_member(db: AsyncSession, *, chat_id: int, user_id: int, role: ChatMemberRole) -> ChatMember:
|
||||
member = ChatMember(chat_id=chat_id, user_id=user_id, role=role)
|
||||
db.add(member)
|
||||
@@ -61,6 +84,11 @@ async def get_chat_by_id(db: AsyncSession, chat_id: int) -> Chat | None:
|
||||
return result.scalar_one_or_none()
|
||||
|
||||
|
||||
async def get_chat_by_handle(db: AsyncSession, handle: str) -> Chat | None:
|
||||
result = await db.execute(select(Chat).where(Chat.handle == handle))
|
||||
return result.scalar_one_or_none()
|
||||
|
||||
|
||||
async def get_chat_member(db: AsyncSession, *, chat_id: int, user_id: int) -> ChatMember | None:
|
||||
result = await db.execute(
|
||||
select(ChatMember).where(
|
||||
@@ -83,6 +111,38 @@ async def list_user_chat_ids(db: AsyncSession, *, user_id: int) -> list[int]:
|
||||
return list(result.scalars().all())
|
||||
|
||||
|
||||
async def find_saved_chat_for_user(db: AsyncSession, *, user_id: int) -> Chat | None:
|
||||
stmt = (
|
||||
select(Chat)
|
||||
.join(ChatMember, ChatMember.chat_id == Chat.id)
|
||||
.where(ChatMember.user_id == user_id, Chat.is_saved.is_(True))
|
||||
.limit(1)
|
||||
)
|
||||
result = await db.execute(stmt)
|
||||
return result.scalar_one_or_none()
|
||||
|
||||
|
||||
async def discover_public_chats(
|
||||
db: AsyncSession,
|
||||
*,
|
||||
user_id: int,
|
||||
query: str | None = None,
|
||||
limit: int = 30,
|
||||
) -> list[tuple[Chat, bool]]:
|
||||
q = select(Chat).where(Chat.is_public.is_(True), Chat.type.in_([ChatType.GROUP, ChatType.CHANNEL]), Chat.is_saved.is_(False))
|
||||
if query and query.strip():
|
||||
like = f"%{query.strip()}%"
|
||||
q = q.where(or_(Chat.title.ilike(like), Chat.handle.ilike(like), Chat.description.ilike(like)))
|
||||
q = q.order_by(Chat.id.desc()).limit(limit)
|
||||
chats = list((await db.execute(q)).scalars().all())
|
||||
if not chats:
|
||||
return []
|
||||
chat_ids = [c.id for c in chats]
|
||||
m_stmt = select(ChatMember.chat_id).where(ChatMember.user_id == user_id, ChatMember.chat_id.in_(chat_ids))
|
||||
memberships = set((await db.execute(m_stmt)).scalars().all())
|
||||
return [(chat, chat.id in memberships) for chat in chats]
|
||||
|
||||
|
||||
async def find_private_chat_between_users(db: AsyncSession, *, user_a_id: int, user_b_id: int) -> Chat | None:
|
||||
cm_a = aliased(ChatMember)
|
||||
cm_b = aliased(ChatMember)
|
||||
|
||||
Reference in New Issue
Block a user