Rooms and threads fixes
- Fix roomList height again, now based on model.count(). All delegates are assumed to be the same height - Properly update room list when a room is joined or left - Catch exceptions happening in threads (futures), which previously passed silently - Show "Empty room?" as "<i>Empty Room</i>" + gray [?] avatar
This commit is contained in:
parent
d8c6ffefe0
commit
13fca98838
|
@ -2,8 +2,11 @@
|
||||||
# This file is part of harmonyqml, licensed under GPLv3.
|
# This file is part of harmonyqml, licensed under GPLv3.
|
||||||
|
|
||||||
import functools
|
import functools
|
||||||
|
import logging
|
||||||
|
import sys
|
||||||
|
import traceback
|
||||||
from concurrent.futures import Future, ThreadPoolExecutor
|
from concurrent.futures import Future, ThreadPoolExecutor
|
||||||
from threading import Event
|
from threading import Event, currentThread
|
||||||
from typing import Callable, DefaultDict, Dict
|
from typing import Callable, DefaultDict, Dict
|
||||||
|
|
||||||
from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal, pyqtSlot
|
from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal, pyqtSlot
|
||||||
|
@ -22,7 +25,16 @@ _POOLS: DefaultDict[str, ThreadPoolExecutor] = \
|
||||||
def futurize(func: Callable) -> Callable:
|
def futurize(func: Callable) -> Callable:
|
||||||
@functools.wraps(func)
|
@functools.wraps(func)
|
||||||
def wrapper(*args, **kwargs) -> Future:
|
def wrapper(*args, **kwargs) -> Future:
|
||||||
return args[0].pool.submit(func, *args, **kwargs) # args[0] = self
|
def run_and_catch_errs():
|
||||||
|
# Without this, exceptions are silently ignored
|
||||||
|
try:
|
||||||
|
func(*args, **kwargs)
|
||||||
|
except Exception:
|
||||||
|
traceback.print_exc()
|
||||||
|
logging.error("Exiting %s due to exception.", currentThread())
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
return args[0].pool.submit(run_and_catch_errs) # args[0] = self
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
@ -91,7 +103,7 @@ class Client(QObject):
|
||||||
@futurize
|
@futurize
|
||||||
def startSyncing(self) -> None:
|
def startSyncing(self) -> None:
|
||||||
while True:
|
while True:
|
||||||
self._on_sync(self.net.talk(self.nio.sync, timeout=10))
|
self._on_sync(self.net.talk(self.nio.sync, timeout=10_000))
|
||||||
|
|
||||||
if self._stop_sync.is_set():
|
if self._stop_sync.is_set():
|
||||||
self._stop_sync.clear()
|
self._stop_sync.clear()
|
||||||
|
@ -105,7 +117,7 @@ class Client(QObject):
|
||||||
for room_id in response.rooms.join:
|
for room_id in response.rooms.join:
|
||||||
self.roomJoined.emit(room_id)
|
self.roomJoined.emit(room_id)
|
||||||
|
|
||||||
for room_id in response.rooms.left:
|
for room_id in response.rooms.leave:
|
||||||
self.roomLeft.emit(room_id)
|
self.roomLeft.emit(room_id)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ class User(NamedTuple):
|
||||||
|
|
||||||
class Room(NamedTuple):
|
class Room(NamedTuple):
|
||||||
room_id: str
|
room_id: str
|
||||||
display_name: str
|
display_name: Optional[str]
|
||||||
description: str = ""
|
description: str = ""
|
||||||
unread_messages: int = 0
|
unread_messages: int = 0
|
||||||
presence: Presence = Presence.none
|
presence: Presence = Presence.none
|
||||||
|
|
|
@ -129,7 +129,7 @@ class ListModel(QAbstractListModel):
|
||||||
|
|
||||||
@pyqtSlot(int, list)
|
@pyqtSlot(int, list)
|
||||||
def set(self, index: int, value: NewValue) -> None:
|
def set(self, index: int, value: NewValue) -> None:
|
||||||
qidx = QAbstractListModel.index(index, 0)
|
qidx = QAbstractListModel.index(self, index, 0)
|
||||||
value = self._convert_new_value(value)
|
value = self._convert_new_value(value)
|
||||||
self._list[index] = value
|
self._list[index] = value
|
||||||
self.dataChanged.emit(qidx, qidx, self.roleNames())
|
self.dataChanged.emit(qidx, qidx, self.roleNames())
|
||||||
|
@ -139,7 +139,7 @@ class ListModel(QAbstractListModel):
|
||||||
@pyqtSlot(int, str, "QVariant")
|
@pyqtSlot(int, str, "QVariant")
|
||||||
def setProperty(self, index: int, prop: str, value: Any) -> None:
|
def setProperty(self, index: int, prop: str, value: Any) -> None:
|
||||||
self._list[index][self._roles.index(prop)] = value
|
self._list[index][self._roles.index(prop)] = value
|
||||||
qidx = QAbstractListModel.index(index, 0)
|
qidx = QAbstractListModel.index(self, index, 0)
|
||||||
self.dataChanged.emit(qidx, qidx, self.roleNames())
|
self.dataChanged.emit(qidx, qidx, self.roleNames())
|
||||||
self._update_count += 1
|
self._update_count += 1
|
||||||
|
|
||||||
|
|
|
@ -85,14 +85,12 @@ class NetworkManager:
|
||||||
except NioErrorResponse as err:
|
except NioErrorResponse as err:
|
||||||
logging.error("read bad response for %s: %s", nio_func, err)
|
logging.error("read bad response for %s: %s", nio_func, err)
|
||||||
self._close_socket(sock)
|
self._close_socket(sock)
|
||||||
|
|
||||||
if self._should_abort_talk(err):
|
if self._should_abort_talk(err):
|
||||||
logging.error("aborting talk")
|
logging.error("aborting talk")
|
||||||
break
|
break
|
||||||
time.sleep(10)
|
|
||||||
|
|
||||||
except Exception as err:
|
time.sleep(10)
|
||||||
logging.error("talk exception for %s: %r", nio_func, err)
|
|
||||||
break
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
# 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 typing import Optional
|
||||||
|
|
||||||
from PyQt5.QtCore import QObject
|
from PyQt5.QtCore import QObject
|
||||||
|
|
||||||
from .backend import Backend
|
from .backend import Backend
|
||||||
from .client import Client
|
from .client import Client
|
||||||
from .model.items import User, Room
|
from .model.items import Room, User
|
||||||
|
|
||||||
|
|
||||||
class SignalManager(QObject):
|
class SignalManager(QObject):
|
||||||
|
@ -43,14 +45,25 @@ class SignalManager(QObject):
|
||||||
|
|
||||||
|
|
||||||
def onRoomJoined(self, client: Client, room_id: str) -> None:
|
def onRoomJoined(self, client: Client, room_id: str) -> None:
|
||||||
|
model = self.backend.models.rooms[client.userID]
|
||||||
room = client.nio.rooms[room_id]
|
room = client.nio.rooms[room_id]
|
||||||
name = room.name or room.canonical_alias or room.group_name()
|
|
||||||
|
|
||||||
self.backend.models.rooms[client.userID].append(Room(
|
def group_name() -> Optional[str]:
|
||||||
|
name = room.group_name()
|
||||||
|
return None if name == "Empty room?" else name
|
||||||
|
|
||||||
|
item = Room(
|
||||||
room_id = room_id,
|
room_id = room_id,
|
||||||
display_name = name,
|
display_name = room.name or room.canonical_alias or group_name(),
|
||||||
description = getattr(room, "topic", ""), # FIXME: outside init
|
description = getattr(room, "topic", ""), # FIXME: outside init
|
||||||
))
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
index = model.indexWhere("room_id", room_id)
|
||||||
|
except ValueError:
|
||||||
|
model.append(item)
|
||||||
|
else:
|
||||||
|
model[index] = item
|
||||||
|
|
||||||
|
|
||||||
def onRoomLeft(self, client: Client, room_id: str) -> None:
|
def onRoomLeft(self, client: Client, room_id: str) -> None:
|
||||||
|
|
|
@ -4,7 +4,7 @@ import QtQuick.Layouts 1.4
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
property bool invisible: false
|
property bool invisible: false
|
||||||
property string name: "?"
|
property var name: null
|
||||||
property var imageSource: null
|
property var imageSource: null
|
||||||
property int dimmension: 48
|
property int dimmension: 48
|
||||||
|
|
||||||
|
@ -16,11 +16,13 @@ Item {
|
||||||
id: "letterRectangle"
|
id: "letterRectangle"
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
visible: ! invisible && imageSource === null
|
visible: ! invisible && imageSource === null
|
||||||
color: Qt.hsla(Backend.hueFromString(name), 0.22, 0.5, 1)
|
color: name ?
|
||||||
|
Qt.hsla(Backend.hueFromString(name), 0.22, 0.5, 1) :
|
||||||
|
Qt.hsla(0, 0, 0.22, 1)
|
||||||
|
|
||||||
HLabel {
|
HLabel {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: name.charAt(0)
|
text: name ? name.charAt(0) : "?"
|
||||||
color: "white"
|
color: "white"
|
||||||
font.pixelSize: letterRectangle.height / 1.4
|
font.pixelSize: letterRectangle.height / 1.4
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import "../base" as Base
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: "root"
|
id: "root"
|
||||||
width: roomList.width
|
width: roomList.width
|
||||||
height: Math.max(roomLabel.height + subtitleLabel.height, avatar.height)
|
height: roomList.childrenHeight
|
||||||
|
|
||||||
onClicked: pageStack.show_room(
|
onClicked: pageStack.show_room(
|
||||||
roomList.user_id,
|
roomList.user_id,
|
||||||
|
@ -18,14 +18,15 @@ MouseArea {
|
||||||
id: row
|
id: row
|
||||||
spacing: 1
|
spacing: 1
|
||||||
|
|
||||||
Base.Avatar { id: avatar; name: display_name; dimmension: 36 }
|
Base.Avatar { id: avatar; name: display_name; dimmension: root.height }
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
Base.HLabel {
|
Base.HLabel {
|
||||||
id: roomLabel
|
id: roomLabel
|
||||||
text: display_name
|
text: display_name ? display_name : "<i>Empty room</i>"
|
||||||
|
textFormat: Text.StyledText
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
maximumLineCount: 1
|
maximumLineCount: 1
|
||||||
Layout.maximumWidth: row.width - row.spacing - avatar.width
|
Layout.maximumWidth: row.width - row.spacing - avatar.width
|
||||||
|
@ -37,11 +38,12 @@ MouseArea {
|
||||||
rightPadding: leftPadding
|
rightPadding: leftPadding
|
||||||
}
|
}
|
||||||
Base.HLabel {
|
Base.HLabel {
|
||||||
function get_text() {
|
property var msgModel: Backend.models.messages.get(room_id)
|
||||||
var msgs = Backend.models.messages.get(room_id)
|
|
||||||
if (! msgs || msgs.count < 1) { return "" }
|
|
||||||
|
|
||||||
var msg = msgs.get(-1)
|
function get_text() {
|
||||||
|
if (msgModel.count < 1) { return "" }
|
||||||
|
|
||||||
|
var msg = msgModel.get(-1)
|
||||||
var color_ = (msg.sender_id === roomList.user_id ?
|
var color_ = (msg.sender_id === roomList.user_id ?
|
||||||
"darkblue" : "purple")
|
"darkblue" : "purple")
|
||||||
var client = Backend.clientManager.clients[RoomList.for_user_id]
|
var client = Backend.clientManager.clients[RoomList.for_user_id]
|
||||||
|
@ -54,7 +56,7 @@ MouseArea {
|
||||||
|
|
||||||
id: subtitleLabel
|
id: subtitleLabel
|
||||||
visible: text !== ""
|
visible: text !== ""
|
||||||
text: Backend.models.messages.get(room_id).reloadThis, get_text()
|
text: msgModel.reloadThis, get_text()
|
||||||
textFormat: Text.StyledText
|
textFormat: Text.StyledText
|
||||||
|
|
||||||
font.pixelSize: smallSize
|
font.pixelSize: smallSize
|
||||||
|
|
|
@ -6,17 +6,12 @@ import "../base" as Base
|
||||||
ListView {
|
ListView {
|
||||||
property var for_user_id: null
|
property var for_user_id: null
|
||||||
|
|
||||||
|
property int childrenHeight: 36
|
||||||
property int contentHeight: 0
|
property int contentHeight: 0
|
||||||
|
|
||||||
onCountChanged: {
|
onCountChanged: {
|
||||||
var children = roomList.contentItem.children
|
contentHeight = childrenHeight * model.count +
|
||||||
var childrenHeight = 0
|
spacing * (model.count - 1)
|
||||||
|
|
||||||
for (var i = 0; i < children.length; i++) {
|
|
||||||
childrenHeight += children[i].height
|
|
||||||
}
|
|
||||||
|
|
||||||
contentHeight = childrenHeight + spacing * (children.length - 1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
id: "roomList"
|
id: "roomList"
|
||||||
|
|
Loading…
Reference in New Issue
Block a user