Add Backend.devices ListModelMap

The Device ListModels will be populated after login.
This commit is contained in:
miruka 2019-05-09 13:58:46 -04:00
parent a15f6b0bac
commit 1a90bb4331
5 changed files with 79 additions and 23 deletions

View File

@ -71,3 +71,12 @@
- Distribution - Distribution
- Review setup.py, add dependencies - Review setup.py, add dependencies
- README.md - 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
```

View File

@ -30,6 +30,7 @@ class Backend(QObject):
self._accounts: ListModel = ListModel(parent=parent) self._accounts: ListModel = ListModel(parent=parent)
self._room_events: ListModelMap = ListModelMap(Deque, parent) self._room_events: ListModelMap = ListModelMap(Deque, parent)
self._devices: ListModelMap = ListModelMap(parent=parent)
from .signal_manager import SignalManager from .signal_manager import SignalManager
self._signal_manager: SignalManager = SignalManager(self) self._signal_manager: SignalManager = SignalManager(self)
@ -53,6 +54,10 @@ class Backend(QObject):
def roomEvents(self): def roomEvents(self):
return self._room_events return self._room_events
@pyqtProperty("QVariant", constant=True)
def devices(self):
return self._devices
@pyqtProperty("QVariant", constant=True) @pyqtProperty("QVariant", constant=True)
def signals(self): def signals(self):
return self._signal_manager return self._signal_manager
@ -141,6 +146,7 @@ class Backend(QObject):
cl = self.clients cl = self.clients
ac = self.accounts ac = self.accounts
re = self.roomEvents re = self.roomEvents
de = self.devices
tcl = lambda user: cl[f"@test_{user}:matrix.org"] tcl = lambda user: cl[f"@test_{user}:matrix.org"]

View File

@ -13,6 +13,7 @@ from PyQt5.QtCore import (
import nio import nio
from .model.items import Trust
from .network_manager import NetworkManager from .network_manager import NetworkManager
from .pyqt_future import PyQtFuture, futurize from .pyqt_future import PyQtFuture, futurize
@ -81,21 +82,21 @@ class Client(QObject):
@futurize(max_running=1, discard_if_max_running=True, pyqt=False) @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) self.net.talk(self.nio.keys_upload)
def _keys_query(self) -> None: def queryE2EKeys(self) -> None:
self.net.talk(self.nio.keys_query) 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) self.net.talk(self.nio.keys_claim, room_id)
def _share_group_session(self, def shareRoomE2ESession(self,
room_id: str, room_id: str,
ignore_missing_sessions: bool = False) -> None: ignore_missing_sessions: bool = False) -> None:
self.net.talk( self.net.talk(
self.nio.share_group_session, self.nio.share_group_session,
room_id = room_id, room_id = room_id,
@ -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, result="QVariant")
@pyqtSlot(str, str, result="QVariant") @pyqtSlot(str, str, result="QVariant")
@futurize() @futurize()
def login(self, password: str, device_name: str = "") -> "Client": def login(self, password: str, device_name: str = "") -> "Client":
response = self.net.talk(self.nio.login, password, device_name) response = self.net.talk(self.nio.login, password, device_name)
self.nio_sync.receive_response(response) self.nio_sync.receive_response(response)
if not self.nio.olm_account_shared:
self._keys_upload()
return self return self
@ -123,10 +129,6 @@ class Client(QObject):
response = nio.LoginResponse(user_id, device_id, token) response = nio.LoginResponse(user_id, device_id, token)
self.nio.receive_response(response) self.nio.receive_response(response)
self.nio_sync.receive_response(response) self.nio_sync.receive_response(response)
if not self.nio.olm_account_shared:
self._keys_upload()
return self return self
@ -158,10 +160,10 @@ class Client(QObject):
self.nio.receive_response(response) self.nio.receive_response(response)
if self.nio.should_upload_keys: if self.nio.should_upload_keys:
self._keys_upload() self.uploadE2EKeys()
if self.nio.should_query_keys: if self.nio.should_query_keys:
self._keys_query() self.queryE2EKeys()
for room_id, room_info in response.rooms.invite.items(): for room_id, room_info in response.rooms.invite.items():
for ev in room_info.invite_state: for ev in room_info.invite_state:
@ -280,12 +282,12 @@ class Client(QObject):
except nio.GroupEncryptionError as err: except nio.GroupEncryptionError as err:
log.warning(err) log.warning(err)
try: try:
self._share_group_session(room_id) self.shareRoomE2ESession(room_id)
except nio.EncryptionError as err: except nio.EncryptionError as err:
log.warning(err) log.warning(err)
self._keys_claim(room_id) self.claimE2EKeysForRoom(room_id)
self._share_group_session(room_id, self.shareRoomE2ESession(room_id,
ignore_missing_sessions=True) ignore_missing_sessions=True)
log.debug("Final try to send %r to %r", content, room_id) log.debug("Final try to send %r to %r", content, room_id)
return talk() return talk()

View File

@ -1,3 +1,4 @@
from enum import Enum
from typing import Any, Dict, List, Optional from typing import Any, Dict, List, Optional
from PyQt5.QtCore import QDateTime, QSortFilterProxyModel from PyQt5.QtCore import QDateTime, QSortFilterProxyModel
@ -42,6 +43,22 @@ class RoomCategory(ListItem):
sortedRooms: QSortFilterProxyModel = QSortFilterProxyModel() 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): class Account(ListItem):
_required_init_values = {"userId", "roomCategories"} _required_init_values = {"userId", "roomCategories"}
_constant = {"userId", "roomCategories"} _constant = {"userId", "roomCategories"}

View File

@ -11,7 +11,9 @@ from nio.rooms import MatrixRoom
from .backend import Backend from .backend import Backend
from .client import Client 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 from .model.sort_filter_proxy import SortFilterProxy
Inviter = Optional[Dict[str, str]] Inviter = Optional[Dict[str, str]]
@ -35,8 +37,7 @@ class SignalManager(QObject):
def onClientAdded(self, client: Client) -> None: 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]] = [ room_categories_kwargs: List[Dict[str, Any]] = [
{"name": "Invites", "rooms": ListModel()}, {"name": "Invites", "rooms": ListModel()},
{"name": "Rooms", "rooms": ListModel()}, {"name": "Rooms", "rooms": ListModel()},
@ -59,6 +60,27 @@ class SignalManager(QObject):
displayName = self.backend.getUserDisplayName(client.userId), 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: def onClientDeleted(self, user_id: str) -> None:
del self.backend.accounts[user_id] del self.backend.accounts[user_id]