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:
miruka 2019-04-13 08:59:10 -04:00
parent d8c6ffefe0
commit 13fca98838
8 changed files with 58 additions and 36 deletions

View File

@ -2,8 +2,11 @@
# This file is part of harmonyqml, licensed under GPLv3.
import functools
import logging
import sys
import traceback
from concurrent.futures import Future, ThreadPoolExecutor
from threading import Event
from threading import Event, currentThread
from typing import Callable, DefaultDict, Dict
from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal, pyqtSlot
@ -22,7 +25,16 @@ _POOLS: DefaultDict[str, ThreadPoolExecutor] = \
def futurize(func: Callable) -> Callable:
@functools.wraps(func)
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
@ -91,7 +103,7 @@ class Client(QObject):
@futurize
def startSyncing(self) -> None:
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():
self._stop_sync.clear()
@ -105,7 +117,7 @@ class Client(QObject):
for room_id in response.rooms.join:
self.roomJoined.emit(room_id)
for room_id in response.rooms.left:
for room_id in response.rooms.leave:
self.roomLeft.emit(room_id)

View File

@ -17,7 +17,7 @@ class User(NamedTuple):
class Room(NamedTuple):
room_id: str
display_name: str
display_name: Optional[str]
description: str = ""
unread_messages: int = 0
presence: Presence = Presence.none

View File

@ -129,7 +129,7 @@ class ListModel(QAbstractListModel):
@pyqtSlot(int, list)
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)
self._list[index] = value
self.dataChanged.emit(qidx, qidx, self.roleNames())
@ -139,7 +139,7 @@ class ListModel(QAbstractListModel):
@pyqtSlot(int, str, "QVariant")
def setProperty(self, index: int, prop: str, value: Any) -> None:
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._update_count += 1

View File

@ -85,14 +85,12 @@ class NetworkManager:
except NioErrorResponse as err:
logging.error("read bad response for %s: %s", nio_func, err)
self._close_socket(sock)
if self._should_abort_talk(err):
logging.error("aborting talk")
break
time.sleep(10)
except Exception as err:
logging.error("talk exception for %s: %r", nio_func, err)
break
time.sleep(10)
else:
break

View File

@ -1,11 +1,13 @@
# Copyright 2019 miruka
# This file is part of harmonyqml, licensed under GPLv3.
from typing import Optional
from PyQt5.QtCore import QObject
from .backend import Backend
from .client import Client
from .model.items import User, Room
from .model.items import Room, User
class SignalManager(QObject):
@ -43,14 +45,25 @@ class SignalManager(QObject):
def onRoomJoined(self, client: Client, room_id: str) -> None:
room = client.nio.rooms[room_id]
name = room.name or room.canonical_alias or room.group_name()
model = self.backend.models.rooms[client.userID]
room = client.nio.rooms[room_id]
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,
display_name = name,
display_name = room.name or room.canonical_alias or group_name(),
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:

View File

@ -4,7 +4,7 @@ import QtQuick.Layouts 1.4
Item {
property bool invisible: false
property string name: "?"
property var name: null
property var imageSource: null
property int dimmension: 48
@ -16,11 +16,13 @@ Item {
id: "letterRectangle"
anchors.fill: parent
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 {
anchors.centerIn: parent
text: name.charAt(0)
text: name ? name.charAt(0) : "?"
color: "white"
font.pixelSize: letterRectangle.height / 1.4
}

View File

@ -6,7 +6,7 @@ import "../base" as Base
MouseArea {
id: "root"
width: roomList.width
height: Math.max(roomLabel.height + subtitleLabel.height, avatar.height)
height: roomList.childrenHeight
onClicked: pageStack.show_room(
roomList.user_id,
@ -18,14 +18,15 @@ MouseArea {
id: row
spacing: 1
Base.Avatar { id: avatar; name: display_name; dimmension: 36 }
Base.Avatar { id: avatar; name: display_name; dimmension: root.height }
ColumnLayout {
spacing: 0
Base.HLabel {
id: roomLabel
text: display_name
text: display_name ? display_name : "<i>Empty room</i>"
textFormat: Text.StyledText
elide: Text.ElideRight
maximumLineCount: 1
Layout.maximumWidth: row.width - row.spacing - avatar.width
@ -37,11 +38,12 @@ MouseArea {
rightPadding: leftPadding
}
Base.HLabel {
function get_text() {
var msgs = Backend.models.messages.get(room_id)
if (! msgs || msgs.count < 1) { return "" }
property var msgModel: Backend.models.messages.get(room_id)
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 ?
"darkblue" : "purple")
var client = Backend.clientManager.clients[RoomList.for_user_id]
@ -54,7 +56,7 @@ MouseArea {
id: subtitleLabel
visible: text !== ""
text: Backend.models.messages.get(room_id).reloadThis, get_text()
text: msgModel.reloadThis, get_text()
textFormat: Text.StyledText
font.pixelSize: smallSize

View File

@ -6,17 +6,12 @@ import "../base" as Base
ListView {
property var for_user_id: null
property int childrenHeight: 36
property int contentHeight: 0
onCountChanged: {
var children = roomList.contentItem.children
var childrenHeight = 0
for (var i = 0; i < children.length; i++) {
childrenHeight += children[i].height
}
contentHeight = childrenHeight + spacing * (children.length - 1)
contentHeight = childrenHeight * model.count +
spacing * (model.count - 1)
}
id: "roomList"