Clean up room widget and add nick list

This commit is contained in:
Joscha 2019-06-21 15:33:13 +00:00
parent d3cd63b67e
commit d7f6d5a536

View file

@ -1,8 +1,8 @@
import asyncio import asyncio
from enum import Enum
from typing import Any, Awaitable, Callable, List, Optional, Tuple, TypeVar from typing import Any, Awaitable, Callable, List, Optional, Tuple, TypeVar
import urwid import urwid
import yaboli import yaboli
from ..attributed_text_widget import ATWidget from ..attributed_text_widget import ATWidget
@ -14,6 +14,7 @@ from ..markup import AT, AttributedText, Attributes
from .edit_widgets import EditWidget from .edit_widgets import EditWidget
from .euph_config import EuphConfig from .euph_config import EuphConfig
from .euph_renderer import EuphRenderer from .euph_renderer import EuphRenderer
from .nick_list_widget import NickListWidget
__all__ = ["RoomWidget"] __all__ = ["RoomWidget"]
@ -129,23 +130,33 @@ class RoomLayout(urwid.WidgetWrap):
string = self._edit_separator * tree_width string = self._edit_separator * tree_width
return AT(string, attributes=self._border_attrs) return AT(string, attributes=self._border_attrs)
def set_edit_visible(self, visible: bool): def set_edit_visible(self, visible: bool) -> None:
if visible: if visible:
self._left_wrap._w = self._edit_pile self._left_wrap._w = self._edit_pile
else: else:
self._left_wrap._w = self._tree self._left_wrap._w = self._tree
def focus_on_edit(self): def focus_on_edit(self) -> None:
self._edit_pile.focus_position = 2 self._edit_pile.focus_position = 2
self._columns.focus_position = 0 self._columns.focus_position = 0
def focus_on_tree(self): def focus_on_tree(self) -> None:
self._edit_pile.focus_position = 0 self._edit_pile.focus_position = 0
self._columns.focus_position = 0 self._columns.focus_position = 0
def focus_on_user_list(self): def focus_on_nick_list(self) -> None:
self._columns.focus_position = 2 self._columns.focus_position = 2
class UiMode(Enum):
CONNECTING = "connecting"
CONNECTION_FAILED = "connection failed"
SETTING_PASSWORD = "setting password"
AUTHENTICATING = "authenticating"
SETTING_NICK = "setting nick"
VIEWING = "viewing"
EDITING = "editing"
class RoomWidget(urwid.WidgetWrap): class RoomWidget(urwid.WidgetWrap):
""" """
The RoomWidget connects to and displays a single yaboli room. The RoomWidget connects to and displays a single yaboli room.
@ -159,11 +170,6 @@ class RoomWidget(urwid.WidgetWrap):
event event
""" """
CONNECTING = "connecting"
CONNECTION_FAILED = "connection_failed"
VIEWING = "viewing"
EDITING = "editing"
def __init__(self, def __init__(self,
roomname: str, roomname: str,
config: EuphConfig, config: EuphConfig,
@ -172,17 +178,24 @@ class RoomWidget(urwid.WidgetWrap):
self.c = config self.c = config
if log_amount < 1:
raise ValueError() # TODO add better text
self._log_amount = log_amount self._log_amount = log_amount
if self._log_amount < 1:
raise ValueError("log request amount must be at least 1")
self._mode: str self._mode: UiMode
self._requesting_logs = False self._requesting_logs = False
self._hit_top_of_supply = False self._hit_top_of_supply = False
self._room = yaboli.Room(roomname) self._room = yaboli.Room(roomname)
self._room.register_event("connected", self.on_connected)
self._room.register_event("snapshot", self.on_snapshot) self._room.register_event("snapshot", self.on_snapshot)
self._room.register_event("send", self.on_send) self._room.register_event("send", self.on_send)
self._room.register_event("join", self.on_join)
self._room.register_event("part", self.on_part)
self._room.register_event("nick", self.on_nick)
self._room.register_event("edit", self.on_edit)
self._room.register_event("disconnect", self.on_disconnect)
self._supply = InMemorySupply[Message]() self._supply = InMemorySupply[Message]()
self._renderer = self._create_euph_renderer() self._renderer = self._create_euph_renderer()
@ -297,7 +310,12 @@ class RoomWidget(urwid.WidgetWrap):
return urwid.Edit(multiline=True) return urwid.Edit(multiline=True)
def _create_nick_list_widget(self) -> Any: def _create_nick_list_widget(self) -> Any:
return urwid.SolidFill("n") return NickListWidget(
heading_attrs={"style": self.c.nick_list_heading_style},
counter_attrs={"style": self.c.nick_list_counter_style},
nick_attrs={"style": self.c.nick_style},
own_nick_attrs={"style": self.c.own_nick_style},
)
def _create_room_layout_widget(self, def _create_room_layout_widget(self,
room_name: Any, room_name: Any,
@ -336,50 +354,42 @@ class RoomWidget(urwid.WidgetWrap):
## UI mode and mode switching ## UI mode and mode switching
CONNECTING = "connecting"
CONNECTION_FAILED = "connection_failed"
SETTING_PASSWORD = "setting_password"
AUTHENTICATING = "authenticating"
SETTING_NICK = "setting_nick"
VIEWING = "viewing"
EDITING = "editing"
def switch_connecting(self) -> None: def switch_connecting(self) -> None:
self._w = self._connecting self._w = self._connecting
self._mode = self.CONNECTING self._mode = UiMode.CONNECTING
def switch_connection_failed(self) -> None: def switch_connection_failed(self) -> None:
self._w = self._connection_failed self._w = self._connection_failed
self._mode = self.CONNECTION_FAILED self._mode = UiMode.CONNECTION_FAILED
def switch_setting_password(self) -> None: def switch_setting_password(self) -> None:
self._w = self._overlay self._w = self._overlay
self._overlay.set_top(self._edit_password) self._overlay.set_top(self._edit_password)
self._mode = self.SETTING_PASSWORD self._mode = UiMode.SETTING_PASSWORD
def switch_authenticating(self) -> None: def switch_authenticating(self) -> None:
self._w = self._overlay self._w = self._overlay
self._overlay.set_top(self._authenticating) self._overlay.set_top(self._authenticating)
self._mode = self.AUTHENTICATING self._mode = UiMode.AUTHENTICATING
def switch_setting_nick(self) -> None: def switch_setting_nick(self) -> None:
self._w = self._overlay self._w = self._overlay
self._box.original_widget = self._edit_nick self._box.original_widget = self._edit_nick
self._edit_nick.text = self._room.session.nick self._edit_nick.text = self._room.session.nick
self.update_edit_nick() self.update_edit_nick()
self._mode = self.SETTING_NICK self._mode = UiMode.SETTING_NICK
def switch_view(self) -> None: def switch_view(self) -> None:
self._w = self._layout self._w = self._layout
self._layout.set_edit_visible(False) self._layout.set_edit_visible(False)
self._layout.focus_on_tree() self._layout.focus_on_tree()
self._mode = self.VIEWING self._mode = UiMode.VIEWING
def switch_edit(self) -> None: def switch_edit(self) -> None:
self._w = self._layout self._w = self._layout
self._layout.set_edit_visible(True) self._layout.set_edit_visible(True)
self._layout.focus_on_edit() self._layout.focus_on_edit()
self._mode = self.EDITING self._mode = UiMode.EDITING
# Updating various parts of the UI # Updating various parts of the UI
@ -388,12 +398,15 @@ class RoomWidget(urwid.WidgetWrap):
def update_nick_list(self) -> None: def update_nick_list(self) -> None:
# Ensure that self._room.session and self._room.users exist # Ensure that self._room.session and self._room.users exist
if self._mode not in {self.SETTING_NICK, self.VIEWING, self.EDITING}: allowed = {UiMode.SETTING_NICK, UiMode.VIEWING, UiMode.EDITING}
if self._mode not in allowed:
return return
#self._nick_list.update(self._room.session, self._room.users) # Automatically rerenders
self._nick_list.session = self._room.session
self._nick_list.users = self._room.users
def update_edit_nick(self): def update_edit_nick(self) -> None:
width = self._edit_nick.width width = self._edit_nick.width
self._overlay.set_overlay_parameters( self._overlay.set_overlay_parameters(
align=urwid.CENTER, align=urwid.CENTER,
@ -403,15 +416,15 @@ class RoomWidget(urwid.WidgetWrap):
) )
self._overlay._invalidate() self._overlay._invalidate()
# Reacting to changes def change_own_nick(self) -> None:
def own_nick_change(self):
self._renderer.nick = self._room.session.nick self._renderer.nick = self._room.session.nick
self._tree.invalidate_all() self._tree.invalidate_all()
self.update_tree() self.update_tree()
self._nick_list.session = self._room.session
self.update_nick_list() self.update_nick_list()
def receive_message(self, msg: yaboli.Message): def receive_message(self, msg: yaboli.Message) -> None:
self._supply.add(Message( self._supply.add(Message(
msg.message_id, msg.message_id,
msg.parent_id, msg.parent_id,
@ -434,7 +447,7 @@ class RoomWidget(urwid.WidgetWrap):
return canvas return canvas
def keypress(self, size: Tuple[int, int], key: str) -> Optional[str]: def keypress(self, size: Tuple[int, int], key: str) -> Optional[str]:
if self._mode == self.VIEWING: if self._mode == UiMode.VIEWING:
if key in {"enter", "meta enter"} and not self._room.session.nick: if key in {"enter", "meta enter"} and not self._room.session.nick:
self.switch_setting_nick() self.switch_setting_nick()
elif key == "enter": elif key == "enter":
@ -452,7 +465,7 @@ class RoomWidget(urwid.WidgetWrap):
else: else:
return super().keypress(size, key) return super().keypress(size, key)
elif self._mode == self.EDITING: elif self._mode == UiMode.EDITING:
if key == "enter": if key == "enter":
if self._edit.edit_text: if self._edit.edit_text:
self.send(self._edit.edit_text, self._tree.cursor_id) self.send(self._edit.edit_text, self._tree.cursor_id)
@ -464,7 +477,7 @@ class RoomWidget(urwid.WidgetWrap):
else: else:
return super().keypress(size, key) return super().keypress(size, key)
elif self._mode == self.SETTING_NICK: elif self._mode == UiMode.SETTING_NICK:
if key == "enter": if key == "enter":
if self._edit_nick.text: if self._edit_nick.text:
self.nick(self._edit_nick.text) self.nick(self._edit_nick.text)
@ -485,37 +498,62 @@ class RoomWidget(urwid.WidgetWrap):
# Reacting to euph events # Reacting to euph events
async def on_snapshot(self, messages: List[yaboli.Message]): async def on_connected(self) -> None:
pass
async def on_snapshot(self, messages: List[yaboli.LiveMessage]) -> None:
for message in messages: for message in messages:
self.receive_message(message) self.receive_message(message)
self.update_tree()
async def on_send(self, message: yaboli.Message): self.update_nick_list()
async def on_send(self, message: yaboli.LiveMessage) -> None:
self.receive_message(message) self.receive_message(message)
self.update_tree()
async def on_join(self, user: yaboli.LiveSession) -> None:
self.update_nick_list()
async def on_part(self, user: yaboli.LiveSession) -> None:
self.update_nick_list()
async def on_nick(self,
user: yaboli.LiveSession,
from_: str,
to: str,
) -> None:
self.update_nick_list()
async def on_edit(self, message: yaboli.LiveMessage) -> None:
self.receive_message(message)
async def on_disconnect(self, reason: str) -> None:
pass
# Euph actions # Euph actions
@synchronous @synchronous
async def request_logs(self): async def request_logs(self) -> None:
oldest_id = self._supply.oldest_id() oldest_id = self._supply.oldest_id()
if oldest_id is not None: if oldest_id is not None:
messages = await self._room.log(self._log_amount, oldest_id) messages = await self._room.log(self._log_amount, oldest_id)
if len(messages) == 0:
self._hit_top_of_supply = True
for message in messages: for message in messages:
self.receive_message(message) self.receive_message(message)
self.update_tree()
self._requesting_logs = False self._requesting_logs = False
@synchronous @synchronous
async def nick(self, nick: str): async def nick(self, nick: str) -> None:
new_nick = await self._room.nick(nick) await self._room.nick(nick)
self.own_nick_change() self.change_own_nick()
@synchronous @synchronous
async def send(self, content: str, parent_id: Optional[str]): async def send(self, content: str, parent_id: Optional[str]) -> None:
message = await self._room.send(content, parent_id=parent_id) message = await self._room.send(content, parent_id=parent_id)
self.receive_message(message) self.receive_message(message)
self.update_tree()
urwid.register_signal(RoomWidget, ["close"]) urwid.register_signal(RoomWidget, ["close"])