Create new rooms module

This commit is contained in:
James Graham
2025-04-13 16:21:23 +01:00
parent 380a52d981
commit 0380de698c
36 changed files with 217 additions and 152 deletions

View File

@@ -1,131 +0,0 @@
// SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
import QtQuick
import QtQuick.Controls as QQC2
import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.delegates as Delegates
import org.kde.neochat
RowLayout {
id: root
property var desiredWidth
property bool collapsed: false
required property NeoChatConnection connection
signal search
/**
* @brief Emitted when the text is changed in the search field.
*/
signal textChanged(string newText)
Item {
Layout.preferredWidth: Kirigami.Units.largeSpacing
}
Kirigami.Heading {
Layout.fillWidth: true
visible: !root.collapsed
text: i18nc("@title", "Rooms")
}
Item {
Layout.fillWidth: true
visible: root.collapsed
}
QQC2.ToolButton {
id: searchButton
display: QQC2.AbstractButton.IconOnly
onClicked: root.search();
icon.name: "search"
text: i18nc("@action", "Search Rooms")
Shortcut {
sequence: "Ctrl+F"
onActivated: searchButton.clicked()
}
QQC2.ToolTip.visible: hovered
QQC2.ToolTip.text: text
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
}
QQC2.ToolButton {
id: menuButton
Accessible.role: Accessible.ButtonMenu
Accessible.onPressAction: menuButton.action.trigger()
display: QQC2.AbstractButton.IconOnly
checkable: true
action: QQC2.Action {
text: i18nc("@action:button", "Show Menu")
icon.name: "application-menu-symbolic"
onTriggered: {
const item = menu.createObject(menuButton);
item.closed.connect(menuButton.toggle);
item.open();
}
}
QQC2.ToolTip.visible: hovered
QQC2.ToolTip.text: text
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
}
Component {
id: menu
QQC2.Menu {
QQC2.MenuItem {
text: i18n("Find your friends")
icon.name: "list-add-user"
onTriggered: pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'UserSearchPage'), {
connection: root.connection
}, {
title: i18nc("@title", "Find your friends")
})
}
QQC2.MenuItem {
text: i18n("Create a Room")
icon.name: "system-users-symbolic"
action: QQC2.Action {
shortcut: StandardKey.New
onTriggered: {
pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'CreateRoomDialog'), {
connection: root.connection
}, {
title: i18nc("@title", "Create a Room")
});
}
}
}
QQC2.MenuItem {
text: i18n("Create a Space")
icon.name: "list-add"
onTriggered: {
pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'CreateRoomDialog'), {
connection: root.connection,
isSpace: true,
title: i18nc("@title", "Create a Space")
}, {
title: i18nc("@title", "Create a Space")
});
}
}
QQC2.MenuItem {
text: i18n("Scan a QR Code")
icon.name: "view-barcode-qr"
onTriggered: pageStack.pushDialogLayer(Qt.createComponent("org.kde.neochat", "QrScannerPage"), {
connection: root.connection
}, {
title: i18nc("@title", "Scan a QR Code")
})
}
}
}
}

View File

@@ -1,176 +0,0 @@
// SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
import QtQuick
import QtQuick.Controls as QQC2
import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.delegates as Delegates
import org.kde.neochat
Kirigami.NavigationTabBar {
id: root
/**
* @brief The connection for the current user.
*/
required property NeoChatConnection connection
/**
* @brief Emitted when the text is changed in the search field.
*/
signal textChanged(string newText)
Layout.fillWidth: true
actions: [
Kirigami.Action {
id: infoAction
text: i18n("Search")
icon.name: "search"
onTriggered: {
if (explorePopup.visible && explorePopupLoader.sourceComponent == search) {
explorePopup.close();
root.currentIndex = -1;
} else if (explorePopup.visible && explorePopupLoader.sourceComponent != search) {
explorePopup.close();
explorePopup.open();
} else {
explorePopup.open();
}
explorePopupLoader.switchComponent(search);
}
},
Kirigami.Action {
text: i18n("Explore rooms")
icon.name: "compass"
onTriggered: {
explorePopup.close();
let dialog = pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'ExploreRoomsPage'), {
connection: root.connection
}, {
title: i18nc("@title", "Explore Rooms")
});
dialog.roomSelected.connect((roomId, displayName, avatarUrl, alias, topic, memberCount, isJoined) => {
RoomManager.resolveResource(roomId.length > 0 ? roomId : alias, isJoined ? "" : "join");
});
root.currentIndex = -1;
}
},
Kirigami.Action {
text: i18n("Find your friends")
icon.name: "list-add-user"
onTriggered: {
explorePopup.close();
pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'UserSearchPage'), {
connection: root.connection
}, {
title: i18nc("@title", "Find your friends")
});
root.currentIndex = -1;
}
},
Kirigami.Action {
text: i18n("Create New")
icon.name: "list-add"
onTriggered: {
if (explorePopup.visible && explorePopupLoader.sourceComponent == create) {
explorePopup.close();
root.currentIndex = -1;
} else if (explorePopup.visible && explorePopupLoader.sourceComponent != create) {
explorePopup.close();
explorePopup.open();
} else {
explorePopup.open();
}
explorePopupLoader.switchComponent(create);
}
}
]
QQC2.Popup {
id: explorePopup
parent: root
y: -height + 1
width: root.width
leftPadding: Kirigami.Units.largeSpacing
rightPadding: Kirigami.Units.largeSpacing
bottomPadding: Kirigami.Units.largeSpacing
topPadding: Kirigami.Units.largeSpacing
closePolicy: QQC2.Popup.CloseOnEscape
contentItem: Loader {
id: explorePopupLoader
sourceComponent: search
function switchComponent(newComponent) {
if (sourceComponent == search) {
root.textChanged("");
}
sourceComponent = newComponent;
}
}
background: ColumnLayout {
spacing: 0
Kirigami.Separator {
Layout.fillWidth: true
}
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
color: Kirigami.Theme.backgroundColor
}
}
Component {
id: search
Kirigami.SearchField {
onTextChanged: root.textChanged(text)
}
}
Component {
id: create
ColumnLayout {
spacing: 0
Delegates.RoundedItemDelegate {
Layout.fillWidth: true
action: Kirigami.Action {
text: i18n("Create a Room")
icon.name: "system-users-symbolic"
onTriggered: {
pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'CreateRoomPage'), {
connection: root.connection
}, {
title: i18nc("@title", "Create a Room")
});
explorePopup.close();
}
shortcut: StandardKey.New
}
}
Delegates.RoundedItemDelegate {
Layout.fillWidth: true
action: Kirigami.Action {
text: i18n("Create a Space")
icon.name: "list-add"
onTriggered: {
pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'CreateRoomDialog'), {
connection: root.connection,
isSpace: true,
title: i18nc("@title", "Create a Space")
}, {
title: i18nc("@title", "Create a Space")
});
explorePopup.close();
}
}
}
}
}
}
}

View File

@@ -1,158 +0,0 @@
// SPDX-FileCopyrightText: 2019 Black Hat <bhat@encom.eu.org>
// SPDX-FileCopyrightText: 2020 Carl Schwan <carl@carlschwan.eu>
// SPDX-License-Identifier: GPL-3.0-only
import QtQuick
import QtQuick.Controls as QQC2
import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.components as KirigamiComponents
import org.kde.kirigamiaddons.delegates as Delegates
import org.kde.neochat
import org.kde.neochat.settings
/**
* Context menu when clicking on a room in the room list
*/
KirigamiComponents.ConvergentContextMenu {
id: root
property NeoChatRoom room
required property NeoChatConnection connection
headerContentItem: RowLayout {
id: headerLayout
Layout.fillWidth: true
spacing: Kirigami.Units.largeSpacing
KirigamiComponents.Avatar {
id: avatar
source: room.avatarMediaUrl
name: room.displayName
Layout.preferredWidth: Kirigami.Units.gridUnit + Kirigami.Units.largeSpacing * 2
Layout.preferredHeight: Kirigami.Units.gridUnit + Kirigami.Units.largeSpacing * 2
Layout.alignment: Qt.AlignTop
}
Kirigami.Heading {
level: 5
Layout.fillWidth: true
text: room.displayName
elide: Text.ElideRight
}
}
QQC2.Action {
text: i18n("Mark as Read")
icon.name: "checkmark"
enabled: room.notificationCount > 0
onTriggered: room.markAllMessagesAsRead()
}
Kirigami.Action {
separator: true
}
Kirigami.Action {
text: i18nc("@action:inmenu", "Notifications")
icon.name: "notifications"
Kirigami.Action {
text: i18n("Follow Global Setting")
icon.name: "globe"
checkable: true
autoExclusive: true
checked: room.pushNotificationState === PushNotificationState.Default
enabled: room.pushNotificationState != PushNotificationState.Unknown
onTriggered: {
room.pushNotificationState = PushNotificationState.Default;
}
}
Kirigami.Action {
text: i18nc("As in 'notify for all messages'", "All")
icon.name: "notifications"
checkable: true
autoExclusive: true
checked: room.pushNotificationState === PushNotificationState.All
enabled: room.pushNotificationState != PushNotificationState.Unknown
onTriggered: {
room.pushNotificationState = PushNotificationState.All;
}
}
Kirigami.Action {
text: i18nc("As in 'notify when the user is mentioned or the message contains a set keyword'", "@Mentions and Keywords")
icon.name: "im-user"
checkable: true
autoExclusive: true
checked: room.pushNotificationState === PushNotificationState.MentionKeyword
enabled: room.pushNotificationState != PushNotificationState.Unknown
onTriggered: {
room.pushNotificationState = PushNotificationState.MentionKeyword;
}
}
Kirigami.Action {
text: i18nc("As in 'do not notify for any messages'", "Off")
icon.name: "notifications-disabled"
checkable: true
autoExclusive: true
checked: room.pushNotificationState === PushNotificationState.Mute
enabled: room.pushNotificationState != PushNotificationState.Unknown
onTriggered: {
room.pushNotificationState = PushNotificationState.Mute;
}
}
}
QQC2.Action {
text: room.isFavourite ? i18n("Remove from Favorites") : i18n("Add to Favorites")
icon.name: room.isFavourite ? "rating" : "rating-unrated"
onTriggered: room.isFavourite ? room.removeTag("m.favourite") : room.addTag("m.favourite", 1.0)
}
QQC2.Action {
text: room.isLowPriority ? i18n("Reprioritize") : i18n("Deprioritize")
icon.name: room.isLowPriority ? "arrow-up-symbolic" : "arrow-down-symbolic"
onTriggered: room.isLowPriority ? room.removeTag("m.lowpriority") : room.addTag("m.lowpriority", 1.0)
}
Kirigami.Action {
separator: true
}
QQC2.Action {
text: room.isDirectChat() ? i18nc("@action:inmenu", "Copy user's Matrix ID to Clipboard") : i18nc("@action:inmenu", "Copy Address to Clipboard")
icon.name: "edit-copy"
onTriggered: if (room.isDirectChat()) {
Clipboard.saveText(room.directChatRemoteMember.id);
} else if (room.canonicalAlias.length === 0) {
Clipboard.saveText(room.id);
} else {
Clipboard.saveText(room.canonicalAlias);
}
}
QQC2.Action {
text: i18nc("@action:inmenu", "Room Settings")
icon.name: 'settings-configure-symbolic'
onTriggered: {
RoomSettingsView.openRoomSettings(root.room, RoomSettingsView.Room);
}
}
Kirigami.Action {
separator: true
}
QQC2.Action {
text: i18n("Leave Room")
icon.name: "go-previous"
onTriggered: {
Qt.createComponent('org.kde.neochat', 'ConfirmLeaveDialog').createObject(root.QQC2.ApplicationWindow.window, {
room: root.room
}).open();
}
}
}

View File

@@ -1,180 +0,0 @@
// SPDX-FileCopyrightText: 2023 Carl Schwan <carl@carlschwan.eu>
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick
import QtQuick.Controls as QQC2
import QtQuick.Layouts
import QtQml.Models
import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.delegates as Delegates
import org.kde.kirigamiaddons.labs.components as Components
import org.kde.kitemmodels
import org.kde.neochat
Delegates.RoundedItemDelegate {
id: root
required property int index
required property int contextNotificationCount
required property bool hasHighlightNotifications
required property NeoChatRoom currentRoom
required property NeoChatConnection connection
required property url avatar
required property string subtitleText
required property string displayName
property bool openOnClick: true
property bool showConfigure: true
property bool collapsed: false
readonly property bool hasNotifications: contextNotificationCount > 0
Accessible.name: root.displayName
Accessible.onPressAction: clicked()
onClicked: {
if (root.openOnClick) {
RoomManager.resolveResource(currentRoom.id);
pageStack.currentIndex = 1;
}
}
Keys.onSpacePressed: clicked()
Keys.onEnterPressed: clicked()
Keys.onReturnPressed: clicked()
TapHandler {
acceptedButtons: Qt.RightButton
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad | PointerDevice.Stylus
onTapped: (eventPoint, button) => root.createRoomListContextMenu()
}
TapHandler {
acceptedDevices: PointerDevice.TouchScreen
onLongPressed: root.createRoomListContextMenu()
}
contentItem: RowLayout {
spacing: Kirigami.Units.largeSpacing
AvatarNotification {
source: root.avatar
name: root.displayName
visible: NeoChatConfig.showAvatarInRoomDrawer
implicitHeight: Kirigami.Units.gridUnit + (NeoChatConfig.compactRoomList ? 0 : Kirigami.Units.largeSpacing * 2)
implicitWidth: visible ? implicitHeight : 0
notificationCount: root.contextNotificationCount
notificationHighlight: root.hasHighlightNotifications
showNotificationLabel: root.hasNotifications && root.collapsed
asynchronous: true
Layout.preferredWidth: height
}
ColumnLayout {
spacing: 0
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
visible: !root.collapsed
QQC2.Label {
id: label
text: root.displayName
elide: Text.ElideRight
font.weight: root.hasNotifications ? Font.Bold : Font.Normal
textFormat: Text.PlainText
Layout.fillWidth: true
Layout.alignment: subtitle.visible ? Qt.AlignLeft | Qt.AlignBottom : Qt.AlignLeft | Qt.AlignVCenter
}
QQC2.Label {
id: subtitle
text: root.subtitleText
elide: Text.ElideRight
font: Kirigami.Theme.smallFont
opacity: root.hasNotifications ? 0.9 : 0.7
visible: !NeoChatConfig.compactRoomList && text.length > 0
textFormat: Text.PlainText
Layout.fillWidth: true
Layout.alignment: visible ? Qt.AlignLeft | Qt.AlignTop : Qt.AlignLeft | Qt.AlignVCenter
}
}
Kirigami.Icon {
source: "notifications-disabled"
enabled: false
implicitWidth: Kirigami.Units.iconSizes.smallMedium
implicitHeight: Kirigami.Units.iconSizes.smallMedium
visible: currentRoom.pushNotificationState === PushNotificationState.Mute && !configButton.visible && !root.collapsed
Accessible.name: i18n("Muted room")
Layout.rightMargin: Kirigami.Units.smallSpacing
}
QQC2.Label {
id: notificationCountLabel
text: root.contextNotificationCount
visible: root.hasNotifications && !root.collapsed
color: Kirigami.Theme.textColor
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
background: Rectangle {
visible: root.hasNotifications
Kirigami.Theme.colorSet: Kirigami.Theme.Button
Kirigami.Theme.inherit: false
color: root.hasHighlightNotifications > 0 ? Kirigami.Theme.positiveTextColor : Kirigami.Theme.backgroundColor
radius: height / 2
}
Layout.rightMargin: Kirigami.Units.smallSpacing
Layout.minimumHeight: Kirigami.Units.iconSizes.smallMedium
Layout.minimumWidth: Math.max(notificationCountTextMetrics.advanceWidth + Kirigami.Units.smallSpacing * 2, height)
TextMetrics {
id: notificationCountTextMetrics
text: notificationCountLabel.text
}
}
QQC2.Button {
id: configButton
visible: root.hovered && !Kirigami.Settings.isMobile && !NeoChatConfig.compactRoomList && !root.collapsed && root.showConfigure
text: i18n("Configure room")
display: QQC2.Button.IconOnly
icon.name: "overflow-menu-symbolic"
onClicked: createRoomListContextMenu()
}
}
function createRoomListContextMenu(): void {
const component = Qt.createComponent('org.kde.neochat', 'RoomContextMenu');
if (component.status === Component.Error) {
console.error(component.errorString());
}
const menu = component.createObject(root.ListView.view ?? root.treeView, {
room: root.currentRoom,
connection: root.connection
});
if (!Kirigami.Settings.isMobile && !NeoChatConfig.compactRoomList) {
configButton.visible = true;
configButton.down = true;
}
menu.closed.connect(function () {
configButton.down = undefined;
configButton.visible = Qt.binding(() => {
return root.hovered && !Kirigami.Settings.isMobile && !NeoChatConfig.compactRoomList;
});
});
menu.popup();
}
}

View File

@@ -1,332 +0,0 @@
// SPDX-FileCopyrightText: 2019 Black Hat <bhat@encom.eu.org>
// SPDX-FileCopyrightText: 2020 Carl Schwan <carl@carlschwan.eu>
// SPDX-License-Identifier: GPL-3.0-only
import QtQuick
import QtQuick.Controls as QQC2
import QtQuick.Layouts
import QtQml.Models
import Qt.labs.qmlmodels
import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.components as KirigamiComponents
import org.kde.kirigamiaddons.delegates as Delegates
import org.kde.neochat
Kirigami.Page {
id: root
/**
* @brief The current width of the room list.
*
* @note Other objects can access the value but the private function makes sure
* that only the internal members can modify it.
*/
readonly property int currentWidth: _private.currentWidth + spaceDrawer.width + 1
required property NeoChatConnection connection
readonly property bool collapsed: NeoChatConfig.collapsed
signal search
onCurrentWidthChanged: pageStack.defaultColumnWidth = root.currentWidth
Component.onCompleted: pageStack.defaultColumnWidth = root.currentWidth
onCollapsedChanged: {
if (collapsed) {
RoomManager.sortFilterRoomTreeModel.filterText = "";
}
}
function goToNextRoomFiltered(condition) {
let index = treeView.rowAtIndex(RoomManager.sortFilterRoomTreeModel.currentRoomIndex());
while (index++ < treeView.rows) {
let item = treeView.itemAtIndex(treeView.index(index, 0))
if (condition(item)) {
RoomManager.resolveResource(item.currentRoom.id)
return;
}
}
}
function goToPreviousRoomFiltered(condition) {
let index = treeView.rowAtIndex(RoomManager.sortFilterRoomTreeModel.currentRoomIndex());
while (index-- > 0) {
let item = treeView.itemAtIndex(treeView.index(index, 0))
if (condition(item)) {
RoomManager.resolveResource(item.currentRoom.id)
return;
}
}
}
function goToNextRoom() {
goToNextRoomFiltered(item => (item && item instanceof RoomDelegate));
}
function goToPreviousRoom() {
goToPreviousRoomFiltered(item => (item && item instanceof RoomDelegate));
}
function goToNextUnreadRoom() {
goToNextRoomFiltered(item => (item && item instanceof RoomDelegate && item.hasUnread));
}
function goToPreviousUnreadRoom() {
goToPreviousRoomFiltered(item => (item && item instanceof RoomDelegate && item.hasUnread));
}
titleDelegate: Loader {
Layout.fillWidth: true
sourceComponent: Kirigami.Settings.isMobile ? userInfo : exploreComponent
}
padding: 0
Connections {
target: RoomManager
function onCurrentSpaceChanged() {
treeView.expandRecursively();
}
function onCurrentRoomChanged() {
treeView.positionViewAtIndex(RoomManager.sortFilterRoomTreeModel.currentRoomIndex(), TableView.AlignVCenter)
}
}
RowLayout {
anchors.fill: parent
spacing: 0
SpaceDrawer {
id: spaceDrawer
Layout.preferredWidth: Kirigami.Units.gridUnit * 3
Layout.fillHeight: true
connection: root.connection
}
Kirigami.Separator {
Layout.fillHeight: true
Layout.preferredWidth: 1
}
QQC2.ScrollView {
id: scrollView
Layout.fillWidth: true
Layout.fillHeight: true
background: Rectangle {
color: Kirigami.Theme.backgroundColor
Kirigami.Theme.colorSet: Kirigami.Theme.View
}
Keys.onDownPressed: ; // Do not delete 🫠
Keys.onUpPressed: ; // These make sure the scrollview doesn't also scroll while going through the roomlist using the arrow keys
contentItem: TreeView {
id: treeView
topMargin: Math.round(Kirigami.Units.smallSpacing / 2)
clip: true
reuseItems: false
model: RoomManager.sortFilterRoomTreeModel
selectionModel: ItemSelectionModel {}
delegate: DelegateChooser {
role: "delegateType"
DelegateChoice {
roleValue: "section"
delegate: RoomTreeSection {
collapsed: root.collapsed
}
}
DelegateChoice {
roleValue: "normal"
delegate: RoomDelegate {
id: roomDelegate
required property int row
required property TreeView treeView
required property bool current
onCurrentChanged: if (current) {
forceActiveFocus(Qt.TabFocusReason);
}
implicitWidth: treeView.width
connection: root.connection
collapsed: root.collapsed
highlighted: RoomManager.currentRoom === currentRoom
}
}
DelegateChoice {
roleValue: "addDirect"
delegate: Delegates.RoundedItemDelegate {
text: i18n("Find your friends")
icon.name: "list-add-user"
icon.width: Kirigami.Units.gridUnit * 2
icon.height: Kirigami.Units.gridUnit * 2
onClicked: pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'UserSearchPage'), {
connection: root.connection
}, {
title: i18nc("@title", "Find your friends")
})
}
}
}
}
}
}
Kirigami.PlaceholderMessage {
anchors.centerIn: parent
anchors.horizontalCenterOffset: (spaceDrawer.width + 1) / 2
width: scrollView.width - Kirigami.Units.largeSpacing * 4
visible: treeView.rows == 0
text: if (RoomManager.sortFilterRoomTreeModel.filterText.length > 0) {
return spaceDrawer.showDirectChats ? i18n("No friends found") : i18n("No rooms found");
} else {
return spaceDrawer.showDirectChats ? i18n("You haven't added any of your friends yet, click below to search for them.") : i18n("Join some rooms to get started");
}
helpfulAction: spaceDrawer.showDirectChats ? userSearchAction : exploreRoomAction
Kirigami.Action {
id: exploreRoomAction
icon.name: RoomManager.sortFilterRoomTreeModel.filterText.length > 0 ? "search" : "list-add"
text: RoomManager.sortFilterRoomTreeModel.filterText.length > 0 ? i18n("Search in room directory") : i18n("Explore rooms")
onTriggered: {
let dialog = pageStack.layers.push(Qt.createComponent('org.kde.neochat', 'ExploreRoomsPage'), {
connection: root.connection,
keyword: RoomManager.sortFilterRoomTreeModel.filterText
}, {
title: i18nc("@title", "Explore Rooms")
});
dialog.roomSelected.connect((roomId, displayName, avatarUrl, alias, topic, memberCount, isJoined) => {
RoomManager.resolveResource(roomId.length > 0 ? roomId : alias, isJoined ? "" : "join");
});
}
}
Kirigami.Action {
id: userSearchAction
icon.name: RoomManager.sortFilterRoomTreeModel.filterText.length > 0 ? "search" : "list-add"
text: RoomManager.sortFilterRoomTreeModel.filterText.length > 0 ? i18n("Search in friend directory") : i18n("Find your friends")
onTriggered: pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'UserSearchPage'), {
connection: root.connection
}, {
title: i18nc("@title", "Find your friends")
})
}
}
footer: Loader {
width: parent.width
sourceComponent: Kirigami.Settings.isMobile ? exploreComponentMobile : userInfoDesktop
}
MouseArea {
anchors.top: parent.top
anchors.bottom: parent.bottom
parent: applicationWindow().overlay.parent
x: root.currentWidth - width / 2
width: Kirigami.Units.smallSpacing * 2
z: root.z + 1
enabled: RoomManager.hasOpenRoom && applicationWindow().width >= Kirigami.Units.gridUnit * 35
visible: enabled
cursorShape: Qt.SplitHCursor
property int _lastX
onPressed: mouse => {
_lastX = mouse.x;
}
onPositionChanged: mouse => {
if (_lastX == -1) {
return;
}
if (mouse.x > _lastX) {
// we moved to the right
if (_private.currentWidth < _private.collapseWidth && _private.currentWidth + (mouse.x - _lastX) >= _private.collapseWidth) {
// Here we get back directly to a more wide mode.
_private.currentWidth = _private.defaultWidth;
NeoChatConfig.collapsed = false;
} else if (_private.currentWidth >= _private.collapseWidth) {
// Increase page width
_private.currentWidth = Math.min(_private.defaultWidth, _private.currentWidth + (mouse.x - _lastX));
}
} else if (mouse.x < _lastX) {
const tmpWidth = _private.currentWidth - (_lastX - mouse.x);
if (tmpWidth < _private.collapseWidth) {
_private.currentWidth = Qt.binding(() => _private.collapsedSize);
NeoChatConfig.collapsed = true;
} else {
_private.currentWidth = tmpWidth;
}
}
}
}
Component {
id: userInfo
UserInfo {
bottomEdge: false
connection: root.connection
}
}
Component {
id: userInfoDesktop
UserInfoDesktop {
connection: root.connection
collapsed: root.collapsed
}
}
Component {
id: exploreComponent
ExploreComponent {
desiredWidth: root.width - Kirigami.Units.largeSpacing
collapsed: root.collapsed
connection: root.connection
onSearch: root.search()
onTextChanged: newText => {
RoomManager.sortFilterRoomTreeModel.filterText = newText;
treeView.expandRecursively();
}
}
}
Component {
id: exploreComponentMobile
ExploreComponentMobile {
connection: root.connection
onTextChanged: newText => {
RoomManager.sortFilterRoomTreeModel.filterText = newText;
}
}
}
/*
* Hold the modifiable currentWidth in a private object so that only internal
* members can modify it.
*/
QtObject {
id: _private
property int currentWidth: NeoChatConfig.collapsed ? collapsedSize : defaultWidth
readonly property int defaultWidth: Kirigami.Units.gridUnit * 15
readonly property int collapseWidth: Kirigami.Units.gridUnit * 10
readonly property int collapsedSize: Kirigami.Units.gridUnit + (NeoChatConfig.compactRoomList ? 0 : Kirigami.Units.largeSpacing * 2) + Kirigami.Units.largeSpacing * 2 + (scrollView.QQC2.ScrollBar.vertical.visible ? scrollView.QQC2.ScrollBar.vertical.width : 0)
}
}

View File

@@ -1,83 +0,0 @@
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
import QtQuick
import QtQuick.Controls as QQC2
import QtQuick.Layouts
import org.kde.kirigami as Kirigami
QQC2.ItemDelegate {
id: root
required property TreeView treeView
required property bool isTreeNode
required property bool expanded
required property bool hasChildren
required property int depth
required property string displayName
required property int row
required property bool current
onCurrentChanged: if (current) {
collapseButton.forceActiveFocus(Qt.TabFocusReason);
}
required property bool selected
property bool collapsed: false
implicitWidth: treeView.width
hoverEnabled: false
activeFocusOnTab: false
background: null
onClicked: root.treeView.toggleExpanded(row)
Keys.onEnterPressed: root.treeView.toggleExpanded(row)
Keys.onReturnPressed: root.treeView.toggleExpanded(row)
Keys.onSpacePressed: root.treeView.toggleExpanded(row)
contentItem: RowLayout {
spacing: Kirigami.Units.largeSpacing
Kirigami.Heading {
Layout.alignment: Qt.AlignVCenter
visible: !root.collapsed
opacity: 0.7
level: 5
type: Kirigami.Heading.Primary
text: root.collapsed ? "" : model.displayName
elide: Text.ElideRight
// we override the Primary type's font weight (DemiBold) for Bold for contrast with small text
font.weight: Font.Bold
}
Kirigami.Separator {
Layout.fillWidth: true
visible: !root.collapsed
Layout.alignment: Qt.AlignVCenter
}
QQC2.ToolButton {
id: collapseButton
Layout.alignment: Qt.AlignHCenter
icon {
name: root.expanded ? "go-up" : "go-down"
width: Kirigami.Units.iconSizes.small
height: Kirigami.Units.iconSizes.small
}
text: root.expanded ? i18nc("Collapse <section name>", "Collapse %1", root.displayName) : i18nc("Expand <section name", "Expand %1", root.displayName)
display: QQC2.Button.IconOnly
activeFocusOnTab: false
QQC2.ToolTip.text: text
QQC2.ToolTip.visible: hovered
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
onClicked: root.treeView.toggleExpanded(row)
}
}
}

View File

@@ -1,320 +0,0 @@
// SPDX-FileCopyrightText: 2020-2023 Tobias Fella <tobias.fella@kde.org>
// SPDX-FileCopyrightText: 2021-2022 Bart De Vries <bart@mogwai.be>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
import QtQuick
import QtQuick.Controls as QQC2
import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import org.kde.neochat
QQC2.Control {
id: root
readonly property real pinnedWidth: Kirigami.Units.gridUnit * 6
required property NeoChatConnection connection
leftPadding: 0
rightPadding: 0
topPadding: 0
bottomPadding: 0
onActiveFocusChanged: if (activeFocus) {
notificationsButton.forceActiveFocus();
}
contentItem: ColumnLayout {
spacing: 0
QQC2.ScrollView {
id: scrollView
Layout.fillWidth: true
Layout.fillHeight: true
QQC2.ScrollBar.vertical.policy: QQC2.ScrollBar.AlwaysOff
QQC2.ScrollBar.horizontal.policy: QQC2.ScrollBar.AlwaysOff
contentWidth: -1 // disable horizontal scroll
background: Rectangle {
color: Kirigami.Theme.backgroundColor
Kirigami.Theme.colorSet: Kirigami.Theme.View
}
ColumnLayout {
id: column
width: scrollView.width
spacing: 0
AvatarTabButton {
id: notificationsButton
Layout.fillWidth: true
Layout.preferredHeight: width - Kirigami.Units.smallSpacing
Layout.maximumHeight: width - Kirigami.Units.smallSpacing
Layout.topMargin: Kirigami.Units.smallSpacing / 2
Layout.bottomMargin: Kirigami.Units.smallSpacing / 2
text: i18n("View notifications")
contentItem: Kirigami.Icon {
source: "notifications"
}
activeFocusOnTab: true
onSelected: pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'NotificationsView'), {
connection: root.connection
}, {
title: i18nc("@title", "Notifications"),
modality: Qt.NonModal
})
}
Kirigami.Separator {
Layout.fillWidth: true
Layout.leftMargin: Kirigami.Units.smallSpacing
Layout.rightMargin: Kirigami.Units.smallSpacing
}
AvatarTabButton {
id: allRoomButton
Layout.fillWidth: true
Layout.preferredHeight: width - Kirigami.Units.smallSpacing
Layout.maximumHeight: width - Kirigami.Units.smallSpacing
Layout.topMargin: Kirigami.Units.smallSpacing / 2
text: i18n("Home")
contentItem: Kirigami.Icon {
source: "user-home-symbolic"
QQC2.Label {
id: homeNotificationCountLabel
anchors.top: parent.top
anchors.right: parent.right
anchors.topMargin: -Kirigami.Units.smallSpacing
anchors.rightMargin: -Kirigami.Units.smallSpacing
z: 1
width: Math.max(homeNotificationCountTextMetrics.advanceWidth + Kirigami.Units.smallSpacing * 2, height)
height: Kirigami.Units.iconSizes.smallMedium
text: root.connection.homeNotifications > 0 ? root.connection.homeNotifications : ""
visible: root.connection.homeNotifications > 0 && (RoomManager.currentSpace.length > 0 || root.showDirectChats === true)
color: Kirigami.Theme.textColor
horizontalAlignment: Text.AlignHCenter
background: Rectangle {
visible: true
Kirigami.Theme.colorSet: Kirigami.Theme.Button
Kirigami.Theme.inherit: false
color: root.connection.homeHaveHighlightNotifications ? Kirigami.Theme.positiveTextColor : Kirigami.Theme.backgroundColor
radius: height / 2
}
TextMetrics {
id: homeNotificationCountTextMetrics
text: homeNotificationCountLabel.text
}
}
}
activeFocusOnTab: true
checked: RoomManager.currentSpace.length === 0
onSelected: RoomManager.currentSpace = ""
}
AvatarTabButton {
id: directChatButton
Layout.fillWidth: true
Layout.preferredHeight: width - Kirigami.Units.smallSpacing
Layout.maximumHeight: width - Kirigami.Units.smallSpacing
Layout.topMargin: Kirigami.Units.smallSpacing / 2
text: i18nc("@button View all one-on-one chats with your friends.", "Friends")
contentItem: Kirigami.Icon {
source: "system-users-symbolic"
QQC2.Label {
id: directChatNotificationCountLabel
anchors.top: parent.top
anchors.right: parent.right
anchors.topMargin: -Kirigami.Units.smallSpacing
anchors.rightMargin: -Kirigami.Units.smallSpacing
z: 1
width: Math.max(directChatNotificationCountTextMetrics.advanceWidth + Kirigami.Units.smallSpacing * 2, height)
height: Kirigami.Units.iconSizes.smallMedium
text: root.connection.directChatNotifications > 0 ? root.connection.directChatNotifications : ""
visible: (root.connection.directChatNotifications > 0 || root.connection.directChatInvites) && RoomManager.currentSpace !== "DM"
color: Kirigami.Theme.textColor
horizontalAlignment: Text.AlignHCenter
background: Rectangle {
visible: true
Kirigami.Theme.colorSet: Kirigami.Theme.Button
Kirigami.Theme.inherit: false
color: root.connection.directChatsHaveHighlightNotifications || root.connection.directChatInvites ? Kirigami.Theme.positiveTextColor : Kirigami.Theme.backgroundColor
radius: height / 2
}
TextMetrics {
id: directChatNotificationCountTextMetrics
text: directChatNotificationCountLabel.text
}
Kirigami.Icon {
anchors.fill: parent
source: "list-add-symbolic"
visible: root.connection.directChatInvites && root.connection.directChatNotifications === 0
}
}
}
activeFocusOnTab: true
checked: RoomManager.currentSpace === "DM"
onSelected: RoomManager.currentSpace = "DM"
}
Repeater {
model: RoomManager.sortFilterSpaceListModel
delegate: AvatarTabButton {
id: spaceDelegate
required property string displayName
required property url avatar
required property string roomId
required property var currentRoom
Layout.fillWidth: true
Layout.preferredHeight: width - Kirigami.Units.smallSpacing
Layout.maximumHeight: width - Kirigami.Units.smallSpacing
text: displayName
source: avatar
notificationCount: spaceDelegate.currentRoom.childrenNotificationCount
notificationHighlight: spaceDelegate.currentRoom.childrenHaveHighlightNotifications
showNotificationLabel: spaceDelegate.currentRoom.childrenNotificationCount > 0 && RoomManager.currentSpace != spaceDelegate.roomId
activeFocusOnTab: true
onSelected: {
RoomManager.currentSpace = spaceDelegate.roomId;
}
checked: RoomManager.currentSpace === roomId
onContextMenuRequested: root.createContextMenu(currentRoom)
}
}
AvatarTabButton {
id: recommendedSpaceButton
Layout.fillWidth: true
Layout.preferredHeight: width - Kirigami.Units.smallSpacing
Layout.maximumHeight: width - Kirigami.Units.smallSpacing
activeFocusOnTab: true
visible: SpaceHierarchyCache.recommendedSpaceId.length > 0 && !root.connection.room(SpaceHierarchyCache.recommendedSpaceId) && !SpaceHierarchyCache.recommendedSpaceHidden
text: i18nc("Join <name of a space>", "Join %1", SpaceHierarchyCache.recommendedSpaceDisplayName)
source: SpaceHierarchyCache.recommendedSpaceAvatar.toString().length > 0 ? root.connection.makeMediaUrl(SpaceHierarchyCache.recommendedSpaceAvatar) : ""
onSelected: {
recommendedSpaceDialogComponent.createObject(QQC2.Overlay.overlay, {
connection: root.connection
}).open();
}
Component {
id: recommendedSpaceDialogComponent
RecommendedSpaceDialog {}
}
Rectangle {
color: Kirigami.Theme.backgroundColor
width: Kirigami.Units.gridUnit * 1.5
height: width
anchors.bottom: parent.bottom
anchors.bottomMargin: Kirigami.Units.smallSpacing
anchors.rightMargin: Kirigami.Units.smallSpacing * 2
anchors.right: parent.right
radius: width / 2
z: parent.z + 1
Kirigami.Icon {
anchors.fill: parent
z: parent + 1
source: "list-add"
}
}
}
Kirigami.Separator {
Layout.fillWidth: true
Layout.topMargin: Kirigami.Units.smallSpacing / 2
Layout.bottomMargin: Kirigami.Units.smallSpacing / 2
Layout.leftMargin: Kirigami.Units.smallSpacing
Layout.rightMargin: Kirigami.Units.smallSpacing
}
AvatarTabButton {
Layout.fillWidth: true
Layout.preferredHeight: width - Kirigami.Units.smallSpacing
Layout.maximumHeight: width - Kirigami.Units.smallSpacing
text: i18n("Create a space")
contentItem: Kirigami.Icon {
source: "list-add"
}
activeFocusOnTab: true
onSelected: pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'CreateRoomDialog'), {
connection: root.connection,
isSpace: true,
title: i18nc("@title", "Create a Space")
}, {
title: i18nc("@title", "Create a Space")
})
}
AvatarTabButton {
Layout.fillWidth: true
Layout.preferredHeight: width - Kirigami.Units.smallSpacing
Layout.maximumHeight: width - Kirigami.Units.smallSpacing
text: i18nc("@action:button", "Explore rooms")
contentItem: Kirigami.Icon {
source: "compass"
}
activeFocusOnTab: true
onSelected: {
let dialog = pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'ExploreRoomsPage'), {
connection: root.connection,
keyword: RoomManager.sortFilterRoomTreeModel.filterText
}, {
title: i18nc("@title", "Explore Rooms")
});
dialog.roomSelected.connect((roomId, displayName, avatarUrl, alias, topic, memberCount, isJoined) => {
RoomManager.resolveResource(roomId.length > 0 ? roomId : alias, isJoined ? "" : "join");
});
}
}
}
}
}
function createContextMenu(room: NeoChatRoom): void {
let context = spaceListContextMenu.createObject(root, {
room: room,
connection: root.connection
});
context.popup();
}
Component {
id: spaceListContextMenu
SpaceListContextMenu {
window: root.QQC2.ApplicationWindow.window
}
}
}

View File

@@ -1,77 +0,0 @@
// SPDX-FileCopyrightText: 2020 Carl Schwan <carl@carlschwan.eu>
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org>
// SPDX-License-Identifier: GPL-3.0-only
import QtQuick
import QtQuick.Controls as QQC2
import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.components as KirigamiComponents
import org.kde.kirigamiaddons.formcard as FormCard
import org.kde.neochat
import org.kde.neochat.settings
/**
* Context menu when clicking on a room in the room list
*/
KirigamiComponents.ConvergentContextMenu {
id: root
property NeoChatRoom room
required property NeoChatConnection connection
required property Kirigami.ApplicationWindow window
headerContentItem: RowLayout {
spacing: Kirigami.Units.largeSpacing
KirigamiComponents.Avatar {
id: avatar
source: room.avatarMediaUrl
Layout.preferredWidth: Kirigami.Units.gridUnit * 2
Layout.preferredHeight: Kirigami.Units.gridUnit * 2
}
Kirigami.Heading {
level: 2
Layout.fillWidth: true
text: room.displayName
wrapMode: Text.WordWrap
}
}
QQC2.Action {
text: i18nc("'Space' is a matrix space", "View Space")
icon.name: "view-list-details"
onTriggered: RoomManager.resolveResource(room.id)
}
QQC2.Action {
text: i18nc("@action:inmenu", "Copy Address to Clipboard")
icon.name: "edit-copy"
onTriggered: if (room.canonicalAlias.length === 0) {
Clipboard.saveText(room.id);
} else {
Clipboard.saveText(room.canonicalAlias);
}
}
QQC2.Action {
text: i18nc("'Space' is a matrix space", "Space Settings")
icon.name: 'settings-configure-symbolic'
onTriggered: {
RoomSettingsView.openRoomSettings(root.room, RoomSettingsView.Space);
}
}
Kirigami.Action {
separator: true
}
QQC2.Action {
text: i18nc("'Space' is a matrix space", "Leave Space")
icon.name: "go-previous"
onTriggered: RoomManager.leaveRoom(room)
}
}

View File

@@ -1,129 +0,0 @@
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org>
// SPDX-License-Identifier: GPL-2.0-or-later
import QtQuick
import QtQuick.Controls as QQC2
import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.components as KirigamiComponents
import org.kde.kirigamiaddons.delegates as Delegates
import org.kde.neochat
import org.kde.neochat.settings
RowLayout {
id: root
required property NeoChatConnection connection
property bool collapsed: false
property bool bottomEdge: true
property var addAccount
spacing: Kirigami.Units.largeSpacing
Layout.topMargin: Kirigami.Units.smallSpacing
Layout.bottomMargin: Kirigami.Units.smallSpacing
Layout.rightMargin: Kirigami.Units.largeSpacing
Layout.leftMargin: Kirigami.Units.largeSpacing
Layout.minimumHeight: bottomEdge ? Kirigami.Units.gridUnit * 3 : -1
onVisibleChanged: {
if (!visible) {
accountsPopup.close();
}
}
QQC2.ToolButton {
id: accountButton
down: accountMenu.opened || pressed
onClicked: accountMenu.popup()
Layout.fillWidth: true
Layout.fillHeight: true
QQC2.ToolTip.visible: hovered
QQC2.ToolTip.text: i18nc("@info:tooltip", "Manage Account")
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
contentItem: RowLayout {
spacing: Kirigami.Units.largeSpacing
KirigamiComponents.Avatar {
readonly property url avatarUrl: root.connection.localUser.avatarUrl
Layout.preferredWidth: Kirigami.Units.iconSizes.medium
Layout.preferredHeight: Kirigami.Units.iconSizes.medium
Layout.leftMargin: Kirigami.Units.largeSpacing
// Note: User::avatarUrl does not set user_id, and thus cannot be used directly here. Hence the makeMediaUrl.
source: avatarUrl.toString().length > 0 ? root.connection.makeMediaUrl(avatarUrl) : ""
name: root.connection.localUser.displayName
}
ColumnLayout {
Layout.fillWidth: true
Layout.maximumWidth: Math.round(root.width * 0.55)
visible: !root.collapsed
spacing: 0
QQC2.Label {
id: displayNameLabel
Layout.fillWidth: true
text: root.connection.localUser.displayName
textFormat: Text.PlainText
elide: Text.ElideRight
}
QQC2.Label {
id: idLabel
Layout.fillWidth: true
text: (root.connection.label.length > 0 ? (root.connection.label + " ") : "") + root.connection.localUser.id
font.pointSize: displayNameLabel.font.pointSize * 0.8
opacity: 0.7
textFormat: Text.PlainText
elide: Text.ElideRight
}
}
}
AccountMenu {
id: accountMenu
connection: root.connection
window: QQC2.ApplicationWindow.window as Kirigami.ApplicationWindow
}
}
Kirigami.ActionToolBar {
alignment: Qt.AlignRight
display: QQC2.Button.IconOnly
Layout.fillWidth: true
Layout.preferredWidth: maximumContentWidth
actions: [
Kirigami.Action {
text: i18n("Switch User")
icon.name: "system-switch-user"
shortcut: "Ctrl+U"
onTriggered: accountSwitchDialog.createObject(QQC2.Overlay.overlay, {
connection: root.connection
}).open();
},
Kirigami.Action {
text: i18n("Open Settings")
icon.name: "settings-configure-symbolic"
onTriggered: {
NeoChatSettingsView.open();
}
}
]
}
Component {
id: accountSwitchDialog
AccountSwitchDialog {}
}
}

View File

@@ -1,41 +0,0 @@
// SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
import QtQuick
import QtQuick.Controls as QQC2
import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import org.kde.neochat
QQC2.ToolBar {
id: root
/**
* @brief The connection for the current user.
*/
required property NeoChatConnection connection
property bool collapsed: false
padding: 0
background: Rectangle {
color: Kirigami.Theme.backgroundColor
Kirigami.Theme.colorSet: Kirigami.Theme.Window
Kirigami.Theme.inherit: false
}
contentItem: ColumnLayout {
spacing: 0
Kirigami.Separator {
Layout.fillWidth: true
}
UserInfo {
collapsed: root.collapsed
bottomEdge: true
connection: root.connection
}
}
}