Begin yet another model refactor

Use native ListModel which require a lot of changes, but should be
much faster than the old way which exponentially slowed down to a crawl.
Also fix some popup bugs (leave/forget).

Not working yet: side pane keyboard controls, proper highlight,
room & member filtering, local echo replacement
This commit is contained in:
miruka
2019-12-02 16:29:29 -04:00
parent 2ce5e20efa
commit 9990fecc74
49 changed files with 826 additions and 781 deletions

View File

@@ -11,31 +11,39 @@ from typing import Any, Dict, List, Optional, Tuple, Type, Union
from uuid import UUID
import lxml # nosec
import nio
from ..html_markdown import HTML_PROCESSOR
from ..utils import AutoStrEnum, auto
from .model_item import ModelItem
ZeroDate = datetime.fromtimestamp(0)
OptionalExceptionType = Union[Type[None], Type[Exception]]
class TypeSpecifier(AutoStrEnum):
"""Enum providing clarification of purpose for some matrix events."""
Unset = auto()
ProfileChange = auto()
MembershipChange = auto()
@dataclass
class Account(ModelItem):
"""A logged in matrix account."""
user_id: str = field()
display_name: str = ""
avatar_url: str = ""
first_sync_done: bool = False
profile_updated: Optional[datetime] = None
id: str = field()
display_name: str = ""
avatar_url: str = ""
first_sync_done: bool = False
profile_updated: datetime = ZeroDate
def __lt__(self, other: "Account") -> bool:
"""Sort by display name or user ID."""
name = self.display_name or self.user_id[1:]
other_name = other.display_name or other.user_id[1:]
return name < other_name
name = self.display_name or self.id[1:]
other_name = other.display_name or other.id[1:]
return name.lower() < other_name.lower()
@property
def filter_string(self) -> str:
@@ -47,18 +55,21 @@ class Account(ModelItem):
class Room(ModelItem):
"""A matrix room we are invited to, are or were member of."""
room_id: str = field()
given_name: str = ""
display_name: str = ""
avatar_url: str = ""
plain_topic: str = ""
topic: str = ""
inviter_id: str = ""
inviter_name: str = ""
inviter_avatar: str = ""
left: bool = False
id: str = field()
given_name: str = ""
display_name: str = ""
main_alias: str = ""
avatar_url: str = ""
plain_topic: str = ""
topic: str = ""
inviter_id: str = ""
inviter_name: str = ""
inviter_avatar: str = ""
left: bool = False
typing_members: List[str] = field(default_factory=list)
federated: bool = True
encrypted: bool = False
invite_required: bool = True
guests_allowed: bool = True
@@ -72,7 +83,7 @@ class Room(ModelItem):
can_set_join_rules: bool = False
can_set_guest_access: bool = False
last_event: Optional["Event"] = field(default=None, repr=False)
last_event_date: datetime = ZeroDate
def __lt__(self, other: "Room") -> bool:
"""Sort by join state, then descending last event date, then name.
@@ -85,68 +96,45 @@ class Room(ModelItem):
# Left rooms may still have an inviter_id, so check left first.
return (
self.left,
other.inviter_id,
other.last_event.date if other.last_event else
datetime.fromtimestamp(0),
self.display_name.lower() or self.room_id,
other.last_event_date,
(self.display_name or self.id).lower(),
) < (
other.left,
self.inviter_id,
self.last_event.date if self.last_event else
datetime.fromtimestamp(0),
other.display_name.lower() or other.room_id,
self.last_event_date,
(other.display_name or other.id).lower(),
)
@property
def filter_string(self) -> str:
"""Filter based on room display name, topic, and last event content."""
return " ".join((
self.display_name,
self.topic,
re.sub(r"<.*?>", "", self.last_event.inline_content)
if self.last_event else "",
))
@property
def serialized(self) -> Dict[str, Any]:
dct = super().serialized
if self.last_event is not None:
dct["last_event"] = self.last_event.serialized
return dct
return " ".join((self.display_name, self.topic))
@dataclass
class Member(ModelItem):
"""A member in a matrix room."""
user_id: str = field()
display_name: str = ""
avatar_url: str = ""
typing: bool = False
power_level: int = 0
invited: bool = False
id: str = field()
display_name: str = ""
avatar_url: str = ""
typing: bool = False
power_level: int = 0
invited: bool = False
profile_updated: datetime = ZeroDate
def __lt__(self, other: "Member") -> bool:
"""Sort by power level, then by display name/user ID."""
name = (self.display_name or self.user_id[1:]).lower()
other_name = (other.display_name or other.user_id[1:]).lower()
name = self.display_name or self.id[1:]
other_name = other.display_name or other.id[1:]
return (
self.invited, other.power_level, name,
self.invited, other.power_level, name.lower(),
) < (
other.invited, self.power_level, other_name,
other.invited, self.power_level, other_name.lower(),
)
@@ -168,15 +156,15 @@ class UploadStatus(AutoStrEnum):
class Upload(ModelItem):
"""Represent a running or failed file upload operation."""
uuid: UUID = field()
id: UUID = field()
task: asyncio.Task = field()
monitor: nio.TransferMonitor = field()
filepath: Path = field()
total_size: int = 0
uploaded: int = 0
speed: float = 0
time_left: Optional[timedelta] = None
total_size: int = 0
uploaded: int = 0
speed: float = 0
time_left: timedelta = timedelta(0)
status: UploadStatus = UploadStatus.Uploading
error: OptionalExceptionType = type(None)
@@ -191,21 +179,13 @@ class Upload(ModelItem):
return self.start_date > other.start_date
class TypeSpecifier(AutoStrEnum):
"""Enum providing clarification of purpose for some matrix events."""
none = auto()
profile_change = auto()
membership_change = auto()
@dataclass
class Event(ModelItem):
"""A matrix state event or message."""
source: Optional[nio.Event] = field()
client_id: str = field()
id: str = field()
event_id: str = field()
source: Optional[nio.Event] = field()
date: datetime = field()
sender_id: str = field()
sender_name: str = field()
@@ -213,8 +193,9 @@ class Event(ModelItem):
content: str = ""
inline_content: str = ""
reason: str = ""
type_specifier: TypeSpecifier = TypeSpecifier.none
type_specifier: TypeSpecifier = TypeSpecifier.Unset
target_id: str = ""
target_name: str = ""
@@ -271,12 +252,19 @@ class Event(ModelItem):
return urls
@property
def serialized(self) -> Dict[str, Any]:
dct = super().serialized
del dct["source"]
del dct["local_event_type"]
return dct
@dataclass
class Device(ModelItem):
"""A matrix user's device. This class is currently unused."""
device_id: str = field()
id: str = field()
ed25519_key: str = field()
trusted: bool = False
blacklisted: bool = False