Reorganize backend files, show accounts in UI
This commit is contained in:
parent
4f9a47027c
commit
5d4c7b8520
|
@ -1,5 +1,4 @@
|
||||||
# Copyright 2019 miruka
|
# Copyright 2019 miruka
|
||||||
# This file is part of harmonyqml, licensed under GPLv3.
|
# This file is part of harmonyqml, licensed under GPLv3.
|
||||||
|
|
||||||
|
from .backend import Backend
|
||||||
from .dummy import DummyBackend
|
|
||||||
|
|
42
harmonyqml/backend/backend.py
Normal file
42
harmonyqml/backend/backend.py
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
# Copyright 2019 miruka
|
||||||
|
# This file is part of harmonyqml, licensed under GPLv3.
|
||||||
|
|
||||||
|
import hashlib
|
||||||
|
|
||||||
|
from PyQt5.QtCore import QObject, pyqtProperty, pyqtSlot
|
||||||
|
|
||||||
|
from .client_manager import ClientManager
|
||||||
|
from .model.qml_models import QMLModels
|
||||||
|
|
||||||
|
|
||||||
|
class Backend(QObject):
|
||||||
|
def __init__(self) -> None:
|
||||||
|
super().__init__()
|
||||||
|
self._client_manager: ClientManager = ClientManager()
|
||||||
|
self._models: QMLModels = QMLModels()
|
||||||
|
|
||||||
|
from .signal_manager import SignalManager
|
||||||
|
self._signal_manager: SignalManager = SignalManager(self)
|
||||||
|
|
||||||
|
# a = self._client_manager; m = self._models
|
||||||
|
# from PyQt5.QtCore import pyqtRemoveInputHook as PRI
|
||||||
|
# import pdb; PRI(); pdb.set_trace()
|
||||||
|
|
||||||
|
self.clientManager.configLoad()
|
||||||
|
|
||||||
|
|
||||||
|
@pyqtProperty("QVariant", constant=True)
|
||||||
|
def clientManager(self):
|
||||||
|
return self._client_manager
|
||||||
|
|
||||||
|
|
||||||
|
@pyqtProperty("QVariant", constant=True)
|
||||||
|
def models(self):
|
||||||
|
return self._models
|
||||||
|
|
||||||
|
|
||||||
|
@pyqtSlot(str, result=float)
|
||||||
|
def hueFromString(self, string: str) -> float:
|
||||||
|
# pylint:disable=no-self-use
|
||||||
|
md5 = hashlib.md5(bytes(string, "utf-8")).hexdigest()
|
||||||
|
return float("0.%s" % int(md5[-10:], 16))
|
|
@ -1,109 +0,0 @@
|
||||||
# Copyright 2019 miruka
|
|
||||||
# This file is part of harmonyqml, licensed under GPLv3.
|
|
||||||
|
|
||||||
import hashlib
|
|
||||||
from typing import Any, DefaultDict, Dict, NamedTuple, Optional
|
|
||||||
|
|
||||||
from PyQt5.QtCore import QDateTime, QObject, pyqtProperty, pyqtSlot
|
|
||||||
|
|
||||||
from .enums import Activity, MessageKind, Presence
|
|
||||||
from .list_model import ListModel, _QtListModel
|
|
||||||
|
|
||||||
|
|
||||||
class User(NamedTuple):
|
|
||||||
user_id: str
|
|
||||||
display_name: str
|
|
||||||
avatar_url: Optional[str] = None
|
|
||||||
status_message: Optional[str] = None
|
|
||||||
|
|
||||||
|
|
||||||
class Room(NamedTuple):
|
|
||||||
room_id: str
|
|
||||||
display_name: str
|
|
||||||
description: str = ""
|
|
||||||
unread_messages: int = 0
|
|
||||||
presence: Presence = Presence.none
|
|
||||||
activity: Activity = Activity.none
|
|
||||||
last_activity_timestamp_ms: Optional[int] = None
|
|
||||||
avatar_url: Optional[str] = None
|
|
||||||
|
|
||||||
|
|
||||||
class Message(NamedTuple):
|
|
||||||
sender_id: str
|
|
||||||
date_time: QDateTime
|
|
||||||
content: str
|
|
||||||
kind: MessageKind = MessageKind.text
|
|
||||||
sender_avatar: Optional[str] = None
|
|
||||||
|
|
||||||
|
|
||||||
class Backend(QObject):
|
|
||||||
def __init__(self) -> None:
|
|
||||||
super().__init__()
|
|
||||||
self._known_users: Dict[str, User] = {}
|
|
||||||
|
|
||||||
self.accounts: ListModel = ListModel()
|
|
||||||
self.rooms: DefaultDict[str, ListModel] = DefaultDict(ListModel)
|
|
||||||
self.messages: DefaultDict[str, ListModel] = DefaultDict(ListModel)
|
|
||||||
|
|
||||||
|
|
||||||
@pyqtProperty(_QtListModel, constant=True)
|
|
||||||
def accountsModel(self) -> _QtListModel:
|
|
||||||
return self.accounts.qt_model
|
|
||||||
|
|
||||||
|
|
||||||
@pyqtProperty("QVariantMap", constant=True)
|
|
||||||
def roomsModel(self) -> Dict[str, _QtListModel]:
|
|
||||||
return {account_id: l.qt_model for account_id, l in self.rooms.items()}
|
|
||||||
|
|
||||||
|
|
||||||
@pyqtProperty("QVariantMap", constant=True)
|
|
||||||
def messagesModel(self) -> Dict[str, _QtListModel]:
|
|
||||||
return {room_id: l.qt_model for room_id, l in self.messages.items()}
|
|
||||||
|
|
||||||
|
|
||||||
@pyqtSlot(str, str, str)
|
|
||||||
def sendMessage(self, sender_id: str, room_id: str, markdown: str) -> None:
|
|
||||||
self.localEcho(sender_id, room_id, markdown)
|
|
||||||
self.sendToServer(sender_id, room_id, markdown)
|
|
||||||
|
|
||||||
|
|
||||||
def localEcho(self, sender_id: str, room_id: str, html: str) -> None:
|
|
||||||
self.messages[room_id].append(Message(
|
|
||||||
sender_id, QDateTime.currentDateTime(), html,
|
|
||||||
))
|
|
||||||
|
|
||||||
|
|
||||||
def sendToServer(self, sender_id: str, room_id: str, html: str) -> None:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
@pyqtSlot(str, result="QVariantMap")
|
|
||||||
def getUser(self, user_id: str) -> Dict[str, Any]:
|
|
||||||
for user in self.accounts:
|
|
||||||
if user.user_id == user_id:
|
|
||||||
return user._asdict()
|
|
||||||
|
|
||||||
try:
|
|
||||||
return self._known_users[user_id]._asdict()
|
|
||||||
except KeyError:
|
|
||||||
name = user_id.lstrip("@").split(":")[0].capitalize()
|
|
||||||
user = User(user_id, name)
|
|
||||||
self._known_users[user_id] = user
|
|
||||||
return user._asdict()
|
|
||||||
|
|
||||||
|
|
||||||
@pyqtSlot(str, result=float)
|
|
||||||
def hueFromString(self, string: str) -> float:
|
|
||||||
# pylint: disable=no-self-use
|
|
||||||
md5 = hashlib.md5(bytes(string, "utf-8")).hexdigest()
|
|
||||||
return float("0.%s" % int(md5[-10:], 16))
|
|
||||||
|
|
||||||
|
|
||||||
@pyqtSlot(str, str)
|
|
||||||
def setStatusMessage(self, user_id: str, to: str) -> None:
|
|
||||||
for user in self.accounts:
|
|
||||||
if user.user_id == user_id:
|
|
||||||
user.status_message = to
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
raise ValueError(f"{user_id} not found in Backend.accounts")
|
|
|
@ -4,13 +4,15 @@
|
||||||
import functools
|
import functools
|
||||||
from concurrent.futures import Future, ThreadPoolExecutor
|
from concurrent.futures import Future, ThreadPoolExecutor
|
||||||
from threading import Event
|
from threading import Event
|
||||||
from typing import Callable, DefaultDict
|
from typing import Callable, DefaultDict, Dict
|
||||||
|
|
||||||
from PyQt5.QtCore import QObject, pyqtSlot
|
from PyQt5.QtCore import QObject, pyqtSlot
|
||||||
|
|
||||||
import nio
|
import nio
|
||||||
import nio.responses as nr
|
import nio.responses as nr
|
||||||
|
|
||||||
|
from .model.items import User
|
||||||
|
|
||||||
# One pool per hostname/remote server;
|
# One pool per hostname/remote server;
|
||||||
# multiple Client for different accounts on the same server can exist.
|
# multiple Client for different accounts on the same server can exist.
|
||||||
_POOLS: DefaultDict[str, ThreadPoolExecutor] = \
|
_POOLS: DefaultDict[str, ThreadPoolExecutor] = \
|
||||||
|
@ -38,7 +40,7 @@ class Client(QObject):
|
||||||
|
|
||||||
self.pool: ThreadPoolExecutor = _POOLS[self.host]
|
self.pool: ThreadPoolExecutor = _POOLS[self.host]
|
||||||
|
|
||||||
from .net import NetworkManager
|
from .network_manager import NetworkManager
|
||||||
self.net: NetworkManager = NetworkManager(self)
|
self.net: NetworkManager = NetworkManager(self)
|
||||||
|
|
||||||
self._stop_sync: Event = Event()
|
self._stop_sync: Event = Event()
|
||||||
|
@ -75,11 +77,25 @@ class Client(QObject):
|
||||||
self.net.write(self.nio.disconnect())
|
self.net.write(self.nio.disconnect())
|
||||||
|
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
@futurize
|
@futurize
|
||||||
def startSyncing(self) -> None:
|
def startSyncing(self) -> None:
|
||||||
while True:
|
while True:
|
||||||
print(self, self.net.talk(self.nio.sync, timeout=10))
|
self.net.talk(self.nio.sync, timeout=10)
|
||||||
|
|
||||||
if self._stop_sync.is_set():
|
if self._stop_sync.is_set():
|
||||||
self._stop_sync.clear()
|
self._stop_sync.clear()
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
|
@pyqtSlot(str, str, result="QVariantMap")
|
||||||
|
def getUser(self, room_id: str, user_id: str) -> Dict[str, str]:
|
||||||
|
try:
|
||||||
|
name = self.nio.rooms[room_id].user_name(user_id)
|
||||||
|
except KeyError:
|
||||||
|
name = None
|
||||||
|
|
||||||
|
return User(
|
||||||
|
user_id = user_id,
|
||||||
|
display_name = name or user_id,
|
||||||
|
)._asdict()
|
|
@ -6,11 +6,12 @@ import json
|
||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
import threading
|
import threading
|
||||||
from concurrent.futures import Future
|
|
||||||
from typing import Dict, Optional
|
from typing import Dict, Optional
|
||||||
|
|
||||||
from atomicfile import AtomicFile
|
from atomicfile import AtomicFile
|
||||||
from PyQt5.QtCore import QObject, QStandardPaths, pyqtProperty, pyqtSlot
|
from PyQt5.QtCore import (
|
||||||
|
QObject, QStandardPaths, pyqtProperty, pyqtSignal, pyqtSlot
|
||||||
|
)
|
||||||
|
|
||||||
from harmonyqml import __about__
|
from harmonyqml import __about__
|
||||||
|
|
||||||
|
@ -23,6 +24,10 @@ _CONFIG_LOCK = threading.Lock()
|
||||||
|
|
||||||
|
|
||||||
class ClientManager(QObject):
|
class ClientManager(QObject):
|
||||||
|
clientAdded = pyqtSignal(Client)
|
||||||
|
clientDeleted = pyqtSignal(str)
|
||||||
|
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._clients: Dict[str, Client] = {}
|
self._clients: Dict[str, Client] = {}
|
||||||
|
@ -32,7 +37,7 @@ class ClientManager(QObject):
|
||||||
return f"{type(self).__name__}(clients={self.clients!r})"
|
return f"{type(self).__name__}(clients={self.clients!r})"
|
||||||
|
|
||||||
|
|
||||||
@pyqtProperty("QVariantMap")
|
@pyqtProperty("QVariantMap", constant=True)
|
||||||
def clients(self):
|
def clients(self):
|
||||||
return self._clients
|
return self._clients
|
||||||
|
|
||||||
|
@ -40,13 +45,9 @@ class ClientManager(QObject):
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def configLoad(self) -> None:
|
def configLoad(self) -> None:
|
||||||
for user_id, info in self.configAccounts().items():
|
for user_id, info in self.configAccounts().items():
|
||||||
cli = Client(info["hostname"], user_id)
|
client = Client(info["hostname"], user_id)
|
||||||
|
client.resumeSession(user_id, info["token"], info["device_id"])\
|
||||||
def on_done(_: Future, cli=cli) -> None:
|
.add_done_callback(lambda _, c=client: self._on_connected(c))
|
||||||
self._clients[cli.nio.user_id] = cli
|
|
||||||
|
|
||||||
cli.resumeSession(user_id, info["token"], info["device_id"])\
|
|
||||||
.add_done_callback(on_done)
|
|
||||||
|
|
||||||
|
|
||||||
@pyqtSlot(str, str, str)
|
@pyqtSlot(str, str, str)
|
||||||
|
@ -54,22 +55,25 @@ class ClientManager(QObject):
|
||||||
def new(self, hostname: str, username: str, password: str,
|
def new(self, hostname: str, username: str, password: str,
|
||||||
device_id: str = "") -> None:
|
device_id: str = "") -> None:
|
||||||
|
|
||||||
cli = Client(hostname, username, device_id)
|
client = Client(hostname, username, device_id)
|
||||||
|
client.login(password, self.defaultDeviceName)\
|
||||||
|
.add_done_callback(lambda _: self._on_connected(client))
|
||||||
|
|
||||||
def on_done(_: Future, cli=cli) -> None:
|
|
||||||
self._clients[cli.nio.user_id] = cli
|
|
||||||
|
|
||||||
cli.login(password, self.defaultDeviceName).add_done_callback(on_done)
|
def _on_connected(self, client: Client) -> None:
|
||||||
|
self.clients[client.nio.user_id] = client
|
||||||
|
self.clientAdded.emit(client)
|
||||||
|
|
||||||
|
|
||||||
@pyqtSlot(str)
|
@pyqtSlot(str)
|
||||||
def delete(self, user_id: str) -> None:
|
def delete(self, user_id: str) -> None:
|
||||||
client = self._clients.pop(user_id, None)
|
client = self.clients.pop(user_id, None)
|
||||||
if client:
|
if client:
|
||||||
|
self.clientDeleted.emit(user_id)
|
||||||
client.logout()
|
client.logout()
|
||||||
|
|
||||||
|
|
||||||
@pyqtProperty(str)
|
@pyqtProperty(str, constant=True)
|
||||||
def defaultDeviceName(self) -> str: # pylint: disable=no-self-use
|
def defaultDeviceName(self) -> str: # pylint: disable=no-self-use
|
||||||
os_ = f" on {platform.system()}".rstrip()
|
os_ = f" on {platform.system()}".rstrip()
|
||||||
os_ = f"{os_} {platform.release()}".rstrip() if os_ != " on" else ""
|
os_ = f"{os_} {platform.release()}".rstrip() if os_ != " on" else ""
|
|
@ -1,64 +0,0 @@
|
||||||
# Copyright 2019 miruka
|
|
||||||
# This file is part of harmonyqml, licensed under GPLv3.
|
|
||||||
|
|
||||||
from PyQt5.QtCore import QDateTime, Qt
|
|
||||||
|
|
||||||
from .base import Backend, Message, Room, User
|
|
||||||
|
|
||||||
|
|
||||||
class DummyBackend(Backend):
|
|
||||||
def __init__(self) -> None:
|
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
dt = lambda t: QDateTime.fromString(f"2019-03-19T{t}.123",
|
|
||||||
Qt.ISODateWithMs)
|
|
||||||
db = lambda t: QDateTime.fromString(f"2019-03-20T{t}.456",
|
|
||||||
Qt.ISODateWithMs)
|
|
||||||
|
|
||||||
self.accounts.extend([
|
|
||||||
User("@renko:matrix.org", "Renko", None, "Sleeping, zzz..."),
|
|
||||||
User("@mary:matrix.org", "Mary"),
|
|
||||||
])
|
|
||||||
|
|
||||||
self.rooms["@renko:matrix.org"].extend([
|
|
||||||
Room("!test:matrix.org", "Test", "Test room"),
|
|
||||||
Room("!mary:matrix.org", "Mary",
|
|
||||||
"Lorem ipsum sit dolor amet this is a long text to test "
|
|
||||||
"wrapping of room subtitle etc 1234 example foo bar abc", 2),
|
|
||||||
Room("!foo:matrix.org", "Another room"),
|
|
||||||
])
|
|
||||||
|
|
||||||
self.rooms["@mary:matrix.org"].extend([
|
|
||||||
Room("!test:matrix.org", "Test", "Test room"),
|
|
||||||
Room("!mary:matrix.org", "Renko", "Lorem ipsum sit dolor amet"),
|
|
||||||
])
|
|
||||||
|
|
||||||
self.messages["!test:matrix.org"].extend([
|
|
||||||
Message("@renko:matrix.org", dt("10:20:13"), "Lorem"),
|
|
||||||
Message("@renko:matrix.org", dt("10:22:01"), "Ipsum"),
|
|
||||||
Message("@renko:matrix.org", dt("10:22:50"), "Combine"),
|
|
||||||
Message("@renko:matrix.org", dt("10:30:41"),
|
|
||||||
"Time passed, don't combine"),
|
|
||||||
Message("@mary:matrix.org", dt("10:31:12"),
|
|
||||||
"Different person, don't combine"),
|
|
||||||
Message("@mary:matrix.org", dt("10:32:04"),
|
|
||||||
"But combine me"),
|
|
||||||
Message("@mary:matrix.org", dt("13:10:20"),
|
|
||||||
"Long time passed, conv break"),
|
|
||||||
|
|
||||||
Message("@renko:matrix.org", db("10:22:01"), "Daybreak"),
|
|
||||||
Message("@mary:matrix.org", db("10:22:03"),
|
|
||||||
"A longer message to test text wrapping. "
|
|
||||||
"Lorem ipsum dolor sit amet, consectetuer adipiscing "
|
|
||||||
"elit. Aenean commodo ligula "
|
|
||||||
"eget dolor. Aenean massa. Cem sociis natoque penaibs "
|
|
||||||
"et magnis dis parturient montes, nascetur ridiculus "
|
|
||||||
"mus. Donec quam. "),
|
|
||||||
])
|
|
||||||
|
|
||||||
self.messages["!mary:matrix.org"].extend([
|
|
||||||
Message("@mary:matrix.org", dt("10:22:23"), "First"),
|
|
||||||
Message("@mary:matrix.org", dt("12:24:10"), "Second"),
|
|
||||||
])
|
|
||||||
|
|
||||||
self.messages["!foo:matrix.org"].extend([])
|
|
|
@ -1,4 +0,0 @@
|
||||||
# Copyright 2019 miruka
|
|
||||||
# This file is part of harmonyqml, licensed under GPLv3.
|
|
||||||
|
|
||||||
from .backend import MatrixNioBackend
|
|
|
@ -1,27 +0,0 @@
|
||||||
# Copyright 2019 miruka
|
|
||||||
# This file is part of harmonyqml, licensed under GPLv3.
|
|
||||||
|
|
||||||
from typing import Any, DefaultDict, Dict, NamedTuple, Optional
|
|
||||||
|
|
||||||
from PyQt5.QtCore import QDateTime, QObject, pyqtProperty, pyqtSlot
|
|
||||||
|
|
||||||
from matrix_client.user import User as MatrixUser
|
|
||||||
|
|
||||||
from ..base import Backend, User
|
|
||||||
from .client_manager import ClientManager
|
|
||||||
|
|
||||||
|
|
||||||
class MatrixNioBackend(Backend):
|
|
||||||
def __init__(self) -> None:
|
|
||||||
super().__init__()
|
|
||||||
self._client_manager = ClientManager()
|
|
||||||
|
|
||||||
# a = self._client_manager
|
|
||||||
# from PyQt5.QtCore import pyqtRemoveInputHook as PRI; import pdb; PRI(); pdb.set_trace()
|
|
||||||
|
|
||||||
self._client_manager.configLoad()
|
|
||||||
|
|
||||||
|
|
||||||
@pyqtProperty("QVariant")
|
|
||||||
def clientManager(self):
|
|
||||||
return self._client_manager
|
|
6
harmonyqml/backend/model/__init__.py
Normal file
6
harmonyqml/backend/model/__init__.py
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
# Copyright 2019 miruka
|
||||||
|
# This file is part of harmonyqml, licensed under GPLv3.
|
||||||
|
|
||||||
|
from .list_model import ListModel, _QtListModel
|
||||||
|
from .qml_models import QMLModels
|
||||||
|
from . import enums, items
|
34
harmonyqml/backend/model/items.py
Normal file
34
harmonyqml/backend/model/items.py
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
# Copyright 2019 miruka
|
||||||
|
# This file is part of harmonyqml, licensed under GPLv3.
|
||||||
|
|
||||||
|
from typing import NamedTuple, Optional
|
||||||
|
|
||||||
|
from PyQt5.QtCore import QDateTime
|
||||||
|
|
||||||
|
from .enums import Activity, MessageKind, Presence
|
||||||
|
|
||||||
|
|
||||||
|
class User(NamedTuple):
|
||||||
|
user_id: str
|
||||||
|
display_name: str
|
||||||
|
avatar_url: Optional[str] = None
|
||||||
|
status_message: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
class Room(NamedTuple):
|
||||||
|
room_id: str
|
||||||
|
display_name: str
|
||||||
|
description: str = ""
|
||||||
|
unread_messages: int = 0
|
||||||
|
presence: Presence = Presence.none
|
||||||
|
activity: Activity = Activity.none
|
||||||
|
last_activity_timestamp_ms: Optional[int] = None
|
||||||
|
avatar_url: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
class Message(NamedTuple):
|
||||||
|
sender_id: str
|
||||||
|
date_time: QDateTime
|
||||||
|
content: str
|
||||||
|
kind: MessageKind = MessageKind.text
|
||||||
|
sender_avatar: Optional[str] = None
|
|
@ -65,6 +65,16 @@ class _QtListModel(QAbstractListModel):
|
||||||
return self._list[index]._asdict()
|
return self._list[index]._asdict()
|
||||||
|
|
||||||
|
|
||||||
|
@pyqtSlot(str, "QVariant", result=int)
|
||||||
|
def indexWhere(self, prop: str, is_value: Any) -> int:
|
||||||
|
for i, item in enumerate(self._list):
|
||||||
|
if getattr(item, prop) == is_value:
|
||||||
|
return i
|
||||||
|
|
||||||
|
raise ValueError(f"No {type(self._ref_namedlist)} in list with "
|
||||||
|
f"property {prop!r} set to {is_value!r}.")
|
||||||
|
|
||||||
|
|
||||||
@pyqtSlot(int, list)
|
@pyqtSlot(int, list)
|
||||||
def insert(self, index: int, value: NewValue) -> None:
|
def insert(self, index: int, value: NewValue) -> None:
|
||||||
value = self._convert_new_value(value)
|
value = self._convert_new_value(value)
|
||||||
|
@ -176,6 +186,10 @@ class ListModel(MutableSequence):
|
||||||
self.qt_model.insert(index, value)
|
self.qt_model.insert(index, value)
|
||||||
|
|
||||||
|
|
||||||
|
def indexWhere(self, prop: str, is_value: Any) -> int:
|
||||||
|
return self.qt_model.indexWhere(prop, is_value)
|
||||||
|
|
||||||
|
|
||||||
def setProperty(self, index: int, prop: str, value: Any) -> None:
|
def setProperty(self, index: int, prop: str, value: Any) -> None:
|
||||||
"Set role of the item at *index* to *value*."
|
"Set role of the item at *index* to *value*."
|
||||||
self.qt_model.setProperty(index, prop, value)
|
self.qt_model.setProperty(index, prop, value)
|
31
harmonyqml/backend/model/qml_models.py
Normal file
31
harmonyqml/backend/model/qml_models.py
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
# Copyright 2019 miruka
|
||||||
|
# This file is part of harmonyqml, licensed under GPLv3.
|
||||||
|
|
||||||
|
from typing import DefaultDict, Dict
|
||||||
|
|
||||||
|
from PyQt5.QtCore import QObject, pyqtProperty
|
||||||
|
|
||||||
|
from .list_model import ListModel, _QtListModel
|
||||||
|
|
||||||
|
|
||||||
|
class QMLModels(QObject):
|
||||||
|
def __init__(self) -> None:
|
||||||
|
super().__init__()
|
||||||
|
self._accounts: ListModel = ListModel()
|
||||||
|
self._rooms: DefaultDict[str, ListModel] = DefaultDict(ListModel)
|
||||||
|
self._messages: DefaultDict[str, ListModel] = DefaultDict(ListModel)
|
||||||
|
|
||||||
|
|
||||||
|
@pyqtProperty(_QtListModel, constant=True)
|
||||||
|
def accounts(self) -> _QtListModel:
|
||||||
|
return self._accounts.qt_model
|
||||||
|
|
||||||
|
|
||||||
|
@pyqtProperty("QVariantMap", constant=True)
|
||||||
|
def rooms(self) -> Dict[str, _QtListModel]:
|
||||||
|
return {user_id: l.qt_model for user_id, l in self._rooms.items()}
|
||||||
|
|
||||||
|
|
||||||
|
@pyqtProperty("QVariantMap", constant=True)
|
||||||
|
def messages(self) -> Dict[str, _QtListModel]:
|
||||||
|
return {room_id: l.qt_model for room_id, l in self._messages.items()}
|
33
harmonyqml/backend/signal_manager.py
Normal file
33
harmonyqml/backend/signal_manager.py
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
# Copyright 2019 miruka
|
||||||
|
# This file is part of harmonyqml, licensed under GPLv3.
|
||||||
|
|
||||||
|
from PyQt5.QtCore import QObject
|
||||||
|
|
||||||
|
from .backend import Backend
|
||||||
|
from .client import Client
|
||||||
|
from .model.items import User
|
||||||
|
|
||||||
|
|
||||||
|
class SignalManager(QObject):
|
||||||
|
def __init__(self, backend: Backend) -> None:
|
||||||
|
super().__init__()
|
||||||
|
self.backend = backend
|
||||||
|
self.connectAll()
|
||||||
|
|
||||||
|
|
||||||
|
def connectAll(self) -> None:
|
||||||
|
be = self.backend
|
||||||
|
be.clientManager.clientAdded.connect(self.onClientAdded)
|
||||||
|
be.clientManager.clientDeleted.connect(self.onClientDeleted)
|
||||||
|
|
||||||
|
|
||||||
|
def onClientAdded(self, client: Client) -> None:
|
||||||
|
self.backend.models.accounts.append(User(
|
||||||
|
user_id = client.nio.user_id,
|
||||||
|
display_name = client.nio.user_id.lstrip("@").split(":")[0],
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
def onClientDeleted(self, user_id: str) -> None:
|
||||||
|
accs = self.backend.models.accounts
|
||||||
|
del accs[accs.indexWhere("user_id", user_id)]
|
|
@ -11,7 +11,7 @@ Column {
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly property string displayName:
|
readonly property string displayName:
|
||||||
Backend.getUser(sender_id).display_name
|
Backend.getUser(chatPage.room.room_id, sender_id).display_name
|
||||||
|
|
||||||
readonly property bool isOwn:
|
readonly property bool isOwn:
|
||||||
chatPage.user.user_id === sender_id
|
chatPage.user.user_id === sender_id
|
||||||
|
|
|
@ -13,7 +13,7 @@ Rectangle {
|
||||||
ListView {
|
ListView {
|
||||||
id: messageListView
|
id: messageListView
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
model: Backend.messagesModel[chatPage.room.room_id]
|
model: Backend.models.messages[chatPage.room.room_id]
|
||||||
delegate: MessageDelegate {}
|
delegate: MessageDelegate {}
|
||||||
//highlight: Rectangle {color: "lightsteelblue"; radius: 5}
|
//highlight: Rectangle {color: "lightsteelblue"; radius: 5}
|
||||||
|
|
||||||
|
|
|
@ -67,7 +67,7 @@ ColumnLayout {
|
||||||
id: "roomList"
|
id: "roomList"
|
||||||
visible: true
|
visible: true
|
||||||
interactive: false // no scrolling
|
interactive: false // no scrolling
|
||||||
user: Backend.getUser(user_id)
|
for_user_id: user_id
|
||||||
|
|
||||||
Layout.minimumHeight:
|
Layout.minimumHeight:
|
||||||
roomList.visible ?
|
roomList.visible ?
|
||||||
|
|
|
@ -5,7 +5,7 @@ import QtQuick.Layouts 1.4
|
||||||
ListView {
|
ListView {
|
||||||
id: "accountList"
|
id: "accountList"
|
||||||
spacing: 8
|
spacing: 8
|
||||||
model: Backend.accountsModel
|
model: Backend.models.accounts
|
||||||
delegate: AccountDelegate {}
|
delegate: AccountDelegate {}
|
||||||
clip: true
|
clip: true
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ MouseArea {
|
||||||
height: Math.max(roomLabel.height + subtitleLabel.height, avatar.height)
|
height: Math.max(roomLabel.height + subtitleLabel.height, avatar.height)
|
||||||
|
|
||||||
onClicked: pageStack.show_room(
|
onClicked: pageStack.show_room(
|
||||||
roomList.user,
|
roomList.user_id,
|
||||||
roomList.model.get(index)
|
roomList.model.get(index)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -38,22 +38,23 @@ MouseArea {
|
||||||
}
|
}
|
||||||
Base.HLabel {
|
Base.HLabel {
|
||||||
function get_text() {
|
function get_text() {
|
||||||
var msgs = Backend.messagesModel[room_id]
|
var msgs = Backend.models.messages[room_id]
|
||||||
if (msgs.count < 1) { return "" }
|
if (msgs.count < 1) { return "" }
|
||||||
|
|
||||||
var msg = msgs.get(-1)
|
var msg = msgs.get(-1)
|
||||||
var color_ = (msg.sender_id === roomList.user.user_id ?
|
var color_ = (msg.sender_id === roomList.user_id ?
|
||||||
"darkblue" : "purple")
|
"darkblue" : "purple")
|
||||||
|
var client = Backend.clientManager.clients[RoomList.for_user_id]
|
||||||
|
|
||||||
return "<font color=\"" + color_ + "\">" +
|
return "<font color=\"" + color_ + "\">" +
|
||||||
Backend.getUser(msg.sender_id).display_name +
|
client.getUser(room_id, msg.sender_id).display_name +
|
||||||
":</font> " +
|
":</font> " +
|
||||||
msg.content
|
msg.content
|
||||||
}
|
}
|
||||||
|
|
||||||
id: subtitleLabel
|
id: subtitleLabel
|
||||||
visible: text !== ""
|
visible: text !== ""
|
||||||
text: Backend.messagesModel[room_id].reloadThis, get_text()
|
text: Backend.models.messages[room_id].reloadThis, get_text()
|
||||||
textFormat: Text.StyledText
|
textFormat: Text.StyledText
|
||||||
|
|
||||||
font.pixelSize: smallSize
|
font.pixelSize: smallSize
|
||||||
|
|
|
@ -4,7 +4,7 @@ import QtQuick.Layouts 1.4
|
||||||
import "../base" as Base
|
import "../base" as Base
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
property var user: null
|
property var for_user_id: null
|
||||||
|
|
||||||
property int contentHeight: 0
|
property int contentHeight: 0
|
||||||
|
|
||||||
|
@ -21,6 +21,6 @@ ListView {
|
||||||
|
|
||||||
id: "roomList"
|
id: "roomList"
|
||||||
spacing: 8
|
spacing: 8
|
||||||
model: Backend.roomsModel[user.user_id]
|
model: Backend.models.rooms[for_user_id]
|
||||||
delegate: RoomDelegate {}
|
delegate: RoomDelegate {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ from PyQt5.QtQml import QQmlApplicationEngine
|
||||||
|
|
||||||
from .__about__ import __doc__
|
from .__about__ import __doc__
|
||||||
from .app import Application
|
from .app import Application
|
||||||
from .backend.matrix_nio.backend import MatrixNioBackend as CurrentBackend
|
from .backend.backend import Backend
|
||||||
|
|
||||||
# logging.basicConfig(level=logging.INFO)
|
# logging.basicConfig(level=logging.INFO)
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ class Engine(QQmlApplicationEngine):
|
||||||
parent: Optional[QObject] = None) -> None:
|
parent: Optional[QObject] = None) -> None:
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.app = app
|
self.app = app
|
||||||
self.backend = CurrentBackend()
|
self.backend = Backend()
|
||||||
self.app_dir = Path(sys.argv[0]).resolve().parent
|
self.app_dir = Path(sys.argv[0]).resolve().parent
|
||||||
|
|
||||||
# Set QML properties
|
# Set QML properties
|
||||||
|
|
Loading…
Reference in New Issue
Block a user