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

View File

@ -33,32 +33,34 @@ class ModelItem:
def __setattr__(self, name: str, value) -> None:
"""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)
return
if getattr(self, name) == value:
return
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
if not parent.sync_id:
if not parent or not parent.sync_id or not changed_fields:
return
fields = {name: self.serialize_field(name)}
with parent.write_lock:
index_then = parent._sorted_data.index(self)
parent._sorted_data.sort()
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():
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:
@ -82,3 +84,20 @@ class ModelItem:
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]
if account.profile_updated < ev_date:
account.profile_updated = ev_date
account.display_name = now.get("displayname") or ""
account.avatar_url = now.get("avatar_url") or ""
account.set_fields(
profile_updated = ev_date,
display_name = now.get("displayname") or "",
avatar_url = now.get("avatar_url") or "",
)
if self.client.backend.ui_settings["hideProfileChangeEvents"]:
return None
@ -568,6 +570,8 @@ class NioCallbacks:
member = model.get(receipt.user_id)
if member:
member.last_read_event = receipt.event_id
timestamp = receipt.timestamp / 1000
member.last_read_at = datetime.fromtimestamp(timestamp)
timestamp = receipt.timestamp / 1000
member.set_fields(
last_read_event = receipt.event_id,
last_read_at = datetime.fromtimestamp(timestamp),
)