Add Backend.devices ListModelMap
The Device ListModels will be populated after login.
This commit is contained in:
		
							
								
								
									
										9
									
								
								TODO.md
									
									
									
									
									
								
							
							
						
						
									
										9
									
								
								TODO.md
									
									
									
									
									
								
							@@ -71,3 +71,12 @@
 | 
			
		||||
- Distribution
 | 
			
		||||
  - Review setup.py, add dependencies
 | 
			
		||||
  - README.md
 | 
			
		||||
  - Use PyInstaller or pyqtdeploy
 | 
			
		||||
    - Test command:
 | 
			
		||||
    ```
 | 
			
		||||
    pyinstaller --onefile --windowed --name harmonyqml \
 | 
			
		||||
                --add-data 'harmonyqml/components:harmonyqml/components' \
 | 
			
		||||
                --additional-hooks-dir . \
 | 
			
		||||
                --upx-dir ~/opt/upx-3.95-amd64_linux \
 | 
			
		||||
                run.py
 | 
			
		||||
    ```
 | 
			
		||||
 
 | 
			
		||||
@@ -30,6 +30,7 @@ class Backend(QObject):
 | 
			
		||||
 | 
			
		||||
        self._accounts:    ListModel    = ListModel(parent=parent)
 | 
			
		||||
        self._room_events: ListModelMap = ListModelMap(Deque, parent)
 | 
			
		||||
        self._devices:     ListModelMap = ListModelMap(parent=parent)
 | 
			
		||||
 | 
			
		||||
        from .signal_manager import SignalManager
 | 
			
		||||
        self._signal_manager: SignalManager = SignalManager(self)
 | 
			
		||||
@@ -53,6 +54,10 @@ class Backend(QObject):
 | 
			
		||||
    def roomEvents(self):
 | 
			
		||||
        return self._room_events
 | 
			
		||||
 | 
			
		||||
    @pyqtProperty("QVariant", constant=True)
 | 
			
		||||
    def devices(self):
 | 
			
		||||
        return self._devices
 | 
			
		||||
 | 
			
		||||
    @pyqtProperty("QVariant", constant=True)
 | 
			
		||||
    def signals(self):
 | 
			
		||||
        return self._signal_manager
 | 
			
		||||
@@ -141,6 +146,7 @@ class Backend(QObject):
 | 
			
		||||
        cl = self.clients
 | 
			
		||||
        ac = self.accounts
 | 
			
		||||
        re = self.roomEvents
 | 
			
		||||
        de = self.devices
 | 
			
		||||
 | 
			
		||||
        tcl = lambda user: cl[f"@test_{user}:matrix.org"]
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,7 @@ from PyQt5.QtCore import (
 | 
			
		||||
 | 
			
		||||
import nio
 | 
			
		||||
 | 
			
		||||
from .model.items import Trust
 | 
			
		||||
from .network_manager import NetworkManager
 | 
			
		||||
from .pyqt_future import PyQtFuture, futurize
 | 
			
		||||
 | 
			
		||||
@@ -81,19 +82,19 @@ class Client(QObject):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @futurize(max_running=1, discard_if_max_running=True, pyqt=False)
 | 
			
		||||
    def _keys_upload(self) -> None:
 | 
			
		||||
    def uploadE2EKeys(self) -> None:
 | 
			
		||||
        self.net.talk(self.nio.keys_upload)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def _keys_query(self) -> None:
 | 
			
		||||
    def queryE2EKeys(self) -> None:
 | 
			
		||||
        self.net.talk(self.nio.keys_query)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def _keys_claim(self, room_id: str) -> None:
 | 
			
		||||
    def claimE2EKeysForRoom(self, room_id: str) -> None:
 | 
			
		||||
        self.net.talk(self.nio.keys_claim, room_id)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def _share_group_session(self,
 | 
			
		||||
    def shareRoomE2ESession(self,
 | 
			
		||||
                            room_id: str,
 | 
			
		||||
                            ignore_missing_sessions: bool = False) -> None:
 | 
			
		||||
        self.net.talk(
 | 
			
		||||
@@ -103,16 +104,21 @@ class Client(QObject):
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def getDeviceTrust(self, device: nio.crypto.OlmDevice) -> Trust:
 | 
			
		||||
        olm = self.nio.olm
 | 
			
		||||
        return (
 | 
			
		||||
            Trust.trusted     if olm.is_device_verified(device)    else
 | 
			
		||||
            Trust.blacklisted if olm.is_device_blacklisted(device) else
 | 
			
		||||
            Trust.undecided
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @pyqtSlot(str, result="QVariant")
 | 
			
		||||
    @pyqtSlot(str, str, result="QVariant")
 | 
			
		||||
    @futurize()
 | 
			
		||||
    def login(self, password: str, device_name: str = "") -> "Client":
 | 
			
		||||
        response = self.net.talk(self.nio.login, password, device_name)
 | 
			
		||||
        self.nio_sync.receive_response(response)
 | 
			
		||||
 | 
			
		||||
        if not self.nio.olm_account_shared:
 | 
			
		||||
            self._keys_upload()
 | 
			
		||||
 | 
			
		||||
        return self
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -123,10 +129,6 @@ class Client(QObject):
 | 
			
		||||
        response = nio.LoginResponse(user_id, device_id, token)
 | 
			
		||||
        self.nio.receive_response(response)
 | 
			
		||||
        self.nio_sync.receive_response(response)
 | 
			
		||||
 | 
			
		||||
        if not self.nio.olm_account_shared:
 | 
			
		||||
            self._keys_upload()
 | 
			
		||||
 | 
			
		||||
        return self
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -158,10 +160,10 @@ class Client(QObject):
 | 
			
		||||
        self.nio.receive_response(response)
 | 
			
		||||
 | 
			
		||||
        if self.nio.should_upload_keys:
 | 
			
		||||
            self._keys_upload()
 | 
			
		||||
            self.uploadE2EKeys()
 | 
			
		||||
 | 
			
		||||
        if self.nio.should_query_keys:
 | 
			
		||||
            self._keys_query()
 | 
			
		||||
            self.queryE2EKeys()
 | 
			
		||||
 | 
			
		||||
        for room_id, room_info in response.rooms.invite.items():
 | 
			
		||||
            for ev in room_info.invite_state:
 | 
			
		||||
@@ -280,11 +282,11 @@ class Client(QObject):
 | 
			
		||||
            except nio.GroupEncryptionError as err:
 | 
			
		||||
                log.warning(err)
 | 
			
		||||
                try:
 | 
			
		||||
                    self._share_group_session(room_id)
 | 
			
		||||
                    self.shareRoomE2ESession(room_id)
 | 
			
		||||
                except nio.EncryptionError as err:
 | 
			
		||||
                    log.warning(err)
 | 
			
		||||
                    self._keys_claim(room_id)
 | 
			
		||||
                    self._share_group_session(room_id,
 | 
			
		||||
                    self.claimE2EKeysForRoom(room_id)
 | 
			
		||||
                    self.shareRoomE2ESession(room_id,
 | 
			
		||||
                                             ignore_missing_sessions=True)
 | 
			
		||||
 | 
			
		||||
                log.debug("Final try to send %r to %r", content, room_id)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
from enum import Enum
 | 
			
		||||
from typing import Any, Dict, List, Optional
 | 
			
		||||
 | 
			
		||||
from PyQt5.QtCore import QDateTime, QSortFilterProxyModel
 | 
			
		||||
@@ -42,6 +43,22 @@ class RoomCategory(ListItem):
 | 
			
		||||
    sortedRooms: QSortFilterProxyModel = QSortFilterProxyModel()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Trust(Enum):
 | 
			
		||||
    blacklisted = -1
 | 
			
		||||
    undecided   = 0
 | 
			
		||||
    trusted     = 1
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Device(ListItem):
 | 
			
		||||
    _required_init_values = {"deviceId", "ed25519Key"}
 | 
			
		||||
    _constant             = {"deviceId", "ed25519Key"}
 | 
			
		||||
 | 
			
		||||
    deviceId:    str           = ""
 | 
			
		||||
    ed25519Key:  str           = ""
 | 
			
		||||
    displayName: Optional[str] = None
 | 
			
		||||
    trust:       Trust         = Trust.undecided
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Account(ListItem):
 | 
			
		||||
    _required_init_values = {"userId", "roomCategories"}
 | 
			
		||||
    _constant             = {"userId", "roomCategories"}
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,9 @@ from nio.rooms import MatrixRoom
 | 
			
		||||
 | 
			
		||||
from .backend import Backend
 | 
			
		||||
from .client import Client
 | 
			
		||||
from .model.items import Account, ListModel, Room, RoomCategory, RoomEvent
 | 
			
		||||
from .model.items import (
 | 
			
		||||
    Account, Device, ListModel, Room, RoomCategory, RoomEvent
 | 
			
		||||
)
 | 
			
		||||
from .model.sort_filter_proxy import SortFilterProxy
 | 
			
		||||
 | 
			
		||||
Inviter   = Optional[Dict[str, str]]
 | 
			
		||||
@@ -35,8 +37,7 @@ class SignalManager(QObject):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def onClientAdded(self, client: Client) -> None:
 | 
			
		||||
        self.connectClient(client)
 | 
			
		||||
 | 
			
		||||
        # Build an Account item for the Backend.accounts model
 | 
			
		||||
        room_categories_kwargs: List[Dict[str, Any]] = [
 | 
			
		||||
            {"name": "Invites", "rooms": ListModel()},
 | 
			
		||||
            {"name": "Rooms", "rooms": ListModel()},
 | 
			
		||||
@@ -59,6 +60,27 @@ class SignalManager(QObject):
 | 
			
		||||
            displayName = self.backend.getUserDisplayName(client.userId),
 | 
			
		||||
        ))
 | 
			
		||||
 | 
			
		||||
        # Upload our E2E keys to the matrix server if needed
 | 
			
		||||
        if not client.nio.olm_account_shared:
 | 
			
		||||
            client.uploadE2EKeys()
 | 
			
		||||
 | 
			
		||||
        # Add our devices to the Backend.devices model
 | 
			
		||||
        store = client.nio.device_store
 | 
			
		||||
 | 
			
		||||
        for user_id in store.users:
 | 
			
		||||
            self.backend.devices[user_id].clear()
 | 
			
		||||
            self.backend.devices[user_id].extend([
 | 
			
		||||
                Device(
 | 
			
		||||
                    deviceId   = dev.id,
 | 
			
		||||
                    ed25519Key = dev.ed25519,
 | 
			
		||||
                    trust      = client.getDeviceTrust(dev),
 | 
			
		||||
                    # displayName = TODO
 | 
			
		||||
                ) for dev in store.active_user_devices(user_id)
 | 
			
		||||
            ])
 | 
			
		||||
 | 
			
		||||
        # Finally, connect all client signals
 | 
			
		||||
        self.connectClient(client)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def onClientDeleted(self, user_id: str) -> None:
 | 
			
		||||
        del self.backend.accounts[user_id]
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user