From 2a5c04aafe23c03cea267c898a671a0d2a67e73f Mon Sep 17 00:00:00 2001 From: Maxim Fomin Date: Tue, 21 May 2024 01:56:10 +0300 Subject: [PATCH] feat: add "leave room" feature --- src/bot/dialogs/confirmation.py | 2 +- src/bot/dialogs/incoming_invitations.py | 148 ++++++++++++------------ src/bot/dialogs/room.py | 61 ++++++++-- src/bot/dialogs/roomless.py | 38 +++--- src/bot/dialogs/states.py | 1 - 5 files changed, 149 insertions(+), 101 deletions(-) diff --git a/src/bot/dialogs/confirmation.py b/src/bot/dialogs/confirmation.py index f088643..8f2f483 100644 --- a/src/bot/dialogs/confirmation.py +++ b/src/bot/dialogs/confirmation.py @@ -24,7 +24,7 @@ async def on_select(callback: CallbackQuery, select, manager: DialogManager, ite answer = confirmation.yes_message if confirmed else confirmation.no_message if answer is not None: await callback.bot.send_message(callback.message.chat.id, answer) - await manager.done(confirmed, show_mode=ShowMode.SEND) + await manager.done(confirmed, show_mode=ShowMode.NO_UPDATE) confirmation_dialog = Dialog( diff --git a/src/bot/dialogs/incoming_invitations.py b/src/bot/dialogs/incoming_invitations.py index 28307e9..3af9b2e 100644 --- a/src/bot/dialogs/incoming_invitations.py +++ b/src/bot/dialogs/incoming_invitations.py @@ -31,84 +31,90 @@ async def load_invitations(manager: DialogManager): manager.dialog_data["invitations"] = invitations_data -async def on_start(_, manager: DialogManager): - await Loader.load_invitations(manager) - - -def select_invitation(func): - async def wrapped(callback: CallbackQuery, widget, manager: DialogManager): - assert isinstance(manager, SubManager) - - invitations: list[IncomingInvitationInfo] = manager.dialog_data["invitations"] - for i in invitations: - if i.id == int(manager.item_id): - manager.dialog_data["selected_item"] = i - break - else: - raise RuntimeError("Selected non-existent room") +class Events: + @staticmethod + async def on_start(_, manager: DialogManager): + await Loader.load_invitations(manager) - await func(callback, widget, manager) + @staticmethod + def _select_invitation(func): + async def wrapped(callback: CallbackQuery, widget, manager: DialogManager): + assert isinstance(manager, SubManager) - return wrapped + invitations: list[IncomingInvitationInfo] = manager.dialog_data["invitations"] + for i in invitations: + if i.id == int(manager.item_id): + manager.dialog_data["selected_item"] = i + break + else: + raise RuntimeError("Selected non-existent room") + await func(callback, widget, manager) -@select_invitation -async def on_accept_invitation(callback: CallbackQuery, widget, manager: DialogManager): - invitation: IncomingInvitationInfo = manager.dialog_data["selected_item"] - room_name: str = invitation.room_name + return wrapped - await manager.start( - ConfirmationSG.main, - data={ - "intent": "accept", - "input": dataclasses.asdict( - ConfirmationDialogStartData( - f"you want to accept the invitation to {room_name}", - "Accepted", - ) - ), - }, - show_mode=ShowMode.SEND, - ) - - -@select_invitation -async def on_reject_invitation(callback: CallbackQuery, widget, manager: DialogManager): - invitation: IncomingInvitationInfo = manager.dialog_data["selected_item"] - room_name: str = invitation.room_name - - await manager.start( - ConfirmationSG.main, - data={ - "intent": "reject", - "input": dataclasses.asdict( - ConfirmationDialogStartData( - f"you want to reject the invitation to {room_name}", - "Rejected", - ) - ), - }, - show_mode=ShowMode.SEND, - ) + @staticmethod + @_select_invitation + async def on_accept_invitation(callback: CallbackQuery, widget, manager: DialogManager): + invitation: IncomingInvitationInfo = manager.dialog_data["selected_item"] + room_name: str = invitation.room_name + await manager.start( + ConfirmationSG.main, + data={ + "intent": "accept", + "input": dataclasses.asdict( + ConfirmationDialogStartData( + f"you want to accept the invitation to {room_name}", + "Accepted", + ) + ), + }, + show_mode=ShowMode.SEND, + ) -async def process_result(start_data: dict, result: dict, manager: DialogManager): - if not start_data or not result: - return + @staticmethod + @_select_invitation + async def on_reject_invitation(callback: CallbackQuery, widget, manager: DialogManager): + invitation: IncomingInvitationInfo = manager.dialog_data["selected_item"] + room_name: str = invitation.room_name - invitation: IncomingInvitationInfo = manager.dialog_data["selected_item"] - user_id = manager.event.from_user.id - if start_data["intent"] == "accept": - await client.accept_invitation(invitation.id, user_id) await manager.start( - RoomSG.main, - data={"input": dataclasses.asdict(RoomDialogStartData(invitation.room, invitation.room_name))}, - mode=StartMode.RESET_STACK, + ConfirmationSG.main, + data={ + "intent": "reject", + "input": dataclasses.asdict( + ConfirmationDialogStartData( + f"you want to reject the invitation to {room_name}", + "Rejected", + ) + ), + }, show_mode=ShowMode.SEND, ) - elif start_data["intent"] == "reject": - await client.reject_invitation(invitation.id, user_id) - await Loader.load_invitations(manager) + + @staticmethod + async def on_process_result(start_data: dict, result: bool, manager: DialogManager): + if not isinstance(start_data, dict): + return + if not result: + await manager.show(ShowMode.SEND) + return + + invitation: IncomingInvitationInfo = manager.dialog_data["selected_item"] + user_id = manager.event.from_user.id + if start_data["intent"] == "accept": + await client.accept_invitation(invitation.id, user_id) + await manager.start( + RoomSG.main, + data={"input": dataclasses.asdict(RoomDialogStartData(invitation.room, invitation.room_name))}, + mode=StartMode.RESET_STACK, + show_mode=ShowMode.SEND, + ) + elif start_data["intent"] == "reject": + await client.reject_invitation(invitation.id, user_id) + await Loader.load_invitations(manager) + await manager.show(ShowMode.SEND) async def invitations_getter(dialog_manager: DialogManager, **kwargs): @@ -148,12 +154,12 @@ async def invitations_getter(dialog_manager: DialogManager, **kwargs): Row( Button( Format("{pos}. {item.room_name}"), - on_click=on_accept_invitation, + on_click=Events.on_accept_invitation, id=InvitationsWindowConsts.ACCEPT_BUTTON_ID, ), Button( Const("Reject"), - on_click=on_reject_invitation, + on_click=Events.on_reject_invitation, id=InvitationsWindowConsts.REJECT_BUTTON_ID, ), ), @@ -164,6 +170,6 @@ async def invitations_getter(dialog_manager: DialogManager, **kwargs): state=IncomingInvitationsSG.list, getter=invitations_getter, ), - on_process_result=process_result, - on_start=on_start, + on_process_result=Events.on_process_result, + on_start=Events.on_start, ) diff --git a/src/bot/dialogs/room.py b/src/bot/dialogs/room.py index 4964c9f..32203ee 100644 --- a/src/bot/dialogs/room.py +++ b/src/bot/dialogs/room.py @@ -1,19 +1,22 @@ +import dataclasses + from aiogram.types import CallbackQuery -from aiogram_dialog import Dialog, Window, DialogManager +from aiogram_dialog import Dialog, Window, DialogManager, ShowMode, StartMode from aiogram_dialog.widgets.kbd import Row, Button, SwitchTo from aiogram_dialog.widgets.text import Format, Const, List from src.api import client from src.api.schemas.method_output_schemas import DailyInfoResponse, UserInfo -from src.bot.dialogs.communication import RoomDialogStartData -from src.bot.dialogs.states import RoomSG +from src.bot.dialogs.communication import RoomDialogStartData, ConfirmationDialogStartData +from src.bot.dialogs.states import RoomSG, ConfirmationSG, RoomlessSG class MainWindowConsts: REFRESH_BUTTON_ID = "refresh_button" ROOMMATES_BUTTON_ID = "roommates_button" TASKS_BUTTON_ID = "tasks_button" - INVITATIONS_BUTTON_ID = "invitations_button" + INBOX_BUTTON_ID = "incoming_invitations_button" + MY_INVITATIONS_BUTTON_ID = "my_invitations_button" LEAVE_BUTTON_ID = "leave_button" @@ -33,9 +36,45 @@ async def getter(dialog_manager: DialogManager, **kwargs): } -async def on_start(start_data: dict, manager: DialogManager): - manager.dialog_data["room_info"] = RoomDialogStartData(**start_data["input"]) - await Loader.load_daily_info(manager) +class Events: + @staticmethod + async def on_start(start_data: dict, manager: DialogManager): + manager.dialog_data["room_info"] = RoomDialogStartData(**start_data["input"]) + await Loader.load_daily_info(manager) + + @staticmethod + async def on_leave(callback: CallbackQuery, button, manager: DialogManager): + room_name: str = manager.dialog_data["room_info"].name + await manager.start( + ConfirmationSG.main, + data={ + "intent": "leave", + "input": dataclasses.asdict( + ConfirmationDialogStartData( + "you want to leave the room", + f'You have left the room "{room_name}"', + ) + ), + }, + show_mode=ShowMode.SEND, + ) + + @staticmethod + async def on_process_result(start_data: dict, result: bool, manager: DialogManager): + if not isinstance(start_data, dict): + return + if not result: + await manager.show(ShowMode.SEND) + return + + user_id = manager.event.from_user.id + if start_data["intent"] == "leave": + await client.leave_room(user_id) + await manager.start( + RoomlessSG.welcome, + mode=StartMode.RESET_STACK, + show_mode=ShowMode.SEND, + ) class Loader: @@ -87,8 +126,9 @@ async def load_roommates(manager: DialogManager): Button(Const("Tasks"), MainWindowConsts.TASKS_BUTTON_ID), ), Row( - Button(Const("Invitations"), MainWindowConsts.INVITATIONS_BUTTON_ID), - Button(Const("Leave"), MainWindowConsts.LEAVE_BUTTON_ID), + Button(Const("Inbox"), MainWindowConsts.INBOX_BUTTON_ID), + Button(Const("My invitations"), MainWindowConsts.MY_INVITATIONS_BUTTON_ID), + Button(Const("Leave"), MainWindowConsts.LEAVE_BUTTON_ID, on_click=Events.on_leave), ), getter=getter, state=RoomSG.main, @@ -108,5 +148,6 @@ async def load_roommates(manager: DialogManager): state=RoomSG.roommates, getter=getter, ), - on_start=on_start, + on_start=Events.on_start, + on_process_result=Events.on_process_result, ) diff --git a/src/bot/dialogs/roomless.py b/src/bot/dialogs/roomless.py index cd456e8..0a094e3 100644 --- a/src/bot/dialogs/roomless.py +++ b/src/bot/dialogs/roomless.py @@ -20,22 +20,24 @@ class WelcomeWindowConsts: CREATE_ROOM_BUTTON_ID = "create_room_button" -async def process_create_room_result(start_data: Data, result: CreateRoomDialogResult, manager: DialogManager): - if not start_data: - return - - if start_data["intent"] == "create_room": - if result.created: - room_id = await client.create_room(result.name, manager.event.from_user.id) - await manager.start( - RoomSG.main, - data={"input": dataclasses.asdict(RoomDialogStartData(room_id, result.name))}, - mode=StartMode.RESET_STACK, - ) - - -async def start_creating_room(event: CallbackQuery, button: Button, dialog_manager: DialogManager): - await dialog_manager.start(CreateRoomSG.enter_name, data={"intent": "create_room"}, show_mode=ShowMode.SEND) +class Events: + @staticmethod + async def process_create_room_result(start_data: Data, result: CreateRoomDialogResult, manager: DialogManager): + if not isinstance(start_data, dict): + return + + if start_data["intent"] == "create_room": + if result.created: + room_id = await client.create_room(result.name, manager.event.from_user.id) + await manager.start( + RoomSG.main, + data={"input": dataclasses.asdict(RoomDialogStartData(room_id, result.name))}, + mode=StartMode.RESET_STACK, + ) + + @staticmethod + async def on_click_create_room(event: CallbackQuery, button: Button, dialog_manager: DialogManager): + await dialog_manager.start(CreateRoomSG.enter_name, data={"intent": "create_room"}, show_mode=ShowMode.SEND) roomless_dialog = Dialog( @@ -51,10 +53,10 @@ async def start_creating_room(event: CallbackQuery, button: Button, dialog_manag Button( Const("Create"), WelcomeWindowConsts.CREATE_ROOM_BUTTON_ID, - on_click=start_creating_room, + on_click=Events.on_click_create_room, ), ), state=RoomlessSG.welcome, ), - on_process_result=process_create_room_result, + on_process_result=Events.process_create_room_result, ) diff --git a/src/bot/dialogs/states.py b/src/bot/dialogs/states.py index 57012e7..8983b5f 100644 --- a/src/bot/dialogs/states.py +++ b/src/bot/dialogs/states.py @@ -13,7 +13,6 @@ class RoomSG(StatesGroup): main = State() roommates = State() invitations = State() - leave = State() class IncomingInvitationsSG(StatesGroup):