From 8a3189df158649ad1b9fbb2554cc13e30d8eca85 Mon Sep 17 00:00:00 2001 From: miruka Date: Sun, 14 Apr 2019 16:12:07 -0400 Subject: [PATCH] Add users currently typing in room bar --- TODO.md | 2 ++ harmonyqml/backend/client.py | 15 ++++++--- harmonyqml/backend/model/items.py | 16 +++------- harmonyqml/backend/model/list_model.py | 8 ++++- harmonyqml/backend/signal_manager.py | 10 +++++- harmonyqml/components/UI.qml | 4 --- harmonyqml/components/chat/Root.qml | 2 ++ harmonyqml/components/chat/TypingUsersBar.qml | 31 +++++++++++++++++++ harmonyqml/components/chat/utils.js | 18 +++++++++++ 9 files changed, 85 insertions(+), 21 deletions(-) create mode 100644 harmonyqml/components/chat/TypingUsersBar.qml diff --git a/TODO.md b/TODO.md index bcbaf207..3bfc5c00 100644 --- a/TODO.md +++ b/TODO.md @@ -20,3 +20,5 @@ - Load previous events on scroll up - Migrate more JS functions to their own files + +- Accept room\_id arg for getUser diff --git a/harmonyqml/backend/client.py b/harmonyqml/backend/client.py index 18ae3528..67775f7e 100644 --- a/harmonyqml/backend/client.py +++ b/harmonyqml/backend/client.py @@ -37,10 +37,11 @@ def futurize(func: Callable) -> Callable: class Client(QObject): - roomInvited = pyqtSignal(str) - roomJoined = pyqtSignal(str) - roomLeft = pyqtSignal(str) - roomEventReceived = pyqtSignal(str, str, dict) + roomInvited = pyqtSignal(str) + roomJoined = pyqtSignal(str) + roomLeft = pyqtSignal(str) + roomEventReceived = pyqtSignal(str, str, dict) + roomTypingUsersUpdated = pyqtSignal(str, list) def __init__(self, hostname: str, username: str, device_id: str = "" @@ -121,5 +122,11 @@ class Client(QObject): room_id, type(ev).__name__, ev.__dict__ ) + for ev in room_info.ephemeral: + if isinstance(ev, nr.TypingNoticeEvent): + self.roomTypingUsersUpdated.emit(room_id, ev.users) + else: + print("ephemeral event: ", ev) + for room_id in response.rooms.leave: self.roomLeft.emit(room_id) diff --git a/harmonyqml/backend/model/items.py b/harmonyqml/backend/model/items.py index d7a2268b..e785f504 100644 --- a/harmonyqml/backend/model/items.py +++ b/harmonyqml/backend/model/items.py @@ -1,12 +1,10 @@ # Copyright 2019 miruka # This file is part of harmonyqml, licensed under GPLv3. -from typing import Dict, NamedTuple, Optional +from typing import Dict, List, NamedTuple, Optional from PyQt5.QtCore import QDateTime -from .enums import Activity, Presence - class User(NamedTuple): user_id: str @@ -16,14 +14,10 @@ class User(NamedTuple): class Room(NamedTuple): - room_id: str - display_name: Optional[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 + room_id: str + display_name: Optional[str] + description: str = "" + typing_users: List[str] = [] class RoomEvent(NamedTuple): diff --git a/harmonyqml/backend/model/list_model.py b/harmonyqml/backend/model/list_model.py index d4e76021..2a587978 100644 --- a/harmonyqml/backend/model/list_model.py +++ b/harmonyqml/backend/model/list_model.py @@ -10,6 +10,7 @@ from PyQt5.QtCore import ( ) NewValue = Union[Mapping[str, Any], Sequence] +ReturnItem = Dict[str, Any] class ListModel(QAbstractListModel): @@ -96,7 +97,7 @@ class ListModel(QAbstractListModel): @pyqtSlot(int, result="QVariantMap") - def get(self, index: int) -> Dict[str, Any]: + def get(self, index: int) -> ReturnItem: return self._list[index]._asdict() @@ -110,6 +111,11 @@ class ListModel(QAbstractListModel): f"property {prop!r} set to {is_value!r}.") + @pyqtSlot(str, "QVariant", result="QVariantMap") + def getWhere(self, prop: str, is_value: Any) -> ReturnItem: + return self.get(self.indexWhere(prop, is_value)) + + @pyqtSlot(int, list) def insert(self, index: int, value: NewValue) -> None: value = self._convert_new_value(value) diff --git a/harmonyqml/backend/signal_manager.py b/harmonyqml/backend/signal_manager.py index 4c8b8591..8b04645a 100644 --- a/harmonyqml/backend/signal_manager.py +++ b/harmonyqml/backend/signal_manager.py @@ -2,7 +2,7 @@ # This file is part of harmonyqml, licensed under GPLv3. from threading import Lock -from typing import Any, Deque, Dict, Optional +from typing import Any, Deque, Dict, List, Optional from PyQt5.QtCore import QDateTime, QObject, pyqtBoundSignal @@ -109,3 +109,11 @@ class SignalManager(QObject): break else: model.insert(0, new_event) + + + def onRoomTypingUsersUpdated( + self, client: Client, room_id: str, users: List[str] + ) -> None: + + rooms = self.backend.models.rooms[client.userID] + rooms[rooms.indexWhere("room_id", room_id)].typing_users = users diff --git a/harmonyqml/components/UI.qml b/harmonyqml/components/UI.qml index ac43eb5d..9baa38be 100644 --- a/harmonyqml/components/UI.qml +++ b/harmonyqml/components/UI.qml @@ -24,10 +24,6 @@ Controls1.SplitView { } id: "pageStack" -// initialItem: Chat.Root { - //user: Backend.accountsModel.get(0) - //room: Backend.roomsModel[Backend.accountsModel.get(0).user_id].get(0) - //} onCurrentItemChanged: currentItem.forceActiveFocus() diff --git a/harmonyqml/components/chat/Root.qml b/harmonyqml/components/chat/Root.qml index 5c775538..dd4d0b9d 100644 --- a/harmonyqml/components/chat/Root.qml +++ b/harmonyqml/components/chat/Root.qml @@ -6,11 +6,13 @@ ColumnLayout { property var user_id: null property var room: null + id: chatPage spacing: 0 onFocusChanged: sendBox.setFocus() RoomHeader {} MessageList {} + TypingUsersBar {} SendBox { id: sendBox } } diff --git a/harmonyqml/components/chat/TypingUsersBar.qml b/harmonyqml/components/chat/TypingUsersBar.qml new file mode 100644 index 00000000..eb92a411 --- /dev/null +++ b/harmonyqml/components/chat/TypingUsersBar.qml @@ -0,0 +1,31 @@ +import QtQuick 2.7 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.4 +import "../base" as Base +import "utils.js" as ChatJS + +Rectangle { + id: root + Layout.fillWidth: true + Layout.minimumHeight: usersLabel.text ? usersLabel.implicitHeight : 0 + Layout.maximumHeight: Layout.minimumHeight + color: "#BBB" + + Base.HLabel { + id: "usersLabel" + anchors.fill: parent + + Timer { + interval: 500 + repeat: true + running: true + triggeredOnStart: true + onTriggered: usersLabel.text = ChatJS.get_typing_users_text( + chatPage.user_id, chatPage.room.room_id + ) + } + + elide: Text.ElideMiddle + maximumLineCount: 1 + } +} diff --git a/harmonyqml/components/chat/utils.js b/harmonyqml/components/chat/utils.js index f65e579a..9e95d36b 100644 --- a/harmonyqml/components/chat/utils.js +++ b/harmonyqml/components/chat/utils.js @@ -124,3 +124,21 @@ function get_member_event_text(dict) { return "" } + + +function get_typing_users_text(account_id, room_id) { + var names = [] + var room = Backend.models.rooms.get(account_id) + .getWhere("room_id", room_id) + + for (var i = 0; i < room.typing_users.length; i++) { + names.push(Backend.getUser(room.typing_users[i]).display_name) + } + + if (names.length < 1) { return "" } + + return "🖋 " + + [names.slice(0, -1).join(", "), names.slice(-1)[0]] + .join(names.length < 2 ? "" : " and ") + + (names.length > 1 ? " are" : " is") + " typing…" +}