Add a batch set_fields method to Model

This commit is contained in:
miruka 2020-07-01 12:00:50 -04:00
parent 5476e00b9b
commit 4752abf6e5
4 changed files with 75 additions and 43 deletions

View File

@ -300,14 +300,15 @@ class Backend:
""" """
async def update(client: MatrixClient) -> None: async def update(client: MatrixClient) -> None:
room = self.models[client.user_id, "rooms"].get(room_id) room = self.models[client.user_id, "rooms"].get(room_id)
local = room.local_unreads or room.local_highlights
if room and (room.unreads or room.highlights or local): if room:
room.unreads = 0 room.set_fields(
room.highlights = 0 unreads = 0,
room.local_unreads = False highlights = 0,
room.local_highlights = False local_unreads = False,
local_highlights = False,
)
await client.update_account_unread_counts() await client.update_account_unread_counts()
await client.update_receipt_marker(room_id, event_id) await client.update_receipt_marker(room_id, event_id)

View File

@ -353,10 +353,12 @@ class MatrixClient(nio.AsyncClient):
resp = await self.backend.get_profile(self.user_id, use_cache=False) resp = await self.backend.get_profile(self.user_id, use_cache=False)
account = self.models["accounts"][self.user_id] account = self.models["accounts"][self.user_id]
account.profile_updated = datetime.now() account.set_fields(
account.display_name = resp.displayname or "" profile_updated = datetime.now(),
account.avatar_url = resp.avatar_url or "" display_name = resp.displayname or "",
avatar_url = resp.avatar_url or "",
)
async def get_server_config(self) -> int: async def get_server_config(self) -> int:
@ -528,8 +530,10 @@ class MatrixClient(nio.AsyncClient):
upload_item.uploaded = transferred upload_item.uploaded = transferred
def on_speed_changed(speed: float) -> None: def on_speed_changed(speed: float) -> None:
upload_item.speed = speed upload_item.set_fields(
upload_item.time_left = monitor.remaining_time or timedelta(0) speed = speed,
time_left = monitor.remaining_time or timedelta(0),
)
monitor.on_transferred = on_transferred monitor.on_transferred = on_transferred
monitor.on_speed_changed = on_speed_changed monitor.on_speed_changed = on_speed_changed
@ -548,9 +552,11 @@ class MatrixClient(nio.AsyncClient):
raise nio.TransferCancelledError() raise nio.TransferCancelledError()
except (MatrixError, OSError) as err: except (MatrixError, OSError) as err:
upload_item.status = UploadStatus.Error upload_item.set_fields(
upload_item.error = type(err) status = UploadStatus.Error,
upload_item.error_args = err.args error = type(err),
error_args = err.args,
)
# Wait for cancellation from UI, see parent send_file() method # Wait for cancellation from UI, see parent send_file() method
while True: while True:
@ -605,9 +611,11 @@ class MatrixClient(nio.AsyncClient):
thumb_ext = "png" if thumb_info.mime == "image/png" else "jpg" thumb_ext = "png" if thumb_info.mime == "image/png" else "jpg"
thumb_name = f"{path.stem}_thumbnail.{thumb_ext}" thumb_name = f"{path.stem}_thumbnail.{thumb_ext}"
upload_item.status = UploadStatus.Uploading upload_item.set_fields(
upload_item.filepath = Path(thumb_name) status = UploadStatus.Uploading,
upload_item.total_size = len(thumb_data) filepath = Path(thumb_name),
total_size = len(thumb_data),
)
try: try:
upload_item.total_size = thumb_info.size upload_item.total_size = thumb_info.size
@ -1384,11 +1392,13 @@ class MatrixClient(nio.AsyncClient):
if room.local_highlights: if room.local_highlights:
local_highlights = True local_highlights = True
account = self.models["accounts"][self.user_id] account = self.models["accounts"][self.user_id]
account.total_unread = unreads account.set_fields(
account.total_highlights = highlights total_unread = unreads,
account.local_unreads = local_unreads total_highlights = highlights,
account.local_highlights = local_highlights local_unreads = local_unreads,
local_highlights = local_highlights,
)
async def event_is_past(self, ev: Union[nio.Event, Event]) -> bool: async def event_is_past(self, ev: Union[nio.Event, Event]) -> bool:
@ -1552,13 +1562,11 @@ class MatrixClient(nio.AsyncClient):
if not ev.sender_name and not ev.sender_avatar: if not ev.sender_name and not ev.sender_avatar:
sender_name, sender_avatar, _ = await get_profile(ev.sender_id) sender_name, sender_avatar, _ = await get_profile(ev.sender_id)
ev.sender_name = sender_name ev.set_fields(sender_name=sender_name, sender_avatar=sender_avatar)
ev.sender_avatar = sender_avatar
if ev.target_id and not ev.target_name and not ev.target_avatar: if ev.target_id and not ev.target_name and not ev.target_avatar:
target_name, target_avatar, _ = await get_profile(ev.target_id) target_name, target_avatar, _ = await get_profile(ev.target_id)
ev.target_name = target_name ev.set_fields(target_name=target_name, target_avatar=target_avatar)
ev.target_avatar = target_avatar
if ev.redacter_id and not ev.redacter_name: if ev.redacter_id and not ev.redacter_name:
redacter_name, _, _ = await get_profile(ev.target_id) redacter_name, _, _ = await get_profile(ev.target_id)

View File

@ -33,32 +33,34 @@ class ModelItem:
def __setattr__(self, name: str, value) -> None: def __setattr__(self, name: str, value) -> None:
"""If this item is in a `Model`, alert it of attribute changes.""" """If this item is in a `Model`, alert it of attribute changes."""
if name == "parent_model" or not self.parent_model: if (
name == "parent_model" or
not self.parent_model or
getattr(self, name) == value
):
super().__setattr__(name, value) super().__setattr__(name, value)
return return
if getattr(self, name) == value:
return
super().__setattr__(name, value) super().__setattr__(name, value)
self._notify_parent_model({name: self.serialize_field(name)})
def _notify_parent_model(self, changed_fields: Dict[str, Any]) -> None:
parent = self.parent_model parent = self.parent_model
if not parent.sync_id: if not parent or not parent.sync_id or not changed_fields:
return return
fields = {name: self.serialize_field(name)}
with parent.write_lock: with parent.write_lock:
index_then = parent._sorted_data.index(self) index_then = parent._sorted_data.index(self)
parent._sorted_data.sort() parent._sorted_data.sort()
index_now = parent._sorted_data.index(self) index_now = parent._sorted_data.index(self)
ModelItemSet(parent.sync_id, index_then, index_now, fields) ModelItemSet(parent.sync_id, index_then, index_now, changed_fields)
for sync_id, proxy in parent.proxies.items(): for sync_id, proxy in parent.proxies.items():
if sync_id != parent.sync_id: if sync_id != parent.sync_id:
proxy.source_item_set(parent, self.id, self, fields) proxy.source_item_set(parent, self.id, self, changed_fields)
def __delattr__(self, name: str) -> None: def __delattr__(self, name: str) -> None:
@ -82,3 +84,20 @@ class ModelItem:
name.startswith("_") or name in ("parent_model", "serialized") name.startswith("_") or name in ("parent_model", "serialized")
) )
} }
def set_fields(self, **fields: Any) -> None:
"""Set multiple fields's values at once.
The parent model will be resorted only once, and one `ModelItemSet`
event will be sent informing QML of all the changed fields.
"""
for name, value in fields.copy().items():
if getattr(self, name) == value:
del fields[name]
else:
super().__setattr__(name, value)
fields[name] = self.serialize_field(name)
self._notify_parent_model(fields)

View File

@ -388,9 +388,11 @@ class NioCallbacks:
account = self.models["accounts"][self.user_id] account = self.models["accounts"][self.user_id]
if account.profile_updated < ev_date: if account.profile_updated < ev_date:
account.profile_updated = ev_date account.set_fields(
account.display_name = now.get("displayname") or "" profile_updated = ev_date,
account.avatar_url = now.get("avatar_url") or "" display_name = now.get("displayname") or "",
avatar_url = now.get("avatar_url") or "",
)
if self.client.backend.ui_settings["hideProfileChangeEvents"]: if self.client.backend.ui_settings["hideProfileChangeEvents"]:
return None return None
@ -568,6 +570,8 @@ class NioCallbacks:
member = model.get(receipt.user_id) member = model.get(receipt.user_id)
if member: if member:
member.last_read_event = receipt.event_id timestamp = receipt.timestamp / 1000
timestamp = receipt.timestamp / 1000 member.set_fields(
member.last_read_at = datetime.fromtimestamp(timestamp) last_read_event = receipt.event_id,
last_read_at = datetime.fromtimestamp(timestamp),
)