Display room messages and other events
This commit is contained in:
parent
5c8fd4500d
commit
9c66166c4f
21
TODO.md
21
TODO.md
|
@ -1 +1,20 @@
|
||||||
-
|
- Separate categories for invited, group and direct rooms
|
||||||
|
- Invited → Accept/Deny dialog
|
||||||
|
- Keep the room header name and topic updated
|
||||||
|
- Merge login page
|
||||||
|
- Show actual display name for AccountDelegate
|
||||||
|
|
||||||
|
- When inviting someone to direct chat, room is "Empty room" until accepted,
|
||||||
|
it should be the peer's display name instead.
|
||||||
|
- Support "Empty room (was ...)" after peer left
|
||||||
|
|
||||||
|
- Catch network errors in socket operations
|
||||||
|
|
||||||
|
- Proper logoff when closing client
|
||||||
|
|
||||||
|
- Handle cases where an avatar char is # or @ (#alias room, @user\_id)
|
||||||
|
|
||||||
|
- Use Loader? for MessageDelegate to show sub-components based on condition
|
||||||
|
- Better names and organization for the Message components
|
||||||
|
|
||||||
|
- Load previous events on scroll up
|
||||||
|
|
|
@ -2,10 +2,12 @@
|
||||||
# This file is part of harmonyqml, licensed under GPLv3.
|
# This file is part of harmonyqml, licensed under GPLv3.
|
||||||
|
|
||||||
import hashlib
|
import hashlib
|
||||||
|
from typing import Dict
|
||||||
|
|
||||||
from PyQt5.QtCore import QObject, pyqtProperty, pyqtSlot
|
from PyQt5.QtCore import QObject, pyqtProperty, pyqtSlot
|
||||||
|
|
||||||
from .client_manager import ClientManager
|
from .client_manager import ClientManager
|
||||||
|
from .model.items import User
|
||||||
from .model.qml_models import QMLModels
|
from .model.qml_models import QMLModels
|
||||||
|
|
||||||
|
|
||||||
|
@ -35,6 +37,18 @@ class Backend(QObject):
|
||||||
return self._models
|
return self._models
|
||||||
|
|
||||||
|
|
||||||
|
@pyqtSlot(str, result="QVariantMap")
|
||||||
|
def getUser(self, user_id: str) -> Dict[str, str]:
|
||||||
|
for client in self.clientManager.clients.values():
|
||||||
|
for room in client.nio.rooms.values():
|
||||||
|
|
||||||
|
name = room.user_name(user_id)
|
||||||
|
if name:
|
||||||
|
return User(user_id=user_id, display_name=name)._asdict()
|
||||||
|
|
||||||
|
return User(user_id=user_id, display_name=user_id)._asdict()
|
||||||
|
|
||||||
|
|
||||||
@pyqtSlot(str, result=float)
|
@pyqtSlot(str, result=float)
|
||||||
def hueFromString(self, string: str) -> float:
|
def hueFromString(self, string: str) -> float:
|
||||||
# pylint:disable=no-self-use
|
# pylint:disable=no-self-use
|
||||||
|
|
|
@ -7,15 +7,13 @@ import sys
|
||||||
import traceback
|
import traceback
|
||||||
from concurrent.futures import Future, ThreadPoolExecutor
|
from concurrent.futures import Future, ThreadPoolExecutor
|
||||||
from threading import Event, currentThread
|
from threading import Event, currentThread
|
||||||
from typing import Callable, DefaultDict, Dict
|
from typing import Callable, DefaultDict
|
||||||
|
|
||||||
from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal, pyqtSlot
|
from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal, 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] = \
|
||||||
|
@ -42,6 +40,7 @@ class Client(QObject):
|
||||||
roomInvited = pyqtSignal(str)
|
roomInvited = pyqtSignal(str)
|
||||||
roomJoined = pyqtSignal(str)
|
roomJoined = pyqtSignal(str)
|
||||||
roomLeft = pyqtSignal(str)
|
roomLeft = pyqtSignal(str)
|
||||||
|
roomEventReceived = pyqtSignal(str, str, dict)
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, hostname: str, username: str, device_id: str = ""
|
def __init__(self, hostname: str, username: str, device_id: str = ""
|
||||||
|
@ -114,21 +113,13 @@ class Client(QObject):
|
||||||
for room_id in response.rooms.invite:
|
for room_id in response.rooms.invite:
|
||||||
self.roomInvited.emit(room_id)
|
self.roomInvited.emit(room_id)
|
||||||
|
|
||||||
for room_id in response.rooms.join:
|
for room_id, room_info in response.rooms.join.items():
|
||||||
self.roomJoined.emit(room_id)
|
self.roomJoined.emit(room_id)
|
||||||
|
|
||||||
|
for ev in room_info.timeline.events:
|
||||||
|
self.roomEventReceived.emit(
|
||||||
|
room_id, type(ev).__name__, ev.__dict__
|
||||||
|
)
|
||||||
|
|
||||||
for room_id in response.rooms.leave:
|
for room_id in response.rooms.leave:
|
||||||
self.roomLeft.emit(room_id)
|
self.roomLeft.emit(room_id)
|
||||||
|
|
||||||
|
|
||||||
@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()
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
# 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 NamedTuple, Optional
|
from typing import Dict, NamedTuple, Optional
|
||||||
|
|
||||||
from PyQt5.QtCore import QDateTime
|
from PyQt5.QtCore import QDateTime
|
||||||
|
|
||||||
from .enums import Activity, MessageKind, Presence
|
from .enums import Activity, Presence
|
||||||
|
|
||||||
|
|
||||||
class User(NamedTuple):
|
class User(NamedTuple):
|
||||||
|
@ -26,9 +26,7 @@ class Room(NamedTuple):
|
||||||
avatar_url: Optional[str] = None
|
avatar_url: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
class Message(NamedTuple):
|
class RoomEvent(NamedTuple):
|
||||||
sender_id: str
|
type: str
|
||||||
date_time: QDateTime
|
date_time: QDateTime
|
||||||
content: str
|
dict: Dict[str, str]
|
||||||
kind: MessageKind = MessageKind.text
|
|
||||||
sender_avatar: Optional[str] = None
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ class QMLModels(QObject):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._accounts: ListModel = ListModel()
|
self._accounts: ListModel = ListModel()
|
||||||
self._rooms: ListModelMap = ListModelMap()
|
self._rooms: ListModelMap = ListModelMap()
|
||||||
self._messages: ListModelMap = ListModelMap()
|
self._room_events: ListModelMap = ListModelMap()
|
||||||
|
|
||||||
|
|
||||||
@pyqtProperty(ListModel, constant=True)
|
@pyqtProperty(ListModel, constant=True)
|
||||||
|
@ -29,5 +29,5 @@ class QMLModels(QObject):
|
||||||
|
|
||||||
|
|
||||||
@pyqtProperty("QVariant", constant=True)
|
@pyqtProperty("QVariant", constant=True)
|
||||||
def messages(self):
|
def roomEvents(self):
|
||||||
return self._messages
|
return self._room_events
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
# 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 typing import Any, Dict, Optional
|
||||||
|
|
||||||
from PyQt5.QtCore import QObject
|
from PyQt5.QtCore import QDateTime, QObject, pyqtBoundSignal
|
||||||
|
|
||||||
from .backend import Backend
|
from .backend import Backend
|
||||||
from .client import Client
|
from .client import Client
|
||||||
from .model.items import Room, User
|
from .model.items import Room, RoomEvent, User
|
||||||
|
|
||||||
|
|
||||||
class SignalManager(QObject):
|
class SignalManager(QObject):
|
||||||
def __init__(self, backend: Backend) -> None:
|
def __init__(self, backend: Backend) -> None:
|
||||||
super().__init__()
|
super().__init__(parent=backend)
|
||||||
self.backend = backend
|
self.backend = backend
|
||||||
|
|
||||||
cm = self.backend.clientManager
|
cm = self.backend.clientManager
|
||||||
|
@ -34,10 +34,15 @@ class SignalManager(QObject):
|
||||||
|
|
||||||
|
|
||||||
def connectClient(self, client: Client) -> None:
|
def connectClient(self, client: Client) -> None:
|
||||||
for sig_name in ("roomInvited", "roomJoined", "roomLeft"):
|
for name in dir(client):
|
||||||
sig = getattr(client, sig_name)
|
attr = getattr(client, name)
|
||||||
on_sig = getattr(self, f"on{sig_name[0].upper()}{sig_name[1:]}")
|
|
||||||
sig.connect(lambda room_id, o=on_sig, c=client: o(c, room_id))
|
if isinstance(attr, pyqtBoundSignal):
|
||||||
|
def onSignal(*args, name=name) -> None:
|
||||||
|
func = getattr(self, f"on{name[0].upper()}{name[1:]}")
|
||||||
|
func(client, *args)
|
||||||
|
|
||||||
|
attr.connect(onSignal)
|
||||||
|
|
||||||
|
|
||||||
def onRoomInvited(self, client: Client, room_id: str) -> None:
|
def onRoomInvited(self, client: Client, room_id: str) -> None:
|
||||||
|
@ -69,3 +74,25 @@ class SignalManager(QObject):
|
||||||
def onRoomLeft(self, client: Client, room_id: str) -> None:
|
def onRoomLeft(self, client: Client, room_id: str) -> None:
|
||||||
rooms = self.backend.models.rooms[client.userID]
|
rooms = self.backend.models.rooms[client.userID]
|
||||||
del rooms[rooms.indexWhere("room_id", room_id)]
|
del rooms[rooms.indexWhere("room_id", room_id)]
|
||||||
|
|
||||||
|
|
||||||
|
def onRoomEventReceived(
|
||||||
|
self, _: Client, room_id: str, etype: str, edict: Dict[str, Any]
|
||||||
|
) -> None:
|
||||||
|
model = self.backend.models.roomEvents[room_id]
|
||||||
|
date_time = QDateTime.fromMSecsSinceEpoch(edict["server_timestamp"])
|
||||||
|
new_event = RoomEvent(type=etype, date_time=date_time, dict=edict)
|
||||||
|
|
||||||
|
# Insert event in model at the right position, based on timestamps
|
||||||
|
# to keep them sorted by date of arrival.
|
||||||
|
# Iterate in reverse, since a new event is more likely to be appended,
|
||||||
|
# but events can arrive out of order.
|
||||||
|
if not model or model[-1].date_time < new_event.date_time:
|
||||||
|
model.append(new_event)
|
||||||
|
else:
|
||||||
|
for i, event in enumerate(reversed(model)):
|
||||||
|
if event.date_time < new_event.date_time:
|
||||||
|
model.insert(-i, new_event)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
model.insert(0, new_event)
|
||||||
|
|
|
@ -17,9 +17,9 @@ Controls1.SplitView {
|
||||||
function show_page(componentName) {
|
function show_page(componentName) {
|
||||||
pageStack.replace(componentName + ".qml")
|
pageStack.replace(componentName + ".qml")
|
||||||
}
|
}
|
||||||
function show_room(user_obj, room_obj) {
|
function show_room(user_id, room_obj) {
|
||||||
pageStack.replace(
|
pageStack.replace(
|
||||||
"chat/Root.qml", { user: user_obj, room: room_obj }
|
"chat/Root.qml", { user_id: user_id, room: room_obj }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
45
harmonyqml/components/chat/EventContent.qml
Normal file
45
harmonyqml/components/chat/EventContent.qml
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
import QtQuick 2.7
|
||||||
|
import QtQuick.Controls 2.0
|
||||||
|
import QtQuick.Layouts 1.4
|
||||||
|
import "../base" as Base
|
||||||
|
import "get_event_text.js" as GetEventTextJS
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: row
|
||||||
|
spacing: standardSpacing
|
||||||
|
layoutDirection: isOwn ? Qt.RightToLeft : Qt.LeftToRight
|
||||||
|
anchors.right: isOwn ? parent.right : undefined
|
||||||
|
|
||||||
|
readonly property string contentText:
|
||||||
|
(isMessage || isUndecryptableEvent) ?
|
||||||
|
"" :
|
||||||
|
GetEventTextJS.get_event_text(type, dict)
|
||||||
|
|
||||||
|
Base.Avatar {
|
||||||
|
id: avatar
|
||||||
|
name: displayName
|
||||||
|
invisible: combine
|
||||||
|
dimmension: 28
|
||||||
|
}
|
||||||
|
|
||||||
|
Base.HLabel {
|
||||||
|
id: contentLabel
|
||||||
|
text: "<font color=gray>" +
|
||||||
|
displayName + " " + contentText +
|
||||||
|
" <font size=" + smallSize + "px>" +
|
||||||
|
Qt.formatDateTime(date_time, "hh:mm:ss") +
|
||||||
|
"</font></font>"
|
||||||
|
textFormat: Text.RichText
|
||||||
|
background: Rectangle {color: "#DDD"}
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
|
||||||
|
leftPadding: horizontalPadding
|
||||||
|
rightPadding: horizontalPadding
|
||||||
|
topPadding: verticalPadding
|
||||||
|
bottomPadding: verticalPadding
|
||||||
|
|
||||||
|
Layout.maximumWidth: Math.min(
|
||||||
|
600, messageListView.width - avatar.width - row.spacing
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
61
harmonyqml/components/chat/MessageContent.qml
Normal file
61
harmonyqml/components/chat/MessageContent.qml
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
import QtQuick 2.7
|
||||||
|
import QtQuick.Controls 2.0
|
||||||
|
import QtQuick.Layouts 1.4
|
||||||
|
import "../base" as Base
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: row
|
||||||
|
spacing: standardSpacing
|
||||||
|
layoutDirection: isOwn ? Qt.RightToLeft : Qt.LeftToRight
|
||||||
|
anchors.right: isOwn ? parent.right : undefined
|
||||||
|
|
||||||
|
Base.Avatar { id: avatar; invisible: combine; name: displayName }
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
Base.HLabel {
|
||||||
|
visible: ! combine
|
||||||
|
id: nameLabel
|
||||||
|
text: displayName
|
||||||
|
background: Rectangle {color: "#DDD"}
|
||||||
|
color: isOwn ? "teal" : "purple"
|
||||||
|
elide: Text.ElideRight
|
||||||
|
maximumLineCount: 1
|
||||||
|
Layout.preferredWidth: contentLabel.width
|
||||||
|
horizontalAlignment: isOwn ? Text.AlignRight : Text.AlignLeft
|
||||||
|
|
||||||
|
leftPadding: horizontalPadding
|
||||||
|
rightPadding: horizontalPadding
|
||||||
|
topPadding: verticalPadding
|
||||||
|
}
|
||||||
|
|
||||||
|
Base.HLabel {
|
||||||
|
id: contentLabel
|
||||||
|
//text: (isOwn ? "" : content + " ") +
|
||||||
|
//"<font size=" + smallSize + "px color=gray>" +
|
||||||
|
//Qt.formatDateTime(date_time, "hh:mm:ss") +
|
||||||
|
//"</font>" +
|
||||||
|
// (isOwn ? " " + content : "")
|
||||||
|
|
||||||
|
text: (isUndecryptableEvent ?
|
||||||
|
"<font color=darkred>Missing decryption keys for this message.</font>" :
|
||||||
|
dict.formatted_body || dict.body) +
|
||||||
|
" <font size=" + smallSize + "px color=gray>" +
|
||||||
|
Qt.formatDateTime(date_time, "hh:mm:ss") +
|
||||||
|
"</font>"
|
||||||
|
textFormat: Text.RichText
|
||||||
|
background: Rectangle {color: "#DDD"}
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
|
||||||
|
leftPadding: horizontalPadding
|
||||||
|
rightPadding: horizontalPadding
|
||||||
|
bottomPadding: verticalPadding
|
||||||
|
|
||||||
|
Layout.minimumWidth: nameLabel.implicitWidth
|
||||||
|
Layout.maximumWidth: Math.min(
|
||||||
|
600, messageListView.width - avatar.width - row.spacing
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,11 +10,16 @@ Column {
|
||||||
return Math.round((((date2 - date1) % 86400000) % 3600000) / 60000)
|
return Math.round((((date2 - date1) % 86400000) % 3600000) / 60000)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
readonly property bool isMessage: type.startsWith("RoomMessage")
|
||||||
|
|
||||||
|
readonly property bool isUndecryptableEvent:
|
||||||
|
type === "OlmEvent" || type === "MegolmEvent"
|
||||||
|
|
||||||
readonly property string displayName:
|
readonly property string displayName:
|
||||||
Backend.getUser(chatPage.room.room_id, sender_id).display_name
|
Backend.getUser(dict.sender).display_name
|
||||||
|
|
||||||
readonly property bool isOwn:
|
readonly property bool isOwn:
|
||||||
chatPage.user.user_id === sender_id
|
chatPage.user_id === dict.sender
|
||||||
|
|
||||||
readonly property var previousData:
|
readonly property var previousData:
|
||||||
index > 0 ? messageListView.model.get(index - 1) : null
|
index > 0 ? messageListView.model.get(index - 1) : null
|
||||||
|
@ -23,7 +28,8 @@ Column {
|
||||||
|
|
||||||
readonly property bool combine:
|
readonly property bool combine:
|
||||||
! isFirstMessage &&
|
! isFirstMessage &&
|
||||||
previousData.sender_id == sender_id &&
|
previousData.isMessage === isMessage &&
|
||||||
|
previousData.dict.sender === dict.sender &&
|
||||||
mins_between(previousData.date_time, date_time) <= 5
|
mins_between(previousData.date_time, date_time) <= 5
|
||||||
|
|
||||||
readonly property bool dayBreak:
|
readonly property bool dayBreak:
|
||||||
|
@ -49,59 +55,7 @@ Column {
|
||||||
|
|
||||||
Daybreak { visible: dayBreak }
|
Daybreak { visible: dayBreak }
|
||||||
|
|
||||||
|
MessageContent { visible: isMessage || isUndecryptableEvent }
|
||||||
|
|
||||||
Row {
|
EventContent { visible: ! (isMessage || isUndecryptableEvent) }
|
||||||
id: row
|
|
||||||
spacing: standardSpacing
|
|
||||||
layoutDirection: isOwn ? Qt.RightToLeft : Qt.LeftToRight
|
|
||||||
anchors.right: isOwn ? parent.right : undefined
|
|
||||||
|
|
||||||
Base.Avatar { id: avatar; invisible: combine; name: displayName }
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
spacing: 0
|
|
||||||
|
|
||||||
Base.HLabel {
|
|
||||||
visible: ! combine
|
|
||||||
id: nameLabel
|
|
||||||
text: displayName
|
|
||||||
background: Rectangle {color: "#DDD"}
|
|
||||||
color: isOwn ? "teal" : "purple"
|
|
||||||
elide: Text.ElideRight
|
|
||||||
maximumLineCount: 1
|
|
||||||
Layout.preferredWidth: contentLabel.width
|
|
||||||
horizontalAlignment: isOwn ? Text.AlignRight : Text.AlignLeft
|
|
||||||
|
|
||||||
leftPadding: horizontalPadding
|
|
||||||
rightPadding: horizontalPadding
|
|
||||||
topPadding: verticalPadding
|
|
||||||
}
|
|
||||||
|
|
||||||
Base.HLabel {
|
|
||||||
id: contentLabel
|
|
||||||
//text: (isOwn ? "" : content + " ") +
|
|
||||||
//"<font size=" + smallSize + "px color=gray>" +
|
|
||||||
//Qt.formatDateTime(date_time, "hh:mm:ss") +
|
|
||||||
//"</font>" +
|
|
||||||
// (isOwn ? " " + content : "")
|
|
||||||
|
|
||||||
text: content +
|
|
||||||
" <font size=" + smallSize + "px color=gray>" +
|
|
||||||
Qt.formatDateTime(date_time, "hh:mm:ss") +
|
|
||||||
"</font>"
|
|
||||||
textFormat: Text.RichText
|
|
||||||
background: Rectangle {color: "#DDD"}
|
|
||||||
wrapMode: Text.Wrap
|
|
||||||
|
|
||||||
leftPadding: horizontalPadding
|
|
||||||
rightPadding: horizontalPadding
|
|
||||||
bottomPadding: verticalPadding
|
|
||||||
|
|
||||||
Layout.minimumWidth: nameLabel.implicitWidth
|
|
||||||
Layout.maximumWidth: Math.min(
|
|
||||||
600, messageListView.width - avatar.width - row.spacing
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ Rectangle {
|
||||||
ListView {
|
ListView {
|
||||||
id: messageListView
|
id: messageListView
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
model: Backend.models.messages.get(chatPage.room.room_id)
|
model: Backend.models.roomEvents.get(chatPage.room.room_id)
|
||||||
delegate: MessageDelegate {}
|
delegate: MessageDelegate {}
|
||||||
//highlight: Rectangle {color: "lightsteelblue"; radius: 5}
|
//highlight: Rectangle {color: "lightsteelblue"; radius: 5}
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ Rectangle {
|
||||||
|
|
||||||
Base.HLabel {
|
Base.HLabel {
|
||||||
id: "roomDescription"
|
id: "roomDescription"
|
||||||
text: chatPage.room.description
|
text: chatPage.room.description || ""
|
||||||
font.pixelSize: smallSize
|
font.pixelSize: smallSize
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
maximumLineCount: 1
|
maximumLineCount: 1
|
||||||
|
|
|
@ -3,7 +3,7 @@ import QtQuick.Controls 2.2
|
||||||
import QtQuick.Layouts 1.4
|
import QtQuick.Layouts 1.4
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
property var user: null
|
property var user_id: null
|
||||||
property var room: null
|
property var room: null
|
||||||
|
|
||||||
id: chatPage
|
id: chatPage
|
||||||
|
|
|
@ -20,7 +20,7 @@ Rectangle {
|
||||||
|
|
||||||
Base.Avatar {
|
Base.Avatar {
|
||||||
id: "avatar"
|
id: "avatar"
|
||||||
name: chatPage.user.display_name
|
name: Backend.getUser(chatPage.user_id).display_name
|
||||||
dimmension: root.Layout.minimumHeight
|
dimmension: root.Layout.minimumHeight
|
||||||
//visible: textArea.text === ""
|
//visible: textArea.text === ""
|
||||||
visible: textArea.height <= root.Layout.minimumHeight
|
visible: textArea.height <= root.Layout.minimumHeight
|
||||||
|
|
122
harmonyqml/components/chat/get_event_text.js
Normal file
122
harmonyqml/components/chat/get_event_text.js
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
function get_event_text(type, dict) {
|
||||||
|
switch (type) {
|
||||||
|
case "RoomCreateEvent":
|
||||||
|
return (dict.federate ? "allowed" : "blocked") +
|
||||||
|
" users on other matrix servers " +
|
||||||
|
(dict.federate ? "to join" : "from joining") +
|
||||||
|
" this room."
|
||||||
|
break
|
||||||
|
|
||||||
|
case "RoomGuestAccessEvent":
|
||||||
|
return (dict.guest_access === "can_join" ? "allowed " : "forbad") +
|
||||||
|
"guests to join the room."
|
||||||
|
break
|
||||||
|
|
||||||
|
case "RoomJoinRulesEvent":
|
||||||
|
return "made the room " +
|
||||||
|
(dict.join_rule === "public." ? "public" : "invite only.")
|
||||||
|
break
|
||||||
|
|
||||||
|
case "RoomHistoryVisibilityEvent":
|
||||||
|
return get_history_visibility_event_text(dict)
|
||||||
|
break
|
||||||
|
|
||||||
|
case "PowerLevelsEvent":
|
||||||
|
return "changed the room's permissions."
|
||||||
|
|
||||||
|
case "RoomMemberEvent":
|
||||||
|
return get_member_event_text(dict)
|
||||||
|
break
|
||||||
|
|
||||||
|
case "RoomAliasEvent":
|
||||||
|
return "set the room's main address to " +
|
||||||
|
dict.canonical_alias + "."
|
||||||
|
break
|
||||||
|
|
||||||
|
case "RoomNameEvent":
|
||||||
|
return "changed the room's name to \"" + dict.name + "\"."
|
||||||
|
break
|
||||||
|
|
||||||
|
case "RoomTopicEvent":
|
||||||
|
return "changed the room's topic to \"" + dict.topic + "\"."
|
||||||
|
break
|
||||||
|
|
||||||
|
case "RoomEncryptionEvent":
|
||||||
|
return "turned on encryption for this room."
|
||||||
|
break
|
||||||
|
|
||||||
|
default:
|
||||||
|
console.log(type + "\n" + JSON.stringify(dict, null, 4) + "\n")
|
||||||
|
return "did something this client does not understand."
|
||||||
|
|
||||||
|
//case "CallEvent": TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function get_history_visibility_event_text(dict) {
|
||||||
|
switch (dict.history_visibility) {
|
||||||
|
case "shared":
|
||||||
|
var end = "all room members."
|
||||||
|
break
|
||||||
|
|
||||||
|
case "world_readable":
|
||||||
|
var end = "any member or outsider."
|
||||||
|
break
|
||||||
|
|
||||||
|
case "joined":
|
||||||
|
var end = "all room members since they joined."
|
||||||
|
break
|
||||||
|
|
||||||
|
case "invited":
|
||||||
|
var end = "all room members since they were invited."
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return "made future history visible to " + end
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function get_member_event_text(dict) {
|
||||||
|
var info = dict.content, prev = dict.prev_content
|
||||||
|
|
||||||
|
if (! prev || (info.membership != prev.membership)) {
|
||||||
|
switch (info.membership) {
|
||||||
|
case "join":
|
||||||
|
return "joined the room."
|
||||||
|
break
|
||||||
|
|
||||||
|
case "invite":
|
||||||
|
var name = Backend.getUser(dict.state_key).display_name
|
||||||
|
var name = name === dict.state_key ? info.displayname : name
|
||||||
|
return "invited " + name + " to the room."
|
||||||
|
break
|
||||||
|
|
||||||
|
case "leave":
|
||||||
|
return "left the room."
|
||||||
|
break
|
||||||
|
|
||||||
|
case "ban":
|
||||||
|
return "was banned from the room."
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var changed = []
|
||||||
|
|
||||||
|
if (prev && (info.avatar_url != prev.avatar_url)) {
|
||||||
|
changed.push("profile picture")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prev && (info.displayname != prev.displayname)) {
|
||||||
|
changed.push("display name from \"" +
|
||||||
|
(prev.displayname || dict.state_key) + '" to "' +
|
||||||
|
(info.displayname || dict.state_key) + '"')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changed.length > 0) {
|
||||||
|
return "changed their " + changed.join(" and ") + "."
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
|
@ -9,7 +9,7 @@ MouseArea {
|
||||||
height: roomList.childrenHeight
|
height: roomList.childrenHeight
|
||||||
|
|
||||||
onClicked: pageStack.show_room(
|
onClicked: pageStack.show_room(
|
||||||
roomList.user_id,
|
roomList.for_user_id,
|
||||||
roomList.model.get(index)
|
roomList.model.get(index)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ MouseArea {
|
||||||
rightPadding: leftPadding
|
rightPadding: leftPadding
|
||||||
}
|
}
|
||||||
Base.HLabel {
|
Base.HLabel {
|
||||||
property var msgModel: Backend.models.messages.get(room_id)
|
property var msgModel: Backend.models.roomEvents.get(room_id)
|
||||||
|
|
||||||
function get_text() {
|
function get_text() {
|
||||||
if (msgModel.count < 1) { return "" }
|
if (msgModel.count < 1) { return "" }
|
||||||
|
@ -46,17 +46,16 @@ MouseArea {
|
||||||
var msg = msgModel.get(-1)
|
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]
|
|
||||||
|
|
||||||
return "<font color=\"" + color_ + "\">" +
|
return "<font color=\"" + color_ + "\">" +
|
||||||
client.getUser(room_id, msg.sender_id).display_name +
|
Backend.getUser(msg.sender_id).display_name +
|
||||||
":</font> " +
|
":</font> " +
|
||||||
msg.content
|
msg.content
|
||||||
}
|
}
|
||||||
|
|
||||||
id: subtitleLabel
|
id: subtitleLabel
|
||||||
visible: text !== ""
|
visible: text !== ""
|
||||||
text: msgModel.reloadThis, get_text()
|
//text: msgModel.reloadThis, get_text()
|
||||||
textFormat: Text.StyledText
|
textFormat: Text.StyledText
|
||||||
|
|
||||||
font.pixelSize: smallSize
|
font.pixelSize: smallSize
|
||||||
|
|
Loading…
Reference in New Issue
Block a user