Capitalize all component folders
This commit is contained in:
118
harmonyqml/components/Chat/Banner.qml
Normal file
118
harmonyqml/components/Chat/Banner.qml
Normal file
@@ -0,0 +1,118 @@
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.2
|
||||
import QtQuick.Layouts 1.4
|
||||
import "../Base" as Base
|
||||
|
||||
Base.HGlassRectangle {
|
||||
id: banner
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 32
|
||||
|
||||
signal buttonClicked(string signalId)
|
||||
|
||||
property alias avatarName: bannerAvatar.name
|
||||
property alias avatarSource: bannerAvatar.imageSource
|
||||
property alias labelText: bannerLabel.text
|
||||
property alias buttonModel: bannerRepeater.model
|
||||
|
||||
Base.HRowLayout {
|
||||
id: bannerRow
|
||||
anchors.fill: parent
|
||||
|
||||
Base.HAvatar {
|
||||
id: bannerAvatar
|
||||
dimension: banner.Layout.preferredHeight
|
||||
}
|
||||
|
||||
Base.HLabel {
|
||||
id: bannerLabel
|
||||
textFormat: Text.StyledText
|
||||
maximumLineCount: 1
|
||||
elide: Text.ElideRight
|
||||
|
||||
visible:
|
||||
bannerRow.width - bannerAvatar.width - bannerButtons.width > 30
|
||||
|
||||
Layout.maximumWidth:
|
||||
bannerRow.width -
|
||||
bannerAvatar.width - bannerButtons.width -
|
||||
Layout.leftMargin - Layout.rightMargin
|
||||
|
||||
Layout.leftMargin: 10
|
||||
Layout.rightMargin: Layout.leftMargin
|
||||
}
|
||||
|
||||
Item { Layout.fillWidth: true }
|
||||
|
||||
Base.HRowLayout {
|
||||
id: bannerButtons
|
||||
spacing: 0
|
||||
|
||||
function getButtonsWidth() {
|
||||
var total = 0
|
||||
|
||||
for (var i = 0; i < bannerRepeater.count; i++) {
|
||||
total += bannerRepeater.itemAt(i).implicitWidth
|
||||
}
|
||||
|
||||
return total
|
||||
}
|
||||
|
||||
property bool compact:
|
||||
bannerRow.width <
|
||||
bannerAvatar.width +
|
||||
bannerLabel.implicitWidth +
|
||||
bannerLabel.Layout.leftMargin +
|
||||
bannerLabel.Layout.rightMargin +
|
||||
getButtonsWidth()
|
||||
|
||||
property int displayMode:
|
||||
compact ? Button.IconOnly : Button.TextBesideIcon
|
||||
|
||||
Repeater {
|
||||
id: bannerRepeater
|
||||
model: []
|
||||
|
||||
Base.HButton {
|
||||
property bool alreadyClicked: false
|
||||
|
||||
text: modelData.text
|
||||
iconName: modelData.iconName
|
||||
display: bannerButtons.displayMode
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
propagateComposedEvents: true
|
||||
onClicked: {
|
||||
if (alreadyClicked) { return }
|
||||
|
||||
iconName = "hourglass"
|
||||
alreadyClicked = true
|
||||
|
||||
// modelData might be undefined after Backend call
|
||||
var signalId = modelData.signalId
|
||||
var isForget =
|
||||
modelData.clientFunction === "forgetRoom"
|
||||
|
||||
var future =
|
||||
Backend.clientManager.clients[chatPage.userId].
|
||||
call(modelData.clientFunction,
|
||||
modelData.clientArgs)
|
||||
|
||||
if (! isForget) {
|
||||
future.onGotResult.connect(function() {
|
||||
iconName = modelData.iconName
|
||||
})
|
||||
}
|
||||
|
||||
if (signalId) { buttonClicked(signalId) }
|
||||
}
|
||||
}
|
||||
|
||||
Layout.maximumWidth: bannerButtons.compact ? height : -1
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
14
harmonyqml/components/Chat/Daybreak.qml
Normal file
14
harmonyqml/components/Chat/Daybreak.qml
Normal file
@@ -0,0 +1,14 @@
|
||||
import QtQuick 2.7
|
||||
import "../Base" as Base
|
||||
|
||||
Base.HNoticeLabel {
|
||||
text: dateTime.toLocaleDateString()
|
||||
color: Base.HStyle.chat.daybreak.foreground
|
||||
backgroundColor: Base.HStyle.chat.daybreak.background
|
||||
radius: Base.HStyle.chat.daybreak.radius
|
||||
|
||||
width: messageDelegate.width
|
||||
//topPadding: messageDelegate.isFirstMessage ?
|
||||
//0 : messageDelegate.standardSpacing
|
||||
//bottomPadding: messageDelegate.standardSpacing
|
||||
}
|
54
harmonyqml/components/Chat/EventContent.qml
Normal file
54
harmonyqml/components/Chat/EventContent.qml
Normal file
@@ -0,0 +1,54 @@
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.0
|
||||
import QtQuick.Layouts 1.4
|
||||
import "../Base" as Base
|
||||
import "utils.js" as ChatJS
|
||||
|
||||
RowLayout {
|
||||
id: row
|
||||
spacing: standardSpacing / 2
|
||||
layoutDirection: isOwn ? Qt.RightToLeft : Qt.LeftToRight
|
||||
anchors.right: isOwn ? parent.right : undefined
|
||||
|
||||
readonly property string contentText:
|
||||
isMessage ? "" : ChatJS.getEventText(type, dict)
|
||||
|
||||
Base.HAvatar {
|
||||
id: avatar
|
||||
name: displayName
|
||||
hidden: combine
|
||||
dimension: 28
|
||||
}
|
||||
|
||||
Base.HLabel {
|
||||
id: contentLabel
|
||||
text: "<font color='" +
|
||||
Qt.hsla(Backend.hueFromString(displayName.value || dict.sender),
|
||||
Base.HStyle.chat.event.saturation,
|
||||
Base.HStyle.chat.event.lightness,
|
||||
1) +
|
||||
"'>" +
|
||||
(displayName.value || dict.sender) + " " +
|
||||
contentText +
|
||||
|
||||
" " +
|
||||
"<font size=" + Base.HStyle.fontSize.small + "px " +
|
||||
"color=" + Base.HStyle.chat.event.date + ">" +
|
||||
Qt.formatDateTime(dateTime, "hh:mm:ss") +
|
||||
"</font> " +
|
||||
"</font>"
|
||||
|
||||
textFormat: Text.RichText
|
||||
background: Rectangle {color: Base.HStyle.chat.event.background}
|
||||
wrapMode: Text.Wrap
|
||||
|
||||
leftPadding: horizontalPadding
|
||||
rightPadding: horizontalPadding
|
||||
topPadding: verticalPadding
|
||||
bottomPadding: verticalPadding
|
||||
|
||||
Layout.maximumWidth: Math.min(
|
||||
600, messageListView.width - avatar.width - row.spacing
|
||||
)
|
||||
}
|
||||
}
|
35
harmonyqml/components/Chat/InviteBanner.qml
Normal file
35
harmonyqml/components/Chat/InviteBanner.qml
Normal file
@@ -0,0 +1,35 @@
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.2
|
||||
import QtQuick.Layouts 1.4
|
||||
import "../Base" as Base
|
||||
|
||||
Banner {
|
||||
property var inviter: null
|
||||
|
||||
color: Base.HStyle.chat.inviteBanner.background
|
||||
|
||||
avatarName: inviter ? inviter.displayname : ""
|
||||
//avatarSource: inviter ? inviter.avatar_url : ""
|
||||
|
||||
labelText:
|
||||
(inviter ?
|
||||
("<b>" + inviter.displayname + "</b>") : qsTr("Someone")) +
|
||||
" " + qsTr("invited you to join the room.")
|
||||
|
||||
buttonModel: [
|
||||
{
|
||||
text: "Accept",
|
||||
iconName: "invite_accept",
|
||||
//iconColor: Qt.hsla(0.45, 0.9, 0.3, 1),
|
||||
clientFunction: "joinRoom",
|
||||
clientArgs: [chatPage.roomId],
|
||||
},
|
||||
{
|
||||
text: "Decline",
|
||||
iconName: "invite_decline",
|
||||
//iconColor: Qt.hsla(0.95, 0.9, 0.35, 1),
|
||||
clientFunction: "leaveRoom",
|
||||
clientArgs: [chatPage.roomId],
|
||||
}
|
||||
]
|
||||
}
|
30
harmonyqml/components/Chat/LeftBanner.qml
Normal file
30
harmonyqml/components/Chat/LeftBanner.qml
Normal file
@@ -0,0 +1,30 @@
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.2
|
||||
import QtQuick.Layouts 1.4
|
||||
import "../Base" as Base
|
||||
import "utils.js" as ChatJS
|
||||
|
||||
Banner {
|
||||
property var leftEvent: null
|
||||
|
||||
color: Base.HStyle.chat.leftBanner.background
|
||||
|
||||
onButtonClicked: if (signalId === "forget") {
|
||||
chatPage.canLoadPastEvents = false
|
||||
pageStack.clear()
|
||||
}
|
||||
|
||||
avatarName: ChatJS.getLeftBannerAvatarName(leftEvent, chatPage.userId)
|
||||
labelText: ChatJS.getLeftBannerText(leftEvent)
|
||||
|
||||
buttonModel: [
|
||||
{
|
||||
signalId: "forget",
|
||||
text: "Forget",
|
||||
iconName: "forget_room",
|
||||
//iconColor: Qt.hsla(0.95, 0.9, 0.35, 1),
|
||||
clientFunction: "forgetRoom",
|
||||
clientArgs: [chatPage.roomId],
|
||||
}
|
||||
]
|
||||
}
|
64
harmonyqml/components/Chat/MessageContent.qml
Normal file
64
harmonyqml/components/Chat/MessageContent.qml
Normal file
@@ -0,0 +1,64 @@
|
||||
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.HAvatar { id: avatar; hidden: combine; name: displayName }
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 0
|
||||
|
||||
Base.HLabel {
|
||||
visible: ! combine
|
||||
id: nameLabel
|
||||
text: displayName.value || dict.sender
|
||||
background: Rectangle {color: Base.HStyle.chat.message.background}
|
||||
color: Qt.hsla(Backend.hueFromString(text),
|
||||
Base.HStyle.displayName.saturation,
|
||||
Base.HStyle.displayName.lightness,
|
||||
1)
|
||||
elide: Text.ElideRight
|
||||
maximumLineCount: 1
|
||||
Layout.preferredWidth: contentLabel.width
|
||||
horizontalAlignment: isOwn ? Text.AlignRight : Text.AlignLeft
|
||||
|
||||
leftPadding: horizontalPadding
|
||||
rightPadding: horizontalPadding
|
||||
topPadding: verticalPadding
|
||||
}
|
||||
|
||||
Base.HRichLabel {
|
||||
id: contentLabel
|
||||
text: (dict.formatted_body ?
|
||||
Backend.htmlFilter.filter(dict.formatted_body) :
|
||||
dict.body) +
|
||||
" <font size=" + Base.HStyle.fontSize.small +
|
||||
"px color=" + Base.HStyle.chat.message.date + ">" +
|
||||
Qt.formatDateTime(dateTime, "hh:mm:ss") +
|
||||
"</font>" +
|
||||
(isLocalEcho ?
|
||||
" <font size=" + Base.HStyle.fontSize.small +
|
||||
"px>⏳</font>" : "")
|
||||
textFormat: Text.RichText
|
||||
background: Rectangle {color: Base.HStyle.chat.message.background}
|
||||
color: Base.HStyle.chat.message.body
|
||||
wrapMode: Text.Wrap
|
||||
|
||||
leftPadding: horizontalPadding
|
||||
rightPadding: horizontalPadding
|
||||
topPadding: nameLabel.visible ? 0 : verticalPadding
|
||||
bottomPadding: verticalPadding
|
||||
|
||||
Layout.minimumWidth: nameLabel.implicitWidth
|
||||
Layout.maximumWidth: Math.min(
|
||||
600, messageListView.width - avatar.width - row.spacing
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
86
harmonyqml/components/Chat/MessageDelegate.qml
Normal file
86
harmonyqml/components/Chat/MessageDelegate.qml
Normal file
@@ -0,0 +1,86 @@
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.0
|
||||
import QtQuick.Layouts 1.4
|
||||
import "../Base" as Base
|
||||
import "utils.js" as ChatJS
|
||||
|
||||
Column {
|
||||
id: messageDelegate
|
||||
|
||||
function minsBetween(date1, date2) {
|
||||
return Math.round((((date2 - date1) % 86400000) % 3600000) / 60000)
|
||||
}
|
||||
|
||||
function getIsMessage(type_) { return type_.startsWith("RoomMessage") }
|
||||
|
||||
function getPreviousItem() {
|
||||
return index < messageListView.model.count - 1 ?
|
||||
messageListView.model.get(index + 1) : null
|
||||
}
|
||||
|
||||
property var previousItem: getPreviousItem()
|
||||
signal reloadPreviousItem()
|
||||
onReloadPreviousItem: previousItem = getPreviousItem()
|
||||
|
||||
readonly property bool isMessage: getIsMessage(type)
|
||||
|
||||
readonly property bool isUndecryptableEvent:
|
||||
type === "OlmEvent" || type === "MegolmEvent"
|
||||
|
||||
readonly property var displayName:
|
||||
Backend.getUserDisplayName(dict.sender)
|
||||
|
||||
readonly property bool isOwn:
|
||||
chatPage.userId === dict.sender
|
||||
|
||||
readonly property bool isFirstEvent: type == "RoomCreateEvent"
|
||||
|
||||
readonly property bool combine:
|
||||
previousItem &&
|
||||
! talkBreak &&
|
||||
! dayBreak &&
|
||||
getIsMessage(previousItem.type) === isMessage &&
|
||||
previousItem.dict.sender === dict.sender &&
|
||||
minsBetween(previousItem.dateTime, dateTime) <= 5
|
||||
|
||||
readonly property bool dayBreak:
|
||||
isFirstEvent ||
|
||||
previousItem &&
|
||||
dateTime.getDate() != previousItem.dateTime.getDate()
|
||||
|
||||
readonly property bool talkBreak:
|
||||
previousItem &&
|
||||
! dayBreak &&
|
||||
minsBetween(previousItem.dateTime, dateTime) >= 20
|
||||
|
||||
|
||||
property int standardSpacing: 16
|
||||
property int horizontalPadding: 7
|
||||
property int verticalPadding: 5
|
||||
|
||||
ListView.onAdd: {
|
||||
var nextDelegate = messageListView.contentItem.children[index]
|
||||
if (nextDelegate) { nextDelegate.reloadPreviousItem() }
|
||||
}
|
||||
|
||||
width: parent.width
|
||||
|
||||
topPadding:
|
||||
isFirstEvent ? 0 :
|
||||
dayBreak ? standardSpacing * 2 :
|
||||
talkBreak ? standardSpacing * 3 :
|
||||
combine ? standardSpacing / 4 :
|
||||
standardSpacing
|
||||
|
||||
Daybreak { visible: dayBreak }
|
||||
|
||||
Item {
|
||||
visible: dayBreak
|
||||
width: parent.width
|
||||
height: topPadding
|
||||
}
|
||||
|
||||
MessageContent { visible: isMessage }
|
||||
|
||||
EventContent { visible: ! isMessage }
|
||||
}
|
55
harmonyqml/components/Chat/MessageList.qml
Normal file
55
harmonyqml/components/Chat/MessageList.qml
Normal file
@@ -0,0 +1,55 @@
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.2
|
||||
import QtQuick.Layouts 1.4
|
||||
import "../Base" as Base
|
||||
|
||||
Base.HGlassRectangle {
|
||||
property bool canLoadPastEvents: true
|
||||
property int space: 8
|
||||
|
||||
color: "transparent"
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
ListView {
|
||||
id: messageListView
|
||||
delegate: MessageDelegate {}
|
||||
model: Backend.models.roomEvents.get(chatPage.roomId)
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: space
|
||||
anchors.rightMargin: space
|
||||
|
||||
clip: true
|
||||
topMargin: space
|
||||
bottomMargin: space
|
||||
verticalLayoutDirection: ListView.BottomToTop
|
||||
|
||||
// Keep x scroll pages cached, to limit images having to be
|
||||
// reloaded from network.
|
||||
cacheBuffer: height * 6
|
||||
|
||||
// Declaring this "alias" provides the on... signal
|
||||
property real yPos: visibleArea.yPosition
|
||||
|
||||
onYPosChanged: {
|
||||
if (chatPage.canLoadPastEvents && yPos <= 0.1) {
|
||||
Backend.loadPastEvents(chatPage.roomId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Base.HLabel {
|
||||
visible: messageListView.model.count < 1
|
||||
anchors.centerIn: parent
|
||||
text: qsTr("Nothing to see here yet…")
|
||||
padding: 10
|
||||
topPadding: padding / 3
|
||||
bottomPadding: topPadding
|
||||
background: Rectangle {
|
||||
color: Base.HStyle.chat.messageList.background
|
||||
radius: 5
|
||||
}
|
||||
}
|
||||
}
|
54
harmonyqml/components/Chat/RoomHeader.qml
Normal file
54
harmonyqml/components/Chat/RoomHeader.qml
Normal file
@@ -0,0 +1,54 @@
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.2
|
||||
import QtQuick.Layouts 1.4
|
||||
import "../Base" as Base
|
||||
|
||||
Base.HGlassRectangle {
|
||||
property string displayName: ""
|
||||
property string topic: ""
|
||||
|
||||
id: root
|
||||
Layout.fillWidth: true
|
||||
Layout.minimumHeight: 36
|
||||
Layout.maximumHeight: Layout.minimumHeight
|
||||
color: Base.HStyle.chat.roomHeader.background
|
||||
|
||||
RowLayout {
|
||||
id: row
|
||||
spacing: 12
|
||||
anchors.fill: parent
|
||||
|
||||
Base.HAvatar {
|
||||
id: avatar
|
||||
Layout.alignment: Qt.AlignTop
|
||||
dimension: root.Layout.minimumHeight
|
||||
name: displayName
|
||||
}
|
||||
|
||||
Base.HLabel {
|
||||
id: roomName
|
||||
text: displayName
|
||||
font.pixelSize: Base.HStyle.fontSize.big
|
||||
elide: Text.ElideRight
|
||||
maximumLineCount: 1
|
||||
Layout.maximumWidth:
|
||||
row.width - row.spacing * (row.children.length - 1) -
|
||||
avatar.width
|
||||
}
|
||||
|
||||
Base.HLabel {
|
||||
id: roomTopic
|
||||
text: topic
|
||||
font.pixelSize: Base.HStyle.fontSize.small
|
||||
elide: Text.ElideRight
|
||||
maximumLineCount: 1
|
||||
Layout.maximumWidth:
|
||||
row.width -
|
||||
row.spacing * (row.children.length - 1) -
|
||||
avatar.width -
|
||||
roomName.width
|
||||
}
|
||||
|
||||
Item { Layout.fillWidth: true }
|
||||
}
|
||||
}
|
44
harmonyqml/components/Chat/Root.qml
Normal file
44
harmonyqml/components/Chat/Root.qml
Normal file
@@ -0,0 +1,44 @@
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.2
|
||||
import QtQuick.Layouts 1.4
|
||||
|
||||
ColumnLayout {
|
||||
property string userId: ""
|
||||
property string roomId: ""
|
||||
|
||||
readonly property var roomInfo:
|
||||
Backend.models.rooms.get(userId).getWhere("roomId", roomId)
|
||||
|
||||
property bool canLoadPastEvents: true
|
||||
|
||||
Component.onCompleted: console.log("replaced")
|
||||
|
||||
|
||||
id: chatPage
|
||||
spacing: 0
|
||||
onFocusChanged: sendBox.setFocus()
|
||||
|
||||
RoomHeader {
|
||||
displayName: roomInfo.displayName
|
||||
topic: roomInfo.topic
|
||||
}
|
||||
|
||||
MessageList {}
|
||||
|
||||
TypingUsersBar {}
|
||||
|
||||
InviteBanner {
|
||||
visible: roomInfo.category === "Invites"
|
||||
inviter: roomInfo.inviter
|
||||
}
|
||||
|
||||
SendBox {
|
||||
id: sendBox
|
||||
visible: roomInfo.category === "Rooms"
|
||||
}
|
||||
|
||||
LeftBanner {
|
||||
visible: roomInfo.category === "Left"
|
||||
leftEvent: roomInfo.leftEvent
|
||||
}
|
||||
}
|
62
harmonyqml/components/Chat/SendBox.qml
Normal file
62
harmonyqml/components/Chat/SendBox.qml
Normal file
@@ -0,0 +1,62 @@
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.2
|
||||
import QtQuick.Layouts 1.4
|
||||
import "../Base" as Base
|
||||
|
||||
Base.HGlassRectangle {
|
||||
function setFocus() { textArea.forceActiveFocus() }
|
||||
|
||||
id: root
|
||||
Layout.fillWidth: true
|
||||
Layout.minimumHeight: 32
|
||||
Layout.preferredHeight: textArea.implicitHeight
|
||||
// parent.height / 2 causes binding loop?
|
||||
Layout.maximumHeight: pageStack.height / 2
|
||||
color: Base.HStyle.chat.sendBox.background
|
||||
|
||||
Base.HRowLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
Base.HAvatar {
|
||||
id: avatar
|
||||
name: Backend.getUserDisplayName(chatPage.userId)
|
||||
dimension: root.Layout.minimumHeight
|
||||
}
|
||||
|
||||
Base.HScrollableTextArea {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
|
||||
id: textArea
|
||||
placeholderText: qsTr("Type a message...")
|
||||
area.focus: true
|
||||
|
||||
function setTyping(typing) {
|
||||
Backend.clientManager.clients[chatPage.userId]
|
||||
.setTypingState(chatPage.roomId, typing)
|
||||
}
|
||||
|
||||
onTextChanged: setTyping(Boolean(text))
|
||||
area.onEditingFinished: setTyping(false) // when lost focus
|
||||
|
||||
Keys.onReturnPressed: {
|
||||
event.accepted = true
|
||||
|
||||
if (event.modifiers & Qt.ShiftModifier ||
|
||||
event.modifiers & Qt.ControlModifier ||
|
||||
event.modifiers & Qt.AltModifier) {
|
||||
textArea.insert(textArea.cursorPosition, "\n")
|
||||
return
|
||||
}
|
||||
|
||||
if (textArea.text === "") { return }
|
||||
Backend.clientManager.clients[chatPage.userId]
|
||||
.sendMarkdown(chatPage.roomId, textArea.text)
|
||||
textArea.clear()
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: Keys.onReturnPressed(event) // numpad enter
|
||||
}
|
||||
}
|
||||
}
|
24
harmonyqml/components/Chat/TypingUsersBar.qml
Normal file
24
harmonyqml/components/Chat/TypingUsersBar.qml
Normal file
@@ -0,0 +1,24 @@
|
||||
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"
|
||||
|
||||
property var typingUsers: chatPage.roomInfo.typingUsers
|
||||
|
||||
Base.HLabel {
|
||||
id: usersLabel
|
||||
anchors.fill: parent
|
||||
text: ChatJS.getTypingUsersText(typingUsers, chatPage.userId)
|
||||
|
||||
elide: Text.ElideMiddle
|
||||
maximumLineCount: 1
|
||||
}
|
||||
}
|
212
harmonyqml/components/Chat/utils.js
Normal file
212
harmonyqml/components/Chat/utils.js
Normal file
@@ -0,0 +1,212 @@
|
||||
function getEventText(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 getHistoryVisibilityEventText(dict)
|
||||
break
|
||||
|
||||
case "PowerLevelsEvent":
|
||||
return "changed the room's permissions."
|
||||
|
||||
case "RoomMemberEvent":
|
||||
return getMemberEventText(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
|
||||
|
||||
case "OlmEvent":
|
||||
case "MegolmEvent":
|
||||
return "hasn't sent your device the keys to decrypt this message."
|
||||
|
||||
default:
|
||||
console.log(type + "\n" + JSON.stringify(dict, null, 4) + "\n")
|
||||
return "did something this client does not understand."
|
||||
|
||||
//case "CallEvent": TODO
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function getHistoryVisibilityEventText(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 the point they joined."
|
||||
break
|
||||
|
||||
case "invited":
|
||||
var end = "all room members, since the point they were invited."
|
||||
break
|
||||
}
|
||||
|
||||
return "made future history visible to " + end
|
||||
}
|
||||
|
||||
|
||||
function getStateDisplayName(dict) {
|
||||
// The dict.content.displayname may be outdated, prefer
|
||||
// retrieving it fresh
|
||||
var name = Backend.getUserDisplayName(dict.state_key, false)
|
||||
return name === dict.state_key ?
|
||||
dict.content.displayname : name.result()
|
||||
}
|
||||
|
||||
|
||||
function getMemberEventText(dict) {
|
||||
var info = dict.content, prev = dict.prev_content
|
||||
|
||||
if (! prev || (info.membership != prev.membership)) {
|
||||
var reason = info.reason ? (" Reason: " + info.reason) : ""
|
||||
|
||||
switch (info.membership) {
|
||||
case "join":
|
||||
return prev && prev.membership === "invite" ?
|
||||
"accepted the invitation." : "joined the room."
|
||||
break
|
||||
|
||||
case "invite":
|
||||
return "invited " + getStateDisplayName(dict) + " to the room."
|
||||
break
|
||||
|
||||
case "leave":
|
||||
if (dict.state_key === dict.sender) {
|
||||
return (prev && prev.membership === "invite" ?
|
||||
"declined the invitation." : "left the room.") +
|
||||
reason
|
||||
}
|
||||
|
||||
var name = getStateDisplayName(dict)
|
||||
return (prev && prev.membership === "invite" ?
|
||||
"withdrew " + name + "'s invitation." :
|
||||
|
||||
prev && prev.membership == "ban" ?
|
||||
"unbanned " + name + " from the room." :
|
||||
|
||||
"kicked out " + name + " from the room.") +
|
||||
reason
|
||||
break
|
||||
|
||||
case "ban":
|
||||
var name = getStateDisplayName(dict)
|
||||
return "banned " + name + " from the room." + reason
|
||||
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 ""
|
||||
}
|
||||
|
||||
|
||||
function getLeftBannerText(leftEvent) {
|
||||
if (! leftEvent) {
|
||||
return "You are not member of this room."
|
||||
}
|
||||
|
||||
var info = leftEvent.content
|
||||
var prev = leftEvent.prev_content
|
||||
var reason = info.reason ? (" Reason: " + info.reason) : ""
|
||||
|
||||
if (leftEvent.state_key === leftEvent.sender) {
|
||||
return (prev && prev.membership === "invite" ?
|
||||
"You declined to join the room." : "You left the room.") +
|
||||
reason
|
||||
}
|
||||
|
||||
if (info.membership)
|
||||
|
||||
var name = Backend.getUserDisplayName(leftEvent.sender, false).result()
|
||||
|
||||
return "<b>" + name + "</b> " +
|
||||
(info.membership == "ban" ?
|
||||
"banned you from the room." :
|
||||
|
||||
prev && prev.membership === "invite" ?
|
||||
"canceled your invitation." :
|
||||
|
||||
prev && prev.membership == "ban" ?
|
||||
"unbanned you from the room." :
|
||||
|
||||
"kicked you out of the room.") +
|
||||
reason
|
||||
}
|
||||
|
||||
|
||||
function getLeftBannerAvatarName(leftEvent, accountId) {
|
||||
if (! leftEvent || leftEvent.state_key == leftEvent.sender) {
|
||||
return Backend.getUserDisplayName(accountId, false).result()
|
||||
}
|
||||
|
||||
return Backend.getUserDisplayName(leftEvent.sender, false).result()
|
||||
}
|
||||
|
||||
|
||||
function getTypingUsersText(users, ourAccountId) {
|
||||
var names = []
|
||||
|
||||
for (var i = 0; i < users.length; i++) {
|
||||
if (users[i] !== ourAccountId) {
|
||||
names.push(Backend.getUserDisplayName(users[i], false).result())
|
||||
}
|
||||
}
|
||||
|
||||
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…"
|
||||
}
|
Reference in New Issue
Block a user