Working python proxy/filter for room list
This commit is contained in:
parent
eee198b238
commit
5432958121
|
@ -7,16 +7,20 @@ import traceback
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, DefaultDict, Dict, List, Optional
|
from typing import Any, DefaultDict, Dict, List, Optional
|
||||||
|
|
||||||
import nio
|
|
||||||
from appdirs import AppDirs
|
from appdirs import AppDirs
|
||||||
|
|
||||||
|
import nio
|
||||||
|
|
||||||
from . import __app_name__
|
from . import __app_name__
|
||||||
from .errors import MatrixError
|
from .errors import MatrixError
|
||||||
from .matrix_client import MatrixClient
|
from .matrix_client import MatrixClient
|
||||||
from .media_cache import MediaCache
|
from .media_cache import MediaCache
|
||||||
from .models import SyncId
|
from .models import SyncId
|
||||||
|
from .models.filters import FieldSubstringFilter
|
||||||
from .models.items import Account
|
from .models.items import Account
|
||||||
|
from .models.model import Model
|
||||||
from .models.model_store import ModelStore
|
from .models.model_store import ModelStore
|
||||||
|
from .models.special_models import AllRooms
|
||||||
from .user_files import Accounts, History, Theme, UISettings, UIState
|
from .user_files import Accounts, History, Theme, UISettings, UIState
|
||||||
|
|
||||||
# Logging configuration
|
# Logging configuration
|
||||||
|
@ -77,8 +81,9 @@ class Backend:
|
||||||
self.ui_state: UIState = UIState(self)
|
self.ui_state: UIState = UIState(self)
|
||||||
self.history: History = History(self)
|
self.history: History = History(self)
|
||||||
|
|
||||||
self.models: ModelStore = ModelStore()
|
self.all_rooms: AllRooms = AllRooms()
|
||||||
self.clients: Dict[str, MatrixClient] = {}
|
self.models: ModelStore = ModelStore()
|
||||||
|
self.clients: Dict[str, MatrixClient] = {}
|
||||||
|
|
||||||
self.profile_cache: Dict[str, nio.ProfileGetResponse] = {}
|
self.profile_cache: Dict[str, nio.ProfileGetResponse] = {}
|
||||||
self.get_profile_locks: DefaultDict[str, asyncio.Lock] = \
|
self.get_profile_locks: DefaultDict[str, asyncio.Lock] = \
|
||||||
|
@ -304,3 +309,12 @@ class Backend:
|
||||||
|
|
||||||
await asyncio.sleep(0.1)
|
await asyncio.sleep(0.1)
|
||||||
failures += 1
|
failures += 1
|
||||||
|
|
||||||
|
|
||||||
|
async def set_substring_filter(self, model_id: SyncId, value: str) -> None:
|
||||||
|
model = Model.proxies[model_id]
|
||||||
|
|
||||||
|
if not isinstance(model, FieldSubstringFilter):
|
||||||
|
raise TypeError("model_id must point to a FieldSubstringFilter")
|
||||||
|
|
||||||
|
model.filter = value
|
||||||
|
|
|
@ -871,7 +871,6 @@ class MatrixClient(nio.AsyncClient):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.models[self.user_id, "rooms"].pop(room_id, None)
|
self.models[self.user_id, "rooms"].pop(room_id, None)
|
||||||
self.models["every_room"].pop((self.user_id, room_id), None)
|
|
||||||
self.models.pop((self.user_id, room_id, "events"), None)
|
self.models.pop((self.user_id, room_id, "events"), None)
|
||||||
self.models.pop((self.user_id, room_id, "members"), None)
|
self.models.pop((self.user_id, room_id, "members"), None)
|
||||||
|
|
||||||
|
@ -1248,8 +1247,7 @@ class MatrixClient(nio.AsyncClient):
|
||||||
unreads = unreads,
|
unreads = unreads,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.models[self.user_id, "rooms"][room.room_id] = room_item
|
self.models[self.user_id, "rooms"][room.room_id] = room_item
|
||||||
self.models["every_room"][self.user_id, room.room_id] = room_item
|
|
||||||
|
|
||||||
# List members that left the room, then remove them from our model
|
# List members that left the room, then remove them from our model
|
||||||
left_the_room = [
|
left_the_room = [
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
|
||||||
from typing import TYPE_CHECKING, Collection, Dict, Optional, Tuple
|
from typing import TYPE_CHECKING, Any, Collection, Dict, Optional, Tuple
|
||||||
|
|
||||||
from . import SyncId
|
from . import SyncId
|
||||||
from .model import Model
|
from .model import Model
|
||||||
|
@ -20,10 +20,20 @@ class ModelFilter(ModelProxy):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def source_item_set(self, source: Model, key, value: "ModelItem") -> None:
|
def source_item_set(
|
||||||
|
self,
|
||||||
|
source: Model,
|
||||||
|
key,
|
||||||
|
value: "ModelItem",
|
||||||
|
_changed_fields: Optional[Dict[str, Any]] = None,
|
||||||
|
) -> None:
|
||||||
if self.accept_source(source):
|
if self.accept_source(source):
|
||||||
dct = self if self.accept_item(value) else self.filtered_out
|
if self.accept_item(value):
|
||||||
dct[source.sync_id, key] = value
|
self.__setitem__((source.sync_id, key), value, _changed_fields)
|
||||||
|
self.filtered_out.pop((source.sync_id, key), None)
|
||||||
|
else:
|
||||||
|
self.filtered_out[source.sync_id, key] = value
|
||||||
|
self.pop((source.sync_id, key), None)
|
||||||
|
|
||||||
|
|
||||||
def source_item_deleted(self, source: Model, key) -> None:
|
def source_item_deleted(self, source: Model, key) -> None:
|
||||||
|
@ -64,11 +74,11 @@ class ModelFilter(ModelProxy):
|
||||||
|
|
||||||
|
|
||||||
class FieldSubstringFilter(ModelFilter):
|
class FieldSubstringFilter(ModelFilter):
|
||||||
def __init__(self, fields: Collection[str], *args, **kwargs) -> None:
|
def __init__(self, sync_id: SyncId, fields: Collection[str]) -> None:
|
||||||
self.fields: Collection[str] = fields
|
self.fields: Collection[str] = fields
|
||||||
self._filter: str = ""
|
self._filter: str = ""
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(sync_id)
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
|
@ -40,6 +40,11 @@ class Model(MutableMapping):
|
||||||
self._sorted_data: List["ModelItem"] = blist()
|
self._sorted_data: List["ModelItem"] = blist()
|
||||||
self._write_lock: RLock = RLock()
|
self._write_lock: RLock = RLock()
|
||||||
|
|
||||||
|
self.take_items_ownership: bool = True
|
||||||
|
|
||||||
|
if self.sync_id:
|
||||||
|
self.instances[self.sync_id] = self
|
||||||
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
"""Provide a full representation of the model and its content."""
|
"""Provide a full representation of the model and its content."""
|
||||||
|
@ -63,27 +68,34 @@ class Model(MutableMapping):
|
||||||
return self._data[key]
|
return self._data[key]
|
||||||
|
|
||||||
|
|
||||||
def __setitem__(self, key, value: "ModelItem") -> None:
|
def __setitem__(
|
||||||
|
self,
|
||||||
|
key,
|
||||||
|
value: "ModelItem",
|
||||||
|
_changed_fields: Optional[Dict[str, Any]] = None,
|
||||||
|
) -> None:
|
||||||
with self._write_lock:
|
with self._write_lock:
|
||||||
existing = self._data.get(key)
|
existing = self._data.get(key)
|
||||||
new = value
|
new = value
|
||||||
|
|
||||||
# Collect changed fields
|
# Collect changed fields
|
||||||
|
|
||||||
changed_fields = {}
|
changed_fields = _changed_fields or {}
|
||||||
|
|
||||||
for field in new.__dataclass_fields__: # type: ignore
|
if not changed_fields:
|
||||||
changed = True
|
for field in new.__dataclass_fields__: # type: ignore
|
||||||
|
changed = True
|
||||||
|
|
||||||
if existing:
|
if existing:
|
||||||
changed = getattr(new, field) != getattr(existing, field)
|
changed = \
|
||||||
|
getattr(new, field) != getattr(existing, field)
|
||||||
|
|
||||||
if changed:
|
if changed:
|
||||||
changed_fields[field] = new.serialize_field(field)
|
changed_fields[field] = new.serialize_field(field)
|
||||||
|
|
||||||
# Set parent model on new item
|
# Set parent model on new item
|
||||||
|
|
||||||
if self.sync_id:
|
if self.sync_id and self.take_items_ownership:
|
||||||
new.parent_model = self
|
new.parent_model = self
|
||||||
|
|
||||||
# Insert into sorted data
|
# Insert into sorted data
|
||||||
|
@ -101,6 +113,12 @@ class Model(MutableMapping):
|
||||||
|
|
||||||
self._data[key] = new
|
self._data[key] = new
|
||||||
|
|
||||||
|
# Callbacks
|
||||||
|
|
||||||
|
for sync_id, proxy in self.proxies.items():
|
||||||
|
if sync_id != self.sync_id:
|
||||||
|
proxy.source_item_set(self, key, value)
|
||||||
|
|
||||||
# Emit PyOtherSide event
|
# Emit PyOtherSide event
|
||||||
|
|
||||||
if self.sync_id and (index_then != index_now or changed_fields):
|
if self.sync_id and (index_then != index_now or changed_fields):
|
||||||
|
@ -121,6 +139,10 @@ class Model(MutableMapping):
|
||||||
index = self._sorted_data.index(item)
|
index = self._sorted_data.index(item)
|
||||||
del self._sorted_data[index]
|
del self._sorted_data[index]
|
||||||
|
|
||||||
|
for sync_id, proxy in self.proxies.items():
|
||||||
|
if sync_id != self.sync_id:
|
||||||
|
proxy.source_item_deleted(self, key)
|
||||||
|
|
||||||
if self.sync_id:
|
if self.sync_id:
|
||||||
ModelItemDeleted(self.sync_id, index)
|
ModelItemDeleted(self.sync_id, index)
|
||||||
|
|
||||||
|
|
|
@ -42,19 +42,23 @@ class ModelItem:
|
||||||
|
|
||||||
super().__setattr__(name, value)
|
super().__setattr__(name, value)
|
||||||
|
|
||||||
model = self.parent_model
|
parent = self.parent_model
|
||||||
|
|
||||||
if not model.sync_id:
|
if not parent.sync_id:
|
||||||
return
|
return
|
||||||
|
|
||||||
with model._write_lock:
|
fields = {name: self.serialize_field(name)}
|
||||||
index_then = model._sorted_data.index(self)
|
|
||||||
model._sorted_data.sort()
|
|
||||||
index_now = model._sorted_data.index(self)
|
|
||||||
|
|
||||||
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(model.sync_id, index_then, index_now, fields)
|
ModelItemSet(parent.sync_id, index_then, index_now, fields)
|
||||||
|
|
||||||
|
for sync_id, proxy in parent.proxies.items():
|
||||||
|
if sync_id != parent.sync_id:
|
||||||
|
proxy.source_item_set(parent, self.id, self, fields)
|
||||||
|
|
||||||
|
|
||||||
def __delattr__(self, name: str) -> None:
|
def __delattr__(self, name: str) -> None:
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING, Any, Dict, Optional
|
||||||
|
|
||||||
from . import SyncId
|
from . import SyncId
|
||||||
from .model import Model
|
from .model import Model
|
||||||
|
@ -12,14 +12,12 @@ if TYPE_CHECKING:
|
||||||
class ModelProxy(Model):
|
class ModelProxy(Model):
|
||||||
def __init__(self, sync_id: SyncId) -> None:
|
def __init__(self, sync_id: SyncId) -> None:
|
||||||
super().__init__(sync_id)
|
super().__init__(sync_id)
|
||||||
|
self.take_items_ownership = False
|
||||||
Model.proxies[sync_id] = self
|
Model.proxies[sync_id] = self
|
||||||
|
|
||||||
for sync_id, model in Model.instances.items():
|
for sync_id, model in Model.instances.items():
|
||||||
if sync_id != self.sync_id and self.accept_source(model):
|
if sync_id != self.sync_id and self.accept_source(model):
|
||||||
for key, item in model.items():
|
for key, item in model.items():
|
||||||
# if isinstance(model, ModelProxy):
|
|
||||||
# key = key[1]
|
|
||||||
|
|
||||||
self.source_item_set(model, key, item)
|
self.source_item_set(model, key, item)
|
||||||
|
|
||||||
|
|
||||||
|
@ -27,9 +25,15 @@ class ModelProxy(Model):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def source_item_set(self, source: Model, key, value: "ModelItem") -> None:
|
def source_item_set(
|
||||||
|
self,
|
||||||
|
source: Model,
|
||||||
|
key,
|
||||||
|
value: "ModelItem",
|
||||||
|
_changed_fields: Optional[Dict[str, Any]] = None,
|
||||||
|
) -> None:
|
||||||
if self.accept_source(source):
|
if self.accept_source(source):
|
||||||
self[source.sync_id, key] = value
|
self.__setitem__((source.sync_id, key), value, _changed_fields)
|
||||||
|
|
||||||
|
|
||||||
def source_item_deleted(self, source: Model, key) -> None:
|
def source_item_deleted(self, source: Model, key) -> None:
|
||||||
|
|
|
@ -8,26 +8,12 @@ import "../Base"
|
||||||
|
|
||||||
HListView {
|
HListView {
|
||||||
id: roomList
|
id: roomList
|
||||||
add: null // See the XXX comment in HListView.qml
|
model: ModelStore.get("all_rooms")
|
||||||
|
|
||||||
model: HStringFilterModel {
|
delegate: Room {
|
||||||
id: filterModel
|
id: room
|
||||||
sourceModel: ModelStore.get("every_room")
|
width: roomList.width
|
||||||
field: "display_name"
|
onActivated: showRoomAtIndex(model.index)
|
||||||
|
|
||||||
delegate: Room {
|
|
||||||
id: room
|
|
||||||
width: roomList.width
|
|
||||||
onActivated: showRoomAtIndex(DelegateModel.filteredIndex)
|
|
||||||
ListView.onAdd: ParallelAnimation {
|
|
||||||
HNumberAnimation {
|
|
||||||
target: room; property: "opacity"; from: 0; to: 1;
|
|
||||||
}
|
|
||||||
HNumberAnimation {
|
|
||||||
target: room; property: "scale"; from: 0; to: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
section.property: "for_account"
|
section.property: "for_account"
|
||||||
|
@ -39,14 +25,16 @@ HListView {
|
||||||
accountModel: ModelStore.get("accounts").find(section)
|
accountModel: ModelStore.get("accounts").find(section)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onFilterChanged: py.callCoro("set_substring_filter", ["all_rooms", filter])
|
||||||
|
|
||||||
property alias filter: filterModel.filter
|
|
||||||
|
property string filter: ""
|
||||||
readonly property var sectionIndice: {
|
readonly property var sectionIndice: {
|
||||||
const sections = {}
|
const sections = {}
|
||||||
let currentUserId = null
|
let currentUserId = null
|
||||||
|
|
||||||
for (let i = 0; i < model.filtered.count; i++) {
|
for (let i = 0; i < model.count; i++) {
|
||||||
const userId = model.filtered.get(i).model.for_account
|
const userId = model.get(i).for_account
|
||||||
|
|
||||||
if (userId !== currentUserId) {
|
if (userId !== currentUserId) {
|
||||||
sections[userId] = i
|
sections[userId] = i
|
||||||
|
@ -68,17 +56,17 @@ HListView {
|
||||||
|
|
||||||
function showRoomAtIndex(index=currentIndex) {
|
function showRoomAtIndex(index=currentIndex) {
|
||||||
if (index === -1) index = 0
|
if (index === -1) index = 0
|
||||||
index = Math.min(index, model.filtered.count - 1)
|
index = Math.min(index, model.count - 1)
|
||||||
|
|
||||||
const room = model.filtered.get(index).model
|
const room = model.get(index)
|
||||||
pageLoader.showRoom(room.for_account, room.id)
|
pageLoader.showRoom(room.for_account, room.id)
|
||||||
currentIndex = index
|
currentIndex = index
|
||||||
}
|
}
|
||||||
|
|
||||||
function showAccountRoomAtIndex(index) {
|
function showAccountRoomAtIndex(index) {
|
||||||
const currentUserId = model.filtered.get(
|
const currentUserId = model.get(
|
||||||
currentIndex === -1 ? 0 : currentIndex
|
currentIndex === -1 ? 0 : currentIndex
|
||||||
).model.for_account
|
).for_account
|
||||||
|
|
||||||
showRoomAtIndex(sectionIndice[currentUserId] + index)
|
showRoomAtIndex(sectionIndice[currentUserId] + index)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user