Rebrand files names Spectral -> NeoChat
This commit is contained in:
50
imports/NeoChat/Dialog/AcceptInvitationDialog.qml
Normal file
50
imports/NeoChat/Dialog/AcceptInvitationDialog.qml
Normal file
@@ -0,0 +1,50 @@
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.12
|
||||
|
||||
Dialog {
|
||||
property var room
|
||||
|
||||
anchors.centerIn: parent
|
||||
width: 360
|
||||
|
||||
id: root
|
||||
|
||||
title: "Invitation Received"
|
||||
modal: true
|
||||
|
||||
contentItem: Label {
|
||||
text: "Accept this invitation?"
|
||||
}
|
||||
|
||||
footer: DialogButtonBox {
|
||||
Button {
|
||||
text: "Accept"
|
||||
flat: true
|
||||
|
||||
onClicked: {
|
||||
room.acceptInvitation()
|
||||
close()
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "Reject"
|
||||
flat: true
|
||||
|
||||
onClicked: {
|
||||
room.forget()
|
||||
close()
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "Cancel"
|
||||
flat: true
|
||||
|
||||
onClicked: close()
|
||||
}
|
||||
}
|
||||
|
||||
onClosed: destroy()
|
||||
}
|
||||
|
||||
350
imports/NeoChat/Dialog/AccountDetailDialog.qml
Normal file
350
imports/NeoChat/Dialog/AccountDetailDialog.qml
Normal file
@@ -0,0 +1,350 @@
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.12
|
||||
import QtQuick.Layouts 1.12
|
||||
import org.kde.kirigami 2.13 as Kirigami
|
||||
|
||||
import NeoChat.Component 2.0
|
||||
import NeoChat.Effect 2.0
|
||||
|
||||
import org.kde.neochat 0.1
|
||||
import NeoChat.Setting 0.1
|
||||
|
||||
Dialog {
|
||||
anchors.centerIn: parent
|
||||
|
||||
width: 480
|
||||
|
||||
id: root
|
||||
|
||||
contentItem: Column {
|
||||
id: detailColumn
|
||||
|
||||
spacing: 0
|
||||
|
||||
ListView {
|
||||
width: parent.width
|
||||
height: 48
|
||||
|
||||
clip: true
|
||||
|
||||
orientation: ListView.Horizontal
|
||||
|
||||
spacing: 16
|
||||
|
||||
model: AccountListModel{ }
|
||||
|
||||
delegate: Kirigami.Avatar {
|
||||
width: 48
|
||||
height: 48
|
||||
|
||||
source: urser.avatarMediaId ? "image://mxc/" + user.avatarMediaId : ""
|
||||
name: user.displayName ?? ""
|
||||
|
||||
Menu {
|
||||
id: contextMenu
|
||||
|
||||
MenuItem {
|
||||
text: "Mark all as read"
|
||||
|
||||
onClicked: Controller.markAllMessagesAsRead(connection)
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
text: "Logout"
|
||||
|
||||
onClicked: Controller.logout(connection)
|
||||
}
|
||||
}
|
||||
|
||||
RippleEffect {
|
||||
anchors.fill: parent
|
||||
|
||||
circular: true
|
||||
|
||||
onPrimaryClicked: Controller.connection = connection
|
||||
onSecondaryClicked: contextMenu.popup()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
width: parent.width
|
||||
|
||||
MenuSeparator {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
ToolButton {
|
||||
Layout.preferredWidth: 48
|
||||
Layout.preferredHeight: 48
|
||||
|
||||
contentItem: MaterialIcon {
|
||||
icon: "\ue145"
|
||||
color: MPalette.lighter
|
||||
}
|
||||
|
||||
onClicked: loginDialog.createObject(ApplicationWindow.overlay).open()
|
||||
}
|
||||
}
|
||||
|
||||
Control {
|
||||
width: parent.width
|
||||
|
||||
contentItem: RowLayout {
|
||||
MaterialIcon {
|
||||
Layout.preferredWidth: 48
|
||||
Layout.preferredHeight: 48
|
||||
|
||||
color: MPalette.foreground
|
||||
icon: "\ue5d2"
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
|
||||
color: MPalette.foreground
|
||||
text: "Explore Rooms"
|
||||
}
|
||||
}
|
||||
|
||||
RippleEffect {
|
||||
anchors.fill: parent
|
||||
|
||||
onPrimaryClicked: {
|
||||
joinRoomDialog.createObject(ApplicationWindow.overlay, {"connection": Controller.connection}).open()
|
||||
root.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Control {
|
||||
width: parent.width
|
||||
|
||||
contentItem: RowLayout {
|
||||
MaterialIcon {
|
||||
Layout.preferredWidth: 48
|
||||
Layout.preferredHeight: 48
|
||||
|
||||
color: MPalette.foreground
|
||||
icon: "\ue7ff"
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
|
||||
color: MPalette.foreground
|
||||
text: "Start a Chat"
|
||||
}
|
||||
}
|
||||
|
||||
RippleEffect {
|
||||
anchors.fill: parent
|
||||
|
||||
onPrimaryClicked: {
|
||||
startChatDialog.createObject(ApplicationWindow.overlay, {"connection": Controller.connection}).open()
|
||||
root.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Control {
|
||||
width: parent.width
|
||||
|
||||
contentItem: RowLayout {
|
||||
MaterialIcon {
|
||||
Layout.preferredWidth: 48
|
||||
Layout.preferredHeight: 48
|
||||
|
||||
color: MPalette.foreground
|
||||
icon: "\ue7fc"
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
|
||||
color: MPalette.foreground
|
||||
text: "Create a Room"
|
||||
}
|
||||
}
|
||||
|
||||
RippleEffect {
|
||||
anchors.fill: parent
|
||||
|
||||
onPrimaryClicked: createRoomDialog.createObject(ApplicationWindow.overlay).open()
|
||||
}
|
||||
}
|
||||
|
||||
MenuSeparator {
|
||||
width: parent.width
|
||||
}
|
||||
|
||||
Control {
|
||||
width: parent.width
|
||||
|
||||
contentItem: RowLayout {
|
||||
MaterialIcon {
|
||||
Layout.preferredWidth: 48
|
||||
Layout.preferredHeight: 48
|
||||
|
||||
color: MPalette.foreground
|
||||
icon: "\ue3a9"
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
|
||||
color: MPalette.foreground
|
||||
text: "Night Mode"
|
||||
}
|
||||
|
||||
Switch {
|
||||
id: darkThemeSwitch
|
||||
|
||||
checked: MSettings.darkTheme
|
||||
onCheckedChanged: MSettings.darkTheme = checked
|
||||
}
|
||||
}
|
||||
|
||||
RippleEffect {
|
||||
anchors.fill: parent
|
||||
|
||||
onPrimaryClicked: darkThemeSwitch.checked = !darkThemeSwitch.checked
|
||||
}
|
||||
}
|
||||
|
||||
Control {
|
||||
width: parent.width
|
||||
|
||||
contentItem: RowLayout {
|
||||
MaterialIcon {
|
||||
Layout.preferredWidth: 48
|
||||
Layout.preferredHeight: 48
|
||||
|
||||
color: MPalette.foreground
|
||||
icon: "\ue8f8"
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
|
||||
color: MPalette.foreground
|
||||
text: "Show Join/Leave"
|
||||
}
|
||||
|
||||
Switch {
|
||||
id: showJoinLeaveSwitch
|
||||
|
||||
checked: MSettings.value("UI/show_joinleave", true)
|
||||
onCheckedChanged: MSettings.setValue("UI/show_joinleave", checked)
|
||||
}
|
||||
}
|
||||
|
||||
RippleEffect {
|
||||
anchors.fill: parent
|
||||
|
||||
onPrimaryClicked: showJoinLeaveSwitch.checked = !showJoinLeaveSwitch.checked
|
||||
}
|
||||
}
|
||||
|
||||
Control {
|
||||
width: parent.width
|
||||
|
||||
contentItem: RowLayout {
|
||||
MaterialIcon {
|
||||
Layout.preferredWidth: 48
|
||||
Layout.preferredHeight: 48
|
||||
|
||||
color: MPalette.foreground
|
||||
icon: "\ue5d2"
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
|
||||
color: MPalette.foreground
|
||||
text: "Enable System Tray"
|
||||
}
|
||||
|
||||
Switch {
|
||||
id: trayIconSwitch
|
||||
|
||||
checked: MSettings.showTray
|
||||
onCheckedChanged: MSettings.showTray = checked
|
||||
}
|
||||
}
|
||||
|
||||
RippleEffect {
|
||||
anchors.fill: parent
|
||||
|
||||
onPrimaryClicked: trayIconSwitch.checked = !trayIconSwitch.checked
|
||||
}
|
||||
}
|
||||
|
||||
Control {
|
||||
width: parent.width
|
||||
|
||||
contentItem: RowLayout {
|
||||
MaterialIcon {
|
||||
Layout.preferredWidth: 48
|
||||
Layout.preferredHeight: 48
|
||||
|
||||
color: MPalette.foreground
|
||||
icon: "\ue7f5"
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
|
||||
color: MPalette.foreground
|
||||
text: "Enable Notifications"
|
||||
}
|
||||
|
||||
Switch {
|
||||
id: notificationsSwitch
|
||||
|
||||
checked: MSettings.showNotification
|
||||
onCheckedChanged: MSettings.showNotification = checked
|
||||
}
|
||||
}
|
||||
|
||||
RippleEffect {
|
||||
anchors.fill: parent
|
||||
|
||||
onPrimaryClicked: notificationsSwitch.checked = !notificationsSwitch.checked
|
||||
}
|
||||
}
|
||||
|
||||
MenuSeparator {
|
||||
width: parent.width
|
||||
}
|
||||
|
||||
Control {
|
||||
width: parent.width
|
||||
|
||||
contentItem: RowLayout {
|
||||
MaterialIcon {
|
||||
Layout.preferredWidth: 48
|
||||
Layout.preferredHeight: 48
|
||||
|
||||
color: MPalette.foreground
|
||||
icon: "\ue167"
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
|
||||
color: MPalette.foreground
|
||||
text: "Font Family"
|
||||
}
|
||||
}
|
||||
|
||||
RippleEffect {
|
||||
anchors.fill: parent
|
||||
|
||||
onPrimaryClicked: fontFamilyDialog.createObject(ApplicationWindow.overlay).open()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onClosed: destroy()
|
||||
}
|
||||
40
imports/NeoChat/Dialog/CreateRoomDialog.qml
Normal file
40
imports/NeoChat/Dialog/CreateRoomDialog.qml
Normal file
@@ -0,0 +1,40 @@
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.12
|
||||
import QtQuick.Layouts 1.12
|
||||
|
||||
import NeoChat.Component 2.0
|
||||
|
||||
import org.kde.neochat 0.1
|
||||
|
||||
Dialog {
|
||||
anchors.centerIn: parent
|
||||
width: 360
|
||||
|
||||
id: root
|
||||
|
||||
title: "Create a Room"
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
AutoTextField {
|
||||
Layout.fillWidth: true
|
||||
|
||||
id: roomNameField
|
||||
|
||||
placeholderText: "Room Name"
|
||||
}
|
||||
|
||||
AutoTextField {
|
||||
Layout.fillWidth: true
|
||||
|
||||
id: roomTopicField
|
||||
|
||||
placeholderText: "Room Topic"
|
||||
}
|
||||
}
|
||||
|
||||
standardButtons: Dialog.Ok | Dialog.Cancel
|
||||
|
||||
onAccepted: Controller.createRoom(Controller.connection, roomNameField.text, roomTopicField.text)
|
||||
|
||||
onClosed: destroy()
|
||||
}
|
||||
30
imports/NeoChat/Dialog/FontFamilyDialog.qml
Normal file
30
imports/NeoChat/Dialog/FontFamilyDialog.qml
Normal file
@@ -0,0 +1,30 @@
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.12
|
||||
import QtQuick.Layouts 1.12
|
||||
|
||||
import NeoChat.Component 2.0
|
||||
import NeoChat.Setting 0.1
|
||||
|
||||
Dialog {
|
||||
anchors.centerIn: parent
|
||||
width: 360
|
||||
|
||||
id: root
|
||||
|
||||
title: "Enter Font Family"
|
||||
|
||||
contentItem: AutoTextField {
|
||||
Layout.fillWidth: true
|
||||
|
||||
id:fontFamilyField
|
||||
|
||||
text: MSettings.fontFamily
|
||||
placeholderText: "Font Family"
|
||||
}
|
||||
|
||||
standardButtons: Dialog.Ok | Dialog.Cancel
|
||||
|
||||
onAccepted: MSettings.fontFamily = fontFamilyField.text
|
||||
|
||||
onClosed: destroy()
|
||||
}
|
||||
170
imports/NeoChat/Dialog/InviteUserDialog.qml
Normal file
170
imports/NeoChat/Dialog/InviteUserDialog.qml
Normal file
@@ -0,0 +1,170 @@
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.12
|
||||
import QtQuick.Layouts 1.12
|
||||
import org.kde.kirigami 2.13 as Kirigami
|
||||
|
||||
import NeoChat.Component 2.0
|
||||
import NeoChat.Effect 2.0
|
||||
import NeoChat.Setting 0.1
|
||||
|
||||
import org.kde.neochat 0.1
|
||||
|
||||
Dialog {
|
||||
property var room
|
||||
|
||||
anchors.centerIn: parent
|
||||
width: 360
|
||||
height: Math.min(window.height - 100, 640)
|
||||
|
||||
id: root
|
||||
|
||||
title: "Invite a User"
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
spacing: 0
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
AutoTextField {
|
||||
property bool isUserID: text.match(/@(.+):(.+)/g)
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
id: identifierField
|
||||
|
||||
placeholderText: "Find a user..."
|
||||
|
||||
onAccepted: {
|
||||
userDictListModel.search()
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
visible: identifierField.isUserID
|
||||
|
||||
text: "Add"
|
||||
highlighted: true
|
||||
|
||||
onClicked: {
|
||||
room.inviteToRoom(identifierField.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MenuSeparator {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
AutoListView {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
id: userDictListView
|
||||
|
||||
clip: true
|
||||
|
||||
spacing: 4
|
||||
|
||||
model: UserDirectoryListModel {
|
||||
id: userDictListModel
|
||||
|
||||
connection: root.room.connection
|
||||
keyword: identifierField.text
|
||||
}
|
||||
|
||||
delegate: Control {
|
||||
property bool inRoom: room && room.containsUser(userID)
|
||||
|
||||
width: userDictListView.width
|
||||
height: 48
|
||||
|
||||
id: delegate
|
||||
|
||||
padding: 8
|
||||
|
||||
contentItem: RowLayout {
|
||||
spacing: 8
|
||||
|
||||
Kirigami.Avatar {
|
||||
Layout.preferredWidth: height
|
||||
Layout.fillHeight: true
|
||||
|
||||
source: author.avatarMediaId ? "image://mxc/" + author.avatarMediaId : ""
|
||||
name: name
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
spacing: 0
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
text: name
|
||||
color: MPalette.foreground
|
||||
font.pixelSize: 13
|
||||
textFormat: Text.PlainText
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.NoWrap
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
text: userID
|
||||
color: MPalette.lighter
|
||||
font.pixelSize: 10
|
||||
textFormat: Text.PlainText
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.NoWrap
|
||||
}
|
||||
}
|
||||
|
||||
Control {
|
||||
Layout.preferredWidth: 32
|
||||
Layout.preferredHeight: 32
|
||||
|
||||
visible: inRoom
|
||||
|
||||
background: RippleEffect {
|
||||
circular: true
|
||||
}
|
||||
}
|
||||
|
||||
Control {
|
||||
Layout.preferredWidth: 32
|
||||
Layout.preferredHeight: 32
|
||||
|
||||
visible: !inRoom
|
||||
|
||||
background: RippleEffect {
|
||||
circular: true
|
||||
|
||||
onClicked: {
|
||||
room.inviteToRoom(userID)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ScrollBar.vertical: ScrollBar {}
|
||||
|
||||
Label {
|
||||
anchors.centerIn: parent
|
||||
|
||||
visible: userDictListView.count < 1
|
||||
|
||||
text: "No users available"
|
||||
color: MPalette.foreground
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onClosed: destroy()
|
||||
}
|
||||
269
imports/NeoChat/Dialog/JoinRoomDialog.qml
Normal file
269
imports/NeoChat/Dialog/JoinRoomDialog.qml
Normal file
@@ -0,0 +1,269 @@
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.12
|
||||
import QtQuick.Layouts 1.12
|
||||
import org.kde.kirigami 2.13 as Kirigami
|
||||
|
||||
import NeoChat.Component 2.0
|
||||
import NeoChat.Effect 2.0
|
||||
import NeoChat.Setting 0.1
|
||||
|
||||
import org.kde.neochat 0.1
|
||||
|
||||
Dialog {
|
||||
property var connection
|
||||
|
||||
property string keyword
|
||||
property string server
|
||||
|
||||
anchors.centerIn: parent
|
||||
width: 480
|
||||
height: Math.min(window.height - 100, 800)
|
||||
|
||||
id: root
|
||||
|
||||
title: "Explore Rooms"
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
spacing: 0
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
AutoTextField {
|
||||
property bool isRoomAlias: text.match(/#(.+):(.+)/g)
|
||||
property var room: isRoomAlias ? connection.roomByAlias(text) : null
|
||||
property bool isJoined: room != null
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
id: identifierField
|
||||
|
||||
placeholderText: "Find a room..."
|
||||
|
||||
onEditingFinished: {
|
||||
keyword = text
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
id: joinButton
|
||||
|
||||
visible: identifierField.isRoomAlias
|
||||
|
||||
text: identifierField.isJoined ? "View" : "Join"
|
||||
highlighted: true
|
||||
flat: identifierField.isJoined
|
||||
|
||||
onClicked: {
|
||||
if (identifierField.isJoined) {
|
||||
roomListForm.joinRoom(identifierField.room)
|
||||
} else {
|
||||
Controller.joinRoom(connection, identifierField.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
Layout.maximumWidth: 120
|
||||
|
||||
id: serverField
|
||||
|
||||
editable: currentIndex == 1
|
||||
|
||||
model: ["Local", "Global", "matrix.org"]
|
||||
|
||||
onCurrentIndexChanged: {
|
||||
if (currentIndex == 0) {
|
||||
server = ""
|
||||
} else if (currentIndex == 2) {
|
||||
server = "matrix.org"
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onReturnPressed: {
|
||||
if (currentIndex == 1) {
|
||||
server = editText
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MenuSeparator {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
AutoListView {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
id: publicRoomsListView
|
||||
|
||||
clip: true
|
||||
|
||||
spacing: 4
|
||||
|
||||
model: PublicRoomListModel {
|
||||
id: publicRoomListModel
|
||||
|
||||
connection: root.connection
|
||||
server: root.server
|
||||
keyword: root.keyword
|
||||
}
|
||||
|
||||
delegate: Control {
|
||||
width: publicRoomsListView.width
|
||||
height: 48
|
||||
|
||||
padding: 8
|
||||
|
||||
contentItem: RowLayout {
|
||||
spacing: 8
|
||||
|
||||
Kirigami.Avatar {
|
||||
Layout.preferredWidth: height
|
||||
Layout.fillHeight: true
|
||||
|
||||
source: model.avatarMediaId ? "image://mxc/" + model.avatarMediaId : ""
|
||||
hint: name
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
spacing: 0
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
spacing: 4
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
text: name
|
||||
color: MPalette.foreground
|
||||
font.pixelSize: 13
|
||||
textFormat: Text.PlainText
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.NoWrap
|
||||
}
|
||||
|
||||
Label {
|
||||
visible: allowGuests
|
||||
|
||||
text: "GUESTS CAN JOIN"
|
||||
color: MPalette.lighter
|
||||
font.pixelSize: 10
|
||||
padding: 4
|
||||
|
||||
background: Rectangle {
|
||||
color: MPalette.banner
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
visible: worldReadable
|
||||
|
||||
text: "WORLD READABLE"
|
||||
color: MPalette.lighter
|
||||
font.pixelSize: 10
|
||||
padding: 4
|
||||
|
||||
background: Rectangle {
|
||||
color: MPalette.banner
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
visible: text
|
||||
|
||||
text: topic ? topic.replace(/(\r\n\t|\n|\r\t)/gm," ") : ""
|
||||
color: MPalette.lighter
|
||||
font.pixelSize: 10
|
||||
textFormat: Text.PlainText
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.NoWrap
|
||||
}
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
Layout.preferredWidth: 16
|
||||
Layout.preferredHeight: 16
|
||||
|
||||
icon: "\ue7fc"
|
||||
color: MPalette.lighter
|
||||
font.pixelSize: 16
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.preferredWidth: 36
|
||||
|
||||
text: memberCount
|
||||
color: MPalette.lighter
|
||||
font.pixelSize: 12
|
||||
}
|
||||
|
||||
Control {
|
||||
Layout.preferredWidth: 32
|
||||
Layout.preferredHeight: 32
|
||||
|
||||
visible: isJoined
|
||||
|
||||
contentItem: MaterialIcon {
|
||||
icon: "\ue89e"
|
||||
color: MPalette.lighter
|
||||
font.pixelSize: 20
|
||||
}
|
||||
|
||||
background: RippleEffect {
|
||||
circular: true
|
||||
|
||||
onClicked: {
|
||||
roomListForm.joinRoom(connection.room(roomID))
|
||||
root.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Control {
|
||||
Layout.preferredWidth: 32
|
||||
Layout.preferredHeight: 32
|
||||
|
||||
visible: !isJoined
|
||||
|
||||
contentItem: MaterialIcon {
|
||||
icon: "\ue7f0"
|
||||
color: MPalette.lighter
|
||||
font.pixelSize: 20
|
||||
}
|
||||
|
||||
background: RippleEffect {
|
||||
circular: true
|
||||
|
||||
onClicked: {
|
||||
Controller.joinRoom(connection, roomID)
|
||||
root.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ScrollBar.vertical: ScrollBar {}
|
||||
|
||||
onContentYChanged: {
|
||||
if(publicRoomListModel.hasMore && contentHeight - contentY < publicRoomsListView.height + 200)
|
||||
publicRoomListModel.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onClosed: destroy()
|
||||
}
|
||||
27
imports/NeoChat/Dialog/MessageSourceDialog.qml
Normal file
27
imports/NeoChat/Dialog/MessageSourceDialog.qml
Normal file
@@ -0,0 +1,27 @@
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.12
|
||||
|
||||
Popup {
|
||||
property string sourceText
|
||||
|
||||
anchors.centerIn: parent
|
||||
width: 480
|
||||
|
||||
id: root
|
||||
|
||||
modal: true
|
||||
padding: 16
|
||||
|
||||
closePolicy: Dialog.CloseOnEscape | Dialog.CloseOnPressOutside
|
||||
|
||||
contentItem: ScrollView {
|
||||
clip: true
|
||||
|
||||
Label {
|
||||
text: sourceText
|
||||
}
|
||||
}
|
||||
|
||||
onClosed: destroy()
|
||||
}
|
||||
|
||||
12
imports/NeoChat/Dialog/OpenFileDialog.qml
Normal file
12
imports/NeoChat/Dialog/OpenFileDialog.qml
Normal file
@@ -0,0 +1,12 @@
|
||||
import QtQuick 2.12
|
||||
import Qt.labs.platform 1.1
|
||||
|
||||
FileDialog {
|
||||
signal chosen(string path)
|
||||
|
||||
id: root
|
||||
|
||||
title: "Please choose a file"
|
||||
|
||||
onAccepted: chosen(file)
|
||||
}
|
||||
12
imports/NeoChat/Dialog/OpenFolderDialog.qml
Normal file
12
imports/NeoChat/Dialog/OpenFolderDialog.qml
Normal file
@@ -0,0 +1,12 @@
|
||||
import QtQuick 2.12
|
||||
import Qt.labs.platform 1.1
|
||||
|
||||
FolderDialog {
|
||||
signal chosen(string path)
|
||||
|
||||
id: root
|
||||
|
||||
title: "Please choose a folder"
|
||||
|
||||
onAccepted: chosen(folder)
|
||||
}
|
||||
298
imports/NeoChat/Dialog/RoomSettingsDialog.qml
Normal file
298
imports/NeoChat/Dialog/RoomSettingsDialog.qml
Normal file
@@ -0,0 +1,298 @@
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.12
|
||||
import QtQuick.Layouts 1.12
|
||||
import org.kde.kirigami 2.13 as Kirigami
|
||||
|
||||
import NeoChat.Component 2.0
|
||||
import NeoChat.Effect 2.0
|
||||
import NeoChat.Setting 0.1
|
||||
|
||||
import org.kde.neochat 0.1
|
||||
|
||||
Dialog {
|
||||
property var room
|
||||
|
||||
readonly property bool canChangeAvatar: room.canSendState("m.room.avatar")
|
||||
readonly property bool canChangeName: room.canSendState("m.room.name")
|
||||
readonly property bool canChangeTopic: room.canSendState("m.room.topic")
|
||||
readonly property bool canChangeCanonicalAlias: room.canSendState("m.room.canonical_alias")
|
||||
|
||||
anchors.centerIn: parent
|
||||
|
||||
width: 480
|
||||
height: window.height * 0.9
|
||||
|
||||
id: root
|
||||
|
||||
title: "Room Settings - " + room.displayName
|
||||
modal: true
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
spacing: 16
|
||||
|
||||
Kirigami.Avatar {
|
||||
Layout.preferredWidth: 72
|
||||
Layout.preferredHeight: 72
|
||||
Layout.alignment: Qt.AlignTop
|
||||
|
||||
name: room.displayName
|
||||
source: room.avatarMediaId ? "image://mxc/" + room.avatarMediaId : ""
|
||||
|
||||
RippleEffect {
|
||||
anchors.fill: parent
|
||||
|
||||
circular: true
|
||||
|
||||
enabled: canChangeAvatar
|
||||
|
||||
onClicked: {
|
||||
var fileDialog = openFileDialog.createObject(ApplicationWindow.overlay)
|
||||
|
||||
fileDialog.chosen.connect(function(path) {
|
||||
if (!path) return
|
||||
|
||||
room.changeAvatar(path)
|
||||
})
|
||||
|
||||
fileDialog.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: 4
|
||||
|
||||
AutoTextField {
|
||||
Layout.fillWidth: true
|
||||
|
||||
id: roomNameField
|
||||
|
||||
text: room.name
|
||||
placeholderText: "Room Name"
|
||||
|
||||
enabled: canChangeName
|
||||
}
|
||||
|
||||
AutoTextField {
|
||||
Layout.fillWidth: true
|
||||
|
||||
id: roomTopicField
|
||||
|
||||
text: room.topic
|
||||
placeholderText: "Room Topic"
|
||||
|
||||
enabled: canChangeTopic
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
|
||||
visible: canChangeName || canChangeTopic
|
||||
|
||||
text: "Save"
|
||||
highlighted: true
|
||||
|
||||
onClicked: {
|
||||
if (room.name != roomNameField.text) {
|
||||
room.setName(roomNameField.text)
|
||||
}
|
||||
|
||||
if (room.topic != roomTopicField.text) {
|
||||
room.setTopic(roomTopicField.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MenuSeparator {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
ScrollView {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
id: scrollview
|
||||
|
||||
clip: true
|
||||
|
||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||
|
||||
ColumnLayout {
|
||||
width: scrollview.width
|
||||
|
||||
Control {
|
||||
Layout.fillWidth: true
|
||||
|
||||
visible: room.predecessorId && room.connection.room(room.predecessorId)
|
||||
|
||||
padding: 8
|
||||
|
||||
contentItem: RowLayout {
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
spacing: 0
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
|
||||
font.bold: true
|
||||
color: MPalette.foreground
|
||||
text: "This room continues another conversation."
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
|
||||
color: MPalette.lighter
|
||||
text: "Click here to see older messages."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: MPalette.banner
|
||||
|
||||
RippleEffect {
|
||||
anchors.fill: parent
|
||||
|
||||
onClicked: {
|
||||
roomListForm.enteredRoom = Controller.connection.room(room.predecessorId)
|
||||
root.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Control {
|
||||
Layout.fillWidth: true
|
||||
|
||||
visible: room.successorId && room.connection.room(room.successorId)
|
||||
|
||||
padding: 8
|
||||
|
||||
contentItem: RowLayout {
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
spacing: 0
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
|
||||
font.bold: true
|
||||
color: MPalette.foreground
|
||||
text: "This room has been replaced."
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
|
||||
color: MPalette.lighter
|
||||
text: "The conversation continues here."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: MPalette.banner
|
||||
|
||||
RippleEffect {
|
||||
anchors.fill: parent
|
||||
|
||||
onClicked: {
|
||||
roomListForm.enteredRoom = Controller.connection.room(room.successorId)
|
||||
root.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
Label {
|
||||
Layout.preferredWidth: 100
|
||||
|
||||
wrapMode: Label.Wrap
|
||||
text: "Canonical Alias"
|
||||
color: MPalette.lighter
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
Layout.fillWidth: true
|
||||
|
||||
id: canonicalAliasComboBox
|
||||
|
||||
enabled: canChangeCanonicalAlias
|
||||
|
||||
model: room.aliases
|
||||
|
||||
currentIndex: room.aliases.indexOf(room.canonicalAlias)
|
||||
onCurrentIndexChanged: {
|
||||
if (room.canonicalAlias != room.aliases[currentIndex]) {
|
||||
room.setCanonicalAlias(room.aliases[currentIndex])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
visible: room.altAliases && room.altAliases.length
|
||||
|
||||
Label {
|
||||
Layout.preferredWidth: 100
|
||||
Layout.alignment: Qt.AlignTop
|
||||
|
||||
wrapMode: Label.Wrap
|
||||
text: "Alt Aliases"
|
||||
color: MPalette.lighter
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
spacing: 0
|
||||
|
||||
Repeater {
|
||||
model: room.altAliases
|
||||
|
||||
delegate: RowLayout {
|
||||
Layout.maximumWidth: parent.width
|
||||
|
||||
Label {
|
||||
text: modelData
|
||||
|
||||
font.pixelSize: 12
|
||||
color: MPalette.lighter
|
||||
}
|
||||
|
||||
ToolButton {
|
||||
icon.name: ""
|
||||
onClicked: room.removeLocalAlias(modelData)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: openFileDialog
|
||||
|
||||
OpenFileDialog {}
|
||||
}
|
||||
|
||||
onClosed: destroy()
|
||||
}
|
||||
|
||||
181
imports/NeoChat/Dialog/StartChatDialog.qml
Normal file
181
imports/NeoChat/Dialog/StartChatDialog.qml
Normal file
@@ -0,0 +1,181 @@
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.12
|
||||
import QtQuick.Layouts 1.12
|
||||
|
||||
import NeoChat.Component 2.0
|
||||
import NeoChat.Effect 2.0
|
||||
import NeoChat.Setting 0.1
|
||||
|
||||
import org.kde.neochat 0.1
|
||||
|
||||
Dialog {
|
||||
property var connection
|
||||
|
||||
anchors.centerIn: parent
|
||||
width: 360
|
||||
height: Math.min(window.height - 100, 640)
|
||||
|
||||
id: root
|
||||
|
||||
title: "Start a Chat"
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
spacing: 0
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
AutoTextField {
|
||||
property bool isUserID: text.match(/@(.+):(.+)/g)
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
id: identifierField
|
||||
|
||||
placeholderText: "Find a user..."
|
||||
|
||||
onAccepted: {
|
||||
userDictListModel.search()
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
visible: identifierField.isUserID
|
||||
|
||||
text: "Chat"
|
||||
highlighted: true
|
||||
|
||||
onClicked: {
|
||||
Controller.createDirectChat(connection, identifierField.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MenuSeparator {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
AutoListView {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
id: userDictListView
|
||||
|
||||
clip: true
|
||||
|
||||
spacing: 4
|
||||
|
||||
model: UserDirectoryListModel {
|
||||
id: userDictListModel
|
||||
|
||||
connection: root.connection
|
||||
keyword: identifierField.text
|
||||
}
|
||||
|
||||
delegate: Control {
|
||||
width: userDictListView.width
|
||||
height: 48
|
||||
|
||||
padding: 8
|
||||
|
||||
contentItem: RowLayout {
|
||||
spacing: 8
|
||||
|
||||
Kirigami.Avatar {
|
||||
Layout.preferredWidth: height
|
||||
Layout.fillHeight: true
|
||||
|
||||
source: avatar
|
||||
name: name
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
spacing: 0
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
text: name
|
||||
color: MPalette.foreground
|
||||
font.pixelSize: 13
|
||||
textFormat: Text.PlainText
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.NoWrap
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
text: userID
|
||||
color: MPalette.lighter
|
||||
font.pixelSize: 10
|
||||
textFormat: Text.PlainText
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.NoWrap
|
||||
}
|
||||
}
|
||||
|
||||
Control {
|
||||
Layout.preferredWidth: 32
|
||||
Layout.preferredHeight: 32
|
||||
|
||||
visible: directChats != null
|
||||
|
||||
contentItem: MaterialIcon {
|
||||
icon: "\ue89e"
|
||||
color: MPalette.lighter
|
||||
font.pixelSize: 20
|
||||
}
|
||||
|
||||
background: RippleEffect {
|
||||
circular: true
|
||||
|
||||
onClicked: {
|
||||
roomListForm.joinRoom(connection.room(directChats[0]))
|
||||
root.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Control {
|
||||
Layout.preferredWidth: 32
|
||||
Layout.preferredHeight: 32
|
||||
|
||||
contentItem: MaterialIcon {
|
||||
icon: "\ue7f0"
|
||||
color: MPalette.lighter
|
||||
font.pixelSize: 20
|
||||
}
|
||||
|
||||
background: RippleEffect {
|
||||
circular: true
|
||||
|
||||
onClicked: {
|
||||
Controller.createDirectChat(connection, userID)
|
||||
root.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ScrollBar.vertical: ScrollBar {}
|
||||
|
||||
Label {
|
||||
anchors.centerIn: parent
|
||||
|
||||
visible: userDictListView.count < 1
|
||||
|
||||
text: "No users available"
|
||||
color: MPalette.foreground
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onClosed: destroy()
|
||||
}
|
||||
160
imports/NeoChat/Dialog/UserDetailDialog.qml
Normal file
160
imports/NeoChat/Dialog/UserDetailDialog.qml
Normal file
@@ -0,0 +1,160 @@
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.12
|
||||
import QtQuick.Layouts 1.12
|
||||
import org.kde.kirigami 2.13 as Kirigami
|
||||
|
||||
import NeoChat.Component 2.0
|
||||
import NeoChat.Effect 2.0
|
||||
import NeoChat.Setting 0.1
|
||||
|
||||
Dialog {
|
||||
property var room
|
||||
property var user
|
||||
|
||||
property string displayName: user.displayName
|
||||
property string avatarMediaId: user.avatarMediaId
|
||||
property string avatarUrl: user.avatarUrl
|
||||
|
||||
anchors.centerIn: parent
|
||||
width: 360
|
||||
|
||||
id: root
|
||||
|
||||
modal: true
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
spacing: 16
|
||||
|
||||
Kirigami.Avatar {
|
||||
Layout.preferredWidth: 72
|
||||
Layout.preferredHeight: 72
|
||||
|
||||
name: displayName
|
||||
source: avatarMediaId ? "image://mxc/" + avatarMediaId : ""
|
||||
|
||||
RippleEffect {
|
||||
anchors.fill: parent
|
||||
|
||||
circular: true
|
||||
|
||||
onPrimaryClicked: {
|
||||
if (avatarMediaId) {
|
||||
fullScreenImage.createObject(parent, {"filename": displayName, "localPath": room.urlToMxcUrl(avatarUrl)}).showFullScreen()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
|
||||
font.pixelSize: 18
|
||||
font.bold: true
|
||||
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.NoWrap
|
||||
text: displayName
|
||||
color: MPalette.foreground
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: "Online"
|
||||
color: MPalette.lighter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MenuSeparator {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
spacing: 8
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.NoWrap
|
||||
text: user.id
|
||||
color: MPalette.accent
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
|
||||
wrapMode: Label.Wrap
|
||||
text: "User ID"
|
||||
color: MPalette.lighter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MenuSeparator {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Control {
|
||||
Layout.fillWidth: true
|
||||
|
||||
contentItem: RowLayout {
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
|
||||
wrapMode: Label.Wrap
|
||||
text: room.connection.isIgnored(user) ? "Unignore this user" : "Ignore this user"
|
||||
|
||||
color: MPalette.accent
|
||||
}
|
||||
}
|
||||
|
||||
background: RippleEffect {
|
||||
onPrimaryClicked: {
|
||||
root.close()
|
||||
room.connection.isIgnored(user) ? room.connection.removeFromIgnoredUsers(user) : room.connection.addToIgnoredUsers(user)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Control {
|
||||
Layout.fillWidth: true
|
||||
|
||||
contentItem: RowLayout {
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
|
||||
wrapMode: Label.Wrap
|
||||
text: "Kick this user"
|
||||
|
||||
color: MPalette.accent
|
||||
}
|
||||
}
|
||||
|
||||
background: RippleEffect {
|
||||
onPrimaryClicked: room.kickMember(user.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: fullScreenImage
|
||||
|
||||
FullScreenImage {}
|
||||
}
|
||||
|
||||
onClosed: destroy()
|
||||
}
|
||||
|
||||
15
imports/NeoChat/Dialog/qmldir
Normal file
15
imports/NeoChat/Dialog/qmldir
Normal file
@@ -0,0 +1,15 @@
|
||||
module NeoChat.Dialog
|
||||
RoomSettingsDialog 2.0 RoomSettingsDialog.qml
|
||||
UserDetailDialog 2.0 UserDetailDialog.qml
|
||||
MessageSourceDialog 2.0 MessageSourceDialog.qml
|
||||
LoginDialog 2.0 LoginDialog.qml
|
||||
CreateRoomDialog 2.0 CreateRoomDialog.qml
|
||||
JoinRoomDialog 2.0 JoinRoomDialog.qml
|
||||
InviteUserDialog 2.0 InviteUserDialog.qml
|
||||
AcceptInvitationDialog 2.0 AcceptInvitationDialog.qml
|
||||
FontFamilyDialog 2.0 FontFamilyDialog.qml
|
||||
AccountDetailDialog 2.0 AccountDetailDialog.qml
|
||||
OpenFileDialog 2.0 OpenFileDialog.qml
|
||||
OpenFolderDialog 2.0 OpenFolderDialog.qml
|
||||
ImageClipboardDialog 2.0 ImageClipboardDialog.qml
|
||||
StartChatDialog 2.0 StartChatDialog.qml
|
||||
Reference in New Issue
Block a user