Room Drawer Media Tab
Add a tab bar to the room drawer which includes a new media tab in addition to the room information tab. This mr completes the architecture for adding others easily later e.g. message highlights or threads. To put this together I had to make sure things like the menus and the maximize delegate were available to both the room drawer and page so there is some rework there to put it all together. Wide\  Mobile\ 
This commit is contained in:
@@ -2,12 +2,17 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
|
||||
#include "mediamessagefiltermodel.h"
|
||||
#include "models/messageeventmodel.h"
|
||||
|
||||
#include <Quotient/room.h>
|
||||
|
||||
MediaMessageFilterModel::MediaMessageFilterModel(QObject *parent)
|
||||
#include "messageeventmodel.h"
|
||||
#include "messagefiltermodel.h"
|
||||
|
||||
MediaMessageFilterModel::MediaMessageFilterModel(QObject *parent, MessageFilterModel *sourceMediaModel)
|
||||
: QSortFilterProxyModel(parent)
|
||||
{
|
||||
Q_ASSERT(sourceMediaModel);
|
||||
setSourceModel(sourceMediaModel);
|
||||
}
|
||||
|
||||
bool MediaMessageFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
|
||||
@@ -43,9 +48,9 @@ QVariant MediaMessageFilterModel::data(const QModelIndex &index, int role) const
|
||||
}
|
||||
if (role == TypeRole) {
|
||||
if (mapToSource(index).data(MessageEventModel::DelegateTypeRole).toInt() == MessageEventModel::DelegateType::Image) {
|
||||
return 0;
|
||||
return MediaType::Image;
|
||||
} else {
|
||||
return 1;
|
||||
return MediaType::Video;
|
||||
}
|
||||
}
|
||||
if (role == CaptionRole) {
|
||||
@@ -57,6 +62,13 @@ QVariant MediaMessageFilterModel::data(const QModelIndex &index, int role) const
|
||||
if (role == SourceHeightRole) {
|
||||
return mapToSource(index).data(MessageEventModel::MediaInfoRole).toMap()[QStringLiteral("height")].toFloat();
|
||||
}
|
||||
// We need to catch this one and return true if the next media object was
|
||||
// on a different day.
|
||||
if (role == MessageEventModel::ShowSectionRole) {
|
||||
const auto day = mapToSource(index).data(MessageEventModel::TimeRole).toDateTime().toLocalTime().date();
|
||||
const auto previousEventDay = mapToSource(this->index(index.row() + 1, 0)).data(MessageEventModel::TimeRole).toDateTime().toLocalTime().date();
|
||||
return day != previousEventDay;
|
||||
}
|
||||
|
||||
return sourceModel()->data(mapToSource(index), role);
|
||||
}
|
||||
|
||||
@@ -4,9 +4,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <qobjectdefs.h>
|
||||
|
||||
#include "models/messagefiltermodel.h"
|
||||
|
||||
class MessageFilterModel;
|
||||
|
||||
/**
|
||||
* @class MediaMessageFilterModel
|
||||
*
|
||||
@@ -18,6 +21,12 @@ class MediaMessageFilterModel : public QSortFilterProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum MediaType {
|
||||
Image = 0,
|
||||
Video,
|
||||
};
|
||||
Q_ENUM(MediaType)
|
||||
|
||||
/**
|
||||
* @brief Defines the model roles.
|
||||
*/
|
||||
@@ -31,7 +40,7 @@ public:
|
||||
};
|
||||
Q_ENUM(Roles)
|
||||
|
||||
explicit MediaMessageFilterModel(QObject *parent = nullptr);
|
||||
explicit MediaMessageFilterModel(QObject *parent = nullptr, MessageFilterModel *sourceMediaModel = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Custom filter to show only image and video messages.
|
||||
@@ -43,7 +52,7 @@ public:
|
||||
*
|
||||
* @sa QSortFilterProxyModel::data
|
||||
*/
|
||||
[[nodiscard]] QVariant data(const QModelIndex &idx, int role = Qt::DisplayRole) const override;
|
||||
[[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
/**
|
||||
* @brief Returns a mapping from Role enum values to role names.
|
||||
|
||||
@@ -10,9 +10,12 @@
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
MessageFilterModel::MessageFilterModel(QObject *parent)
|
||||
MessageFilterModel::MessageFilterModel(QObject *parent, MessageEventModel *sourceMessageModel)
|
||||
: QSortFilterProxyModel(parent)
|
||||
{
|
||||
Q_ASSERT(sourceMessageModel);
|
||||
setSourceModel(sourceMessageModel);
|
||||
|
||||
connect(NeoChatConfig::self(), &NeoChatConfig::ShowStateEventChanged, this, [this] {
|
||||
invalidateFilter();
|
||||
});
|
||||
|
||||
@@ -33,7 +33,7 @@ public:
|
||||
LastRole, // Keep this last
|
||||
};
|
||||
|
||||
explicit MessageFilterModel(QObject *parent = nullptr);
|
||||
explicit MessageFilterModel(QObject *parent = nullptr, MessageEventModel *sourceMessageModel = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Custom filter function to remove hidden messages.
|
||||
|
||||
@@ -100,6 +100,11 @@ Components.AlbumMaximizeComponent {
|
||||
dialog.currentFile = dialog.folder + "/" + currentRoom.fileNameToDownload(root.currentEventId)
|
||||
}
|
||||
|
||||
Component {
|
||||
id: fileDelegateContextMenu
|
||||
FileDelegateContextMenu {}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: saveAsDialog
|
||||
Platform.FileDialog {
|
||||
|
||||
@@ -129,13 +129,19 @@ TimelineContainer {
|
||||
|
||||
TapHandler {
|
||||
acceptedButtons: Qt.LeftButton
|
||||
gesturePolicy: TapHandler.ReleaseWithinBounds | TapHandler.WithinBounds
|
||||
onTapped: {
|
||||
imageContainer.QQC2.ToolTip.hide()
|
||||
if (root.mediaInfo.animated) {
|
||||
imageContainer.imageItem.paused = true
|
||||
}
|
||||
root.ListView.view.interactive = false
|
||||
root.ListView.view.showMaximizedMedia(root.index)
|
||||
// We need to make sure the index is that of the MediaMessageFilterModel.
|
||||
if (root.ListView.view.model instanceof MessageFilterModel) {
|
||||
RoomManager.maximizeMedia(RoomManager.mediaMessageFilterModel.getRowForSourceItem(root.index))
|
||||
} else {
|
||||
RoomManager.maximizeMedia(root.index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,7 +150,7 @@ TimelineContainer {
|
||||
openSavedFile()
|
||||
} else {
|
||||
openOnFinished = true
|
||||
currentRoom.downloadFile(root.eventId, StandardPaths.writableLocation(StandardPaths.CacheLocation) + "/" + root.eventId.replace(":", "_").replace("/", "_").replace("+", "_") + currentRoom.fileNameToDownload(root.eventId))
|
||||
ListView.view.currentRoom.downloadFile(root.eventId, StandardPaths.writableLocation(StandardPaths.CacheLocation) + "/" + root.eventId.replace(":", "_").replace("/", "_").replace("+", "_") + ListView.view.currentRoom.fileNameToDownload(root.eventId))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@ QQC2.ItemDelegate {
|
||||
property alias labelText: sectionLabel.text
|
||||
property var maxWidth: Number.POSITIVE_INFINITY
|
||||
|
||||
property int colorSet: Kirigami.Theme.Window
|
||||
|
||||
topPadding: Kirigami.Units.largeSpacing
|
||||
bottomPadding: 0 // Note not 0 by default
|
||||
|
||||
@@ -42,6 +44,6 @@ QQC2.ItemDelegate {
|
||||
background: Rectangle {
|
||||
color: Config.blur ? "transparent" : Kirigami.Theme.backgroundColor
|
||||
Kirigami.Theme.inherit: false
|
||||
Kirigami.Theme.colorSet: Config.compactLayout ? Kirigami.Theme.View : Kirigami.Theme.Window
|
||||
Kirigami.Theme.colorSet: sectionDelegate.colorSet
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,6 +65,16 @@ ColumnLayout {
|
||||
*/
|
||||
required property bool showAuthor
|
||||
|
||||
/**
|
||||
* @brief Whether the author should always be shown.
|
||||
*
|
||||
* This is primarily used when these delegates are used in a filtered list of
|
||||
* events rather than a sequential timeline, e.g. the media model view.
|
||||
*
|
||||
* @note This setting still respects the avatar configuration settings.
|
||||
*/
|
||||
property bool alwaysShowAuthor: false
|
||||
|
||||
/**
|
||||
* @brief The delegate type of the message.
|
||||
*/
|
||||
@@ -262,12 +272,17 @@ ColumnLayout {
|
||||
*/
|
||||
property bool cardBackground: true
|
||||
|
||||
/**
|
||||
* @brief Whether the delegate should always stretch to the maximum availabel width.
|
||||
*/
|
||||
property bool alwaysMaxWidth: false
|
||||
|
||||
/**
|
||||
* @brief Whether local user messages should be aligned right.
|
||||
*
|
||||
* TODO: make private
|
||||
*/
|
||||
property bool showUserMessageOnRight: Config.showLocalMessagesOnRight && root.author.isLocalUser && !Config.compactLayout
|
||||
property bool showUserMessageOnRight: Config.showLocalMessagesOnRight && root.author.isLocalUser && !Config.compactLayout && !alwaysMaxWidth
|
||||
|
||||
/**
|
||||
* @brief Whether the message should be highlighted.
|
||||
@@ -296,7 +311,7 @@ ColumnLayout {
|
||||
width: parent ? timelineDelegateSizeHelper.currentWidth : 0
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
state: Config.compactLayout ? "alignLeft" : "alignCenter"
|
||||
state: Config.compactLayout || root.alwaysMaxWidth ? "alignLeft" : "alignCenter"
|
||||
// Align left when in compact mode and center when using bubbles
|
||||
states: [
|
||||
State {
|
||||
@@ -325,21 +340,21 @@ ColumnLayout {
|
||||
|
||||
SectionDelegate {
|
||||
id: sectionDelegate
|
||||
|
||||
Layout.fillWidth: true
|
||||
visible: root.showSection
|
||||
labelText: root.section
|
||||
colorSet: Config.compactLayout || root.alwaysMaxWidth ? Kirigami.Theme.View : Kirigami.Theme.Window
|
||||
}
|
||||
|
||||
QQC2.ItemDelegate {
|
||||
id: mainContainer
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: root.showAuthor ? Kirigami.Units.largeSpacing : (Config.compactLayout ? 1 : Kirigami.Units.smallSpacing)
|
||||
Layout.topMargin: root.showAuthor || root.alwaysShowAuthor ? Kirigami.Units.largeSpacing : (Config.compactLayout ? 1 : Kirigami.Units.smallSpacing)
|
||||
Layout.leftMargin: Kirigami.Units.smallSpacing
|
||||
Layout.rightMargin: Kirigami.Units.smallSpacing
|
||||
|
||||
implicitHeight: Math.max(root.showAuthor ? avatar.implicitHeight : 0, bubble.height)
|
||||
implicitHeight: Math.max(root.showAuthor || root.alwaysShowAuthor ? avatar.implicitHeight : 0, bubble.height)
|
||||
|
||||
Component.onCompleted: {
|
||||
if (root.isReply && root.reply === undefined) {
|
||||
@@ -365,7 +380,7 @@ ColumnLayout {
|
||||
topMargin: Kirigami.Units.smallSpacing
|
||||
}
|
||||
|
||||
visible: root.showAuthor &&
|
||||
visible: (root.showAuthor || root.alwaysShowAuthor) &&
|
||||
Config.showAvatarInTimeline &&
|
||||
(Config.compactLayout || !showUserMessageOnRight)
|
||||
name: root.author.displayName
|
||||
@@ -395,7 +410,7 @@ ColumnLayout {
|
||||
rightMargin: Kirigami.Units.largeSpacing
|
||||
}
|
||||
// HACK: anchoring didn't reset anchors.right when switching from parent.right to undefined reliably
|
||||
width: Config.compactLayout ? mainContainer.width - (Config.showAvatarInTimeline ? Kirigami.Units.gridUnit * 2 : 0) + Kirigami.Units.largeSpacing * 2 : implicitWidth
|
||||
width: Config.compactLayout || root.alwaysMaxWidth ? mainContainer.width - (Config.showAvatarInTimeline ? Kirigami.Units.gridUnit * 2 : 0) + Kirigami.Units.largeSpacing * 2 : implicitWidth
|
||||
|
||||
state: showUserMessageOnRight ? "userMessageOnRight" : "userMessageOnLeft"
|
||||
// states for anchor animations on window resize
|
||||
@@ -440,7 +455,7 @@ ColumnLayout {
|
||||
id: rowLayout
|
||||
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
visible: root.showAuthor
|
||||
visible: root.showAuthor || root.alwaysShowAuthor
|
||||
|
||||
QQC2.Label {
|
||||
id: nameLabel
|
||||
@@ -535,7 +550,7 @@ ColumnLayout {
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
visible: mainContainer.hovered && Config.compactLayout
|
||||
visible: mainContainer.hovered && (Config.compactLayout || root.alwaysMaxWidth)
|
||||
color: Kirigami.ColorUtils.tintWithAlpha(Kirigami.Theme.backgroundColor, Kirigami.Theme.highlightColor, 0.15)
|
||||
radius: Kirigami.Units.smallSpacing
|
||||
}
|
||||
@@ -577,6 +592,16 @@ ColumnLayout {
|
||||
return (yoff + height > 0 && yoff < ListView.view.height)
|
||||
}
|
||||
|
||||
Component {
|
||||
id: messageDelegateContextMenu
|
||||
MessageDelegateContextMenu {}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: fileDelegateContextMenu
|
||||
FileDelegateContextMenu {}
|
||||
}
|
||||
|
||||
/// Open message context dialog for file and videos
|
||||
function openFileContext(file) {
|
||||
const contextMenu = fileDelegateContextMenu.createObject(root, {
|
||||
@@ -616,8 +641,8 @@ ColumnLayout {
|
||||
startBreakpoint: Kirigami.Units.gridUnit * 46
|
||||
endBreakpoint: Kirigami.Units.gridUnit * 66
|
||||
startPercentWidth: 100
|
||||
endPercentWidth: Config.compactLayout ? 100 : 85
|
||||
maxWidth: Config.compactLayout ? -1 : Kirigami.Units.gridUnit * 60
|
||||
endPercentWidth: Config.compactLayout || root.alwaysMaxWidth ? 100 : 85
|
||||
maxWidth: Config.compactLayout || root.alwaysMaxWidth ? -1 : Kirigami.Units.gridUnit * 60
|
||||
|
||||
parentWidth: root.parent ? root.parent.width - (Config.compactLayout && root.ListView.view.width >= Kirigami.Units.gridUnit * 20 ? Kirigami.Units.gridUnit * 2 + Kirigami.Units.largeSpacing : 0) : 0
|
||||
}
|
||||
@@ -625,8 +650,8 @@ ColumnLayout {
|
||||
id: bubbleSizeHelper
|
||||
startBreakpoint: Kirigami.Units.gridUnit * 25
|
||||
endBreakpoint: Kirigami.Units.gridUnit * 40
|
||||
startPercentWidth: Config.compactLayout ? 100 : 90
|
||||
endPercentWidth: Config.compactLayout ? 100 : 60
|
||||
startPercentWidth: Config.compactLayout || root.alwaysMaxWidth ? 100 : 90
|
||||
endPercentWidth: Config.compactLayout || root.alwaysMaxWidth ? 100 : 60
|
||||
|
||||
parentWidth: mainContainer.availableWidth - (Config.showAvatarInTimeline ? avatar.width + bubble.anchors.leftMargin : 0)
|
||||
}
|
||||
|
||||
@@ -292,7 +292,12 @@ TimelineContainer {
|
||||
onTriggered: {
|
||||
root.ListView.view.interactive = false
|
||||
vid.pause()
|
||||
root.ListView.view.showMaximizedMedia(root.index)
|
||||
// We need to make sure the index is that of the MediaMessageFilterModel.
|
||||
if (root.ListView.view.model instanceof MessageFilterModel) {
|
||||
RoomManager.maximizeMedia(RoomManager.mediaMessageFilterModel.getRowForSourceItem(root.index))
|
||||
} else {
|
||||
RoomManager.maximizeMedia(root.index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -328,6 +333,7 @@ TimelineContainer {
|
||||
|
||||
TapHandler {
|
||||
acceptedButtons: Qt.LeftButton
|
||||
gesturePolicy: TapHandler.ReleaseWithinBounds | TapHandler.WithinBounds
|
||||
onTapped: if (root.progressInfo.completed) {
|
||||
if (vid.playbackState == MediaPlayer.PlayingState) {
|
||||
vid.pause()
|
||||
@@ -352,7 +358,7 @@ TimelineContainer {
|
||||
playSavedFile()
|
||||
} else {
|
||||
playOnFinished = true
|
||||
currentRoom.downloadFile(root.eventId, Platform.StandardPaths.writableLocation(Platform.StandardPaths.CacheLocation) + "/" + root.eventId.replace(":", "_").replace("/", "_").replace("+", "_") + currentRoom.fileNameToDownload(root.eventId))
|
||||
ListView.view.currentRoom.downloadFile(root.eventId, Platform.StandardPaths.writableLocation(Platform.StandardPaths.CacheLocation) + "/" + root.eventId.replace(":", "_").replace("/", "_").replace("+", "_") + ListView.view.currentRoom.fileNameToDownload(root.eventId))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,9 @@ QQC2.ScrollView {
|
||||
|
||||
ListView {
|
||||
id: messageListView
|
||||
// So that delegates can access the current room properly.
|
||||
readonly property NeoChatRoom currentRoom: root.currentRoom
|
||||
|
||||
readonly property int largestVisibleIndex: count > 0 ? indexAt(contentX + (width / 2), contentY + height - 1) : -1
|
||||
readonly property var sectionBannerItem: contentHeight >= height ? itemAtIndex(sectionBannerIndex()) : undefined
|
||||
|
||||
@@ -47,33 +50,23 @@ QQC2.ScrollView {
|
||||
interactive: Kirigami.Settings.isMobile
|
||||
bottomMargin: Kirigami.Units.largeSpacing + Math.round(Kirigami.Theme.defaultFont.pointSize * 2)
|
||||
|
||||
model: sortedMessageEventModel
|
||||
|
||||
MessageEventModel {
|
||||
id: messageEventModel
|
||||
room: root.currentRoom
|
||||
}
|
||||
|
||||
MessageFilterModel {
|
||||
id: sortedMessageEventModel
|
||||
sourceModel: messageEventModel
|
||||
}
|
||||
model: RoomManager.messageFilterModel
|
||||
|
||||
Timer {
|
||||
interval: 1000
|
||||
running: messageListView.atYBeginning
|
||||
triggeredOnStart: true
|
||||
onTriggered: {
|
||||
if (messageListView.atYBeginning && messageEventModel.canFetchMore(messageEventModel.index(0, 0))) {
|
||||
messageEventModel.fetchMore(messageEventModel.index(0, 0));
|
||||
if (messageListView.atYBeginning && RoomManager.messageEventModel.canFetchMore(RoomManager.messageEventModel.index(0, 0))) {
|
||||
RoomManager.messageEventModel.fetchMore(RoomManager.messageEventModel.index(0, 0));
|
||||
}
|
||||
}
|
||||
repeat: true
|
||||
}
|
||||
|
||||
// HACK: The view should do this automatically but doesn't.
|
||||
onAtYBeginningChanged: if (atYBeginning && messageEventModel.canFetchMore(messageEventModel.index(0, 0))) {
|
||||
messageEventModel.fetchMore(messageEventModel.index(0, 0));
|
||||
onAtYBeginningChanged: if (atYBeginning && RoomManager.messageEventModel.canFetchMore(RoomManager.messageEventModel.index(0, 0))) {
|
||||
RoomManager.messageEventModel.fetchMore(RoomManager.messageEventModel.index(0, 0));
|
||||
}
|
||||
|
||||
Timer {
|
||||
@@ -207,18 +200,6 @@ QQC2.ScrollView {
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: messageDelegateContextMenu
|
||||
|
||||
MessageDelegateContextMenu {}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: fileDelegateContextMenu
|
||||
|
||||
FileDelegateContextMenu {}
|
||||
}
|
||||
|
||||
TypingPane {
|
||||
id: typingPane
|
||||
visible: root.currentRoom && root.currentRoom.usersTyping.length > 0
|
||||
@@ -285,7 +266,7 @@ QQC2.ScrollView {
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: messageEventModel
|
||||
target: RoomManager.messageEventModel
|
||||
|
||||
function onRowsInserted() {
|
||||
markReadIfVisibleTimer.restart()
|
||||
@@ -326,7 +307,7 @@ QQC2.ScrollView {
|
||||
|
||||
Connections {
|
||||
//enabled: Config.showFancyEffects
|
||||
target: messageEventModel
|
||||
target: RoomManager.messageEventModel
|
||||
|
||||
function onFancyEffectsReasonFound(fancyEffect) {
|
||||
fancyEffectsContainer.processFancyEffectsReason(fancyEffect)
|
||||
@@ -344,21 +325,23 @@ QQC2.ScrollView {
|
||||
}
|
||||
}
|
||||
|
||||
MediaMessageFilterModel {
|
||||
id: mediaMessageFilterModel
|
||||
sourceModel: sortedMessageEventModel
|
||||
}
|
||||
|
||||
Component {
|
||||
id: maximizeComponent
|
||||
NeochatMaximizeComponent {
|
||||
model: mediaMessageFilterModel
|
||||
model: RoomManager.mediaMessageFilterModel
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: RoomManager
|
||||
function onShowMaximizedMedia(index) {
|
||||
messageListView.showMaximizedMedia(index)
|
||||
}
|
||||
}
|
||||
|
||||
function showMaximizedMedia(index) {
|
||||
var popup = maximizeComponent.createObject(QQC2.ApplicationWindow.overlay, {
|
||||
initialIndex: index === -1 ? -1 : mediaMessageFilterModel.getRowForSourceItem(index)
|
||||
initialIndex: index
|
||||
})
|
||||
popup.closed.connect(() => {
|
||||
messageListView.interactive = true
|
||||
@@ -374,10 +357,10 @@ QQC2.ScrollView {
|
||||
}
|
||||
|
||||
function eventToIndex(eventID) {
|
||||
const index = messageEventModel.eventIdToRow(eventID)
|
||||
const index = RoomManager.messageEventModel.eventIdToRow(eventID)
|
||||
if (index === -1)
|
||||
return -1
|
||||
return sortedMessageEventModel.mapFromSource(messageEventModel.index(index, 0)).row
|
||||
return RoomManager.messageFilterModel.mapFromSource(RoomManager.messageEventModel.index(index, 0)).row
|
||||
}
|
||||
|
||||
function firstVisibleIndex() {
|
||||
|
||||
@@ -65,9 +65,12 @@ Kirigami.OverlayDrawer {
|
||||
onAnimatingChanged: if (dim === false) dim = undefined
|
||||
|
||||
topPadding: 0
|
||||
bottomPadding: 0
|
||||
leftPadding: 0
|
||||
rightPadding: 0
|
||||
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
||||
|
||||
contentItem: Loader {
|
||||
id: loader
|
||||
active: root.drawerOpen
|
||||
@@ -75,6 +78,8 @@ Kirigami.OverlayDrawer {
|
||||
sourceComponent: ColumnLayout {
|
||||
spacing: 0
|
||||
|
||||
Component.onCompleted: infoAction.toggle()
|
||||
|
||||
QQC2.ToolBar {
|
||||
Layout.fillWidth: true
|
||||
|
||||
@@ -83,7 +88,7 @@ Kirigami.OverlayDrawer {
|
||||
contentItem: RowLayout {
|
||||
Kirigami.Heading {
|
||||
Layout.fillWidth: true
|
||||
text: i18n("Room information")
|
||||
text: drawerItemLoader.item ? drawerItemLoader.item.title : ""
|
||||
}
|
||||
|
||||
QQC2.ToolButton {
|
||||
@@ -102,18 +107,47 @@ Kirigami.OverlayDrawer {
|
||||
}
|
||||
}
|
||||
|
||||
QQC2.ScrollView {
|
||||
Loader {
|
||||
id: drawerItemLoader
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
sourceComponent: roomInformation
|
||||
}
|
||||
|
||||
// HACK: Hide unnecessary horizontal scrollbar (https://bugreports.qt.io/browse/QTBUG-83890)
|
||||
QQC2.ScrollBar.horizontal.policy: QQC2.ScrollBar.AlwaysOff
|
||||
|
||||
Component {
|
||||
id: roomInformation
|
||||
RoomInformation {
|
||||
id: roomInformation
|
||||
room: root.room
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: roomMedia
|
||||
RoomMedia {
|
||||
currentRoom: root.room
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.NavigationTabBar {
|
||||
id: navigationBar
|
||||
Layout.fillWidth: true
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.Window
|
||||
Kirigami.Theme.inherit: false
|
||||
|
||||
actions: [
|
||||
Kirigami.Action {
|
||||
id: infoAction
|
||||
text: i18n("Information")
|
||||
icon.name: "documentinfo"
|
||||
onTriggered: drawerItemLoader.sourceComponent = roomInformation
|
||||
},
|
||||
Kirigami.Action {
|
||||
text: i18n("Media")
|
||||
icon.name: "mail-attachment-symbollic"
|
||||
onTriggered: drawerItemLoader.sourceComponent = roomMedia
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import org.kde.neochat 1.0
|
||||
*
|
||||
* @sa RoomDrawer
|
||||
*/
|
||||
Kirigami.ScrollablePage {
|
||||
Kirigami.Page {
|
||||
id: root
|
||||
|
||||
/**
|
||||
@@ -26,18 +26,66 @@ Kirigami.ScrollablePage {
|
||||
*/
|
||||
readonly property NeoChatRoom room: RoomManager.currentRoom
|
||||
|
||||
title: roomInformation.title
|
||||
title: drawerItemLoader.item ? drawerItemLoader.item.title : ""
|
||||
|
||||
topPadding: 0
|
||||
bottomPadding: 0
|
||||
leftPadding: 0
|
||||
rightPadding: 0
|
||||
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
||||
Kirigami.Theme.inherit: false
|
||||
|
||||
Component.onCompleted: infoAction.toggle()
|
||||
|
||||
actions {
|
||||
main: Kirigami.Action {
|
||||
displayHint: Kirigami.DisplayHint.IconOnly
|
||||
text: i18n("Settings")
|
||||
icon.name: "settings-configure"
|
||||
onTriggered: applicationWindow().pageStack.pushDialogLayer('qrc:/Categories.qml', {room: root.room}, { title: i18n("Room Settings") })
|
||||
}
|
||||
}
|
||||
|
||||
RoomInformation {
|
||||
Loader {
|
||||
id: drawerItemLoader
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
sourceComponent: roomInformation
|
||||
}
|
||||
|
||||
Component {
|
||||
id: roomInformation
|
||||
room: root.room
|
||||
RoomInformation {
|
||||
room: root.room
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: roomMedia
|
||||
RoomMedia {
|
||||
currentRoom: root.room
|
||||
}
|
||||
}
|
||||
|
||||
footer: Kirigami.NavigationTabBar {
|
||||
id: navigationBar
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.Window
|
||||
Kirigami.Theme.inherit: false
|
||||
|
||||
actions: [
|
||||
Kirigami.Action {
|
||||
id: infoAction
|
||||
text: i18n("Information")
|
||||
icon.name: "documentinfo"
|
||||
onTriggered: drawerItemLoader.sourceComponent = roomInformation
|
||||
},
|
||||
Kirigami.Action {
|
||||
text: i18n("Media")
|
||||
icon.name: "mail-attachment-symbollic"
|
||||
onTriggered: drawerItemLoader.sourceComponent = roomMedia
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Connections {
|
||||
|
||||
@@ -24,7 +24,7 @@ import org.kde.neochat 1.0
|
||||
*
|
||||
* @sa RoomDrawer, RoomDrawerPage
|
||||
*/
|
||||
ListView {
|
||||
QQC2.ScrollView {
|
||||
id: root
|
||||
|
||||
/**
|
||||
@@ -37,206 +37,212 @@ ListView {
|
||||
*/
|
||||
readonly property string title: i18nc("@action:title", "Room information")
|
||||
|
||||
header: ColumnLayout {
|
||||
id: columnLayout
|
||||
// HACK: Hide unnecessary horizontal scrollbar (https://bugreports.qt.io/browse/QTBUG-83890)
|
||||
QQC2.ScrollBar.horizontal.policy: QQC2.ScrollBar.AlwaysOff
|
||||
|
||||
property alias userListSearchField: userListSearchField
|
||||
ListView {
|
||||
id: userList
|
||||
header: ColumnLayout {
|
||||
id: columnLayout
|
||||
|
||||
spacing: 0
|
||||
width: root.width
|
||||
property alias userListSearchField: userListSearchField
|
||||
|
||||
Loader {
|
||||
active: true
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Kirigami.Units.smallSpacing
|
||||
sourceComponent: root.room.isDirectChat() ? directChatDrawerHeader : groupChatDrawerHeader
|
||||
onItemChanged: if (item) {
|
||||
root.positionViewAtBeginning();
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.ListSectionHeader {
|
||||
label: i18n("Options")
|
||||
activeFocusOnTab: false
|
||||
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Delegates.RoundedItemDelegate {
|
||||
id: devtoolsButton
|
||||
|
||||
icon.name: "tools"
|
||||
text: i18n("Open developer tools")
|
||||
visible: Config.developerTools
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
onClicked: {
|
||||
applicationWindow().pageStack.pushDialogLayer("qrc:/DevtoolsPage.qml", {room: root.room}, {title: i18n("Developer Tools")})
|
||||
}
|
||||
}
|
||||
|
||||
Delegates.RoundedItemDelegate {
|
||||
id: searchButton
|
||||
|
||||
icon.name: "search"
|
||||
text: i18n("Search in this room")
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
onClicked: {
|
||||
pageStack.pushDialogLayer("qrc:/SearchPage.qml", {
|
||||
currentRoom: root.room
|
||||
}, {
|
||||
title: i18nc("@action:title", "Search")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Delegates.RoundedItemDelegate {
|
||||
id: favouriteButton
|
||||
|
||||
icon.name: root.room && root.room.isFavourite ? "rating" : "rating-unrated"
|
||||
text: root.room && root.room.isFavourite ? i18n("Remove room from favorites") : i18n("Make room favorite")
|
||||
|
||||
onClicked: root.room.isFavourite ? root.room.removeTag("m.favourite") : root.room.addTag("m.favourite", 1.0)
|
||||
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Delegates.RoundedItemDelegate {
|
||||
id: locationsButton
|
||||
|
||||
icon.name: "map-flat"
|
||||
text: i18n("Show locations for this room")
|
||||
|
||||
onClicked: pageStack.pushDialogLayer("qrc:/LocationsPage.qml", {
|
||||
room: root.room
|
||||
}, {
|
||||
title: i18nc("Locations on a map", "Locations")
|
||||
})
|
||||
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Kirigami.ListSectionHeader {
|
||||
label: i18n("Members")
|
||||
activeFocusOnTab: false
|
||||
spacing: 0
|
||||
visible: !root.room.isDirectChat()
|
||||
width: root.width
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
QQC2.ToolButton {
|
||||
id: memberSearchToggle
|
||||
checkable: true
|
||||
icon.name: "search"
|
||||
QQC2.ToolTip.text: i18n("Search user in room")
|
||||
QQC2.ToolTip.visible: hovered
|
||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
onToggled: {
|
||||
userListSearchField.text = "";
|
||||
Loader {
|
||||
active: true
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Kirigami.Units.smallSpacing
|
||||
sourceComponent: root.room.isDirectChat() ? directChatDrawerHeader : groupChatDrawerHeader
|
||||
onItemChanged: if (item) {
|
||||
userList.positionViewAtBeginning();
|
||||
}
|
||||
}
|
||||
|
||||
QQC2.ToolButton {
|
||||
visible: root.room.canSendState("invite")
|
||||
icon.name: "list-add-user"
|
||||
|
||||
onClicked: {
|
||||
applicationWindow().pageStack.pushDialogLayer("qrc:/InviteUserPage.qml", {room: root.room}, {title: i18nc("@title", "Invite a User")})
|
||||
}
|
||||
|
||||
QQC2.ToolTip.text: i18n("Invite user to room")
|
||||
QQC2.ToolTip.visible: hovered
|
||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
}
|
||||
|
||||
QQC2.Label {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
text: root.room ? i18np("%1 member", "%1 members", root.room.joinedCount) : i18n("No member count")
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.SearchField {
|
||||
id: userListSearchField
|
||||
visible: memberSearchToggle.checked
|
||||
|
||||
onVisibleChanged: if (visible) forceActiveFocus()
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: Kirigami.Units.largeSpacing - 1
|
||||
Layout.rightMargin: Kirigami.Units.largeSpacing - 1
|
||||
Layout.bottomMargin: Kirigami.Units.smallSpacing
|
||||
|
||||
focusSequence: "Ctrl+Shift+F"
|
||||
|
||||
onAccepted: sortedMessageEventModel.filterString = text;
|
||||
}
|
||||
}
|
||||
|
||||
KSortFilterProxyModel {
|
||||
id: sortedMessageEventModel
|
||||
|
||||
sourceModel: UserListModel {
|
||||
room: root.room
|
||||
}
|
||||
|
||||
sortRole: "powerLevel"
|
||||
sortOrder: Qt.DescendingOrder
|
||||
filterRole: "name"
|
||||
filterCaseSensitivity: Qt.CaseInsensitive
|
||||
}
|
||||
|
||||
model: root.room.isDirectChat() ? 0 : sortedMessageEventModel
|
||||
|
||||
clip: true
|
||||
activeFocusOnTab: true
|
||||
|
||||
delegate: Delegates.RoundedItemDelegate {
|
||||
id: userDelegate
|
||||
|
||||
required property string name
|
||||
required property string userId
|
||||
required property string avatar
|
||||
required property int powerLevel
|
||||
required property string powerLevelString
|
||||
|
||||
implicitHeight: Kirigami.Units.gridUnit * 2
|
||||
|
||||
text: name
|
||||
|
||||
onClicked: {
|
||||
userDelegate.highlighted = true;
|
||||
RoomManager.visitUser(room.getUser(userDelegate.userId).object, "mention")
|
||||
}
|
||||
|
||||
contentItem: RowLayout {
|
||||
KirigamiComponents.Avatar {
|
||||
implicitWidth: height
|
||||
sourceSize {
|
||||
height: Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing * 2.5
|
||||
width: Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing * 2.5
|
||||
}
|
||||
source: userDelegate.avatar
|
||||
name: userDelegate.userId
|
||||
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
|
||||
QQC2.Label {
|
||||
text: userDelegate.name
|
||||
textFormat: Text.PlainText
|
||||
elide: Text.ElideRight
|
||||
Kirigami.ListSectionHeader {
|
||||
label: i18n("Options")
|
||||
activeFocusOnTab: false
|
||||
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
QQC2.Label {
|
||||
visible: userDelegate.powerLevel > 0
|
||||
Delegates.RoundedItemDelegate {
|
||||
id: devtoolsButton
|
||||
|
||||
text: userDelegate.powerLevelString
|
||||
color: Kirigami.Theme.disabledTextColor
|
||||
textFormat: Text.PlainText
|
||||
icon.name: "tools"
|
||||
text: i18n("Open developer tools")
|
||||
visible: Config.developerTools
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
onClicked: {
|
||||
applicationWindow().pageStack.pushDialogLayer("qrc:/DevtoolsPage.qml", {room: root.room}, {title: i18n("Developer Tools")})
|
||||
}
|
||||
}
|
||||
|
||||
Delegates.RoundedItemDelegate {
|
||||
id: searchButton
|
||||
|
||||
icon.name: "search"
|
||||
text: i18n("Search in this room")
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
onClicked: {
|
||||
pageStack.pushDialogLayer("qrc:/SearchPage.qml", {
|
||||
currentRoom: root.room
|
||||
}, {
|
||||
title: i18nc("@action:title", "Search")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Delegates.RoundedItemDelegate {
|
||||
id: favouriteButton
|
||||
|
||||
icon.name: root.room && root.room.isFavourite ? "rating" : "rating-unrated"
|
||||
text: root.room && root.room.isFavourite ? i18n("Remove room from favorites") : i18n("Make room favorite")
|
||||
|
||||
onClicked: root.room.isFavourite ? root.room.removeTag("m.favourite") : root.room.addTag("m.favourite", 1.0)
|
||||
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Delegates.RoundedItemDelegate {
|
||||
id: locationsButton
|
||||
|
||||
icon.name: "map-flat"
|
||||
text: i18n("Show locations for this room")
|
||||
|
||||
onClicked: pageStack.pushDialogLayer("qrc:/LocationsPage.qml", {
|
||||
room: root.room
|
||||
}, {
|
||||
title: i18nc("Locations on a map", "Locations")
|
||||
})
|
||||
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Kirigami.ListSectionHeader {
|
||||
label: i18n("Members")
|
||||
activeFocusOnTab: false
|
||||
spacing: 0
|
||||
visible: !root.room.isDirectChat()
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
QQC2.ToolButton {
|
||||
id: memberSearchToggle
|
||||
checkable: true
|
||||
icon.name: "search"
|
||||
QQC2.ToolTip.text: i18n("Search user in room")
|
||||
QQC2.ToolTip.visible: hovered
|
||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
onToggled: {
|
||||
userListSearchField.text = "";
|
||||
}
|
||||
}
|
||||
|
||||
QQC2.ToolButton {
|
||||
visible: root.room.canSendState("invite")
|
||||
icon.name: "list-add-user"
|
||||
|
||||
onClicked: {
|
||||
applicationWindow().pageStack.pushDialogLayer("qrc:/InviteUserPage.qml", {room: root.room}, {title: i18nc("@title", "Invite a User")})
|
||||
}
|
||||
|
||||
QQC2.ToolTip.text: i18n("Invite user to room")
|
||||
QQC2.ToolTip.visible: hovered
|
||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
}
|
||||
|
||||
QQC2.Label {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
text: root.room ? i18np("%1 member", "%1 members", root.room.joinedCount) : i18n("No member count")
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.SearchField {
|
||||
id: userListSearchField
|
||||
visible: memberSearchToggle.checked
|
||||
|
||||
onVisibleChanged: if (visible) forceActiveFocus()
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: Kirigami.Units.largeSpacing - 1
|
||||
Layout.rightMargin: Kirigami.Units.largeSpacing - 1
|
||||
Layout.bottomMargin: Kirigami.Units.smallSpacing
|
||||
|
||||
focusSequence: "Ctrl+Shift+F"
|
||||
|
||||
onAccepted: sortedMessageEventModel.filterString = text;
|
||||
}
|
||||
}
|
||||
|
||||
KSortFilterProxyModel {
|
||||
id: sortedMessageEventModel
|
||||
|
||||
sourceModel: UserListModel {
|
||||
room: root.room
|
||||
}
|
||||
|
||||
sortRole: "powerLevel"
|
||||
sortOrder: Qt.DescendingOrder
|
||||
filterRole: "name"
|
||||
filterCaseSensitivity: Qt.CaseInsensitive
|
||||
}
|
||||
|
||||
model: root.room.isDirectChat() ? 0 : sortedMessageEventModel
|
||||
|
||||
clip: true
|
||||
activeFocusOnTab: true
|
||||
|
||||
delegate: Delegates.RoundedItemDelegate {
|
||||
id: userDelegate
|
||||
|
||||
required property string name
|
||||
required property string userId
|
||||
required property string avatar
|
||||
required property int powerLevel
|
||||
required property string powerLevelString
|
||||
|
||||
implicitHeight: Kirigami.Units.gridUnit * 2
|
||||
|
||||
text: name
|
||||
|
||||
onClicked: {
|
||||
userDelegate.highlighted = true;
|
||||
RoomManager.visitUser(room.getUser(userDelegate.userId).object, "mention")
|
||||
}
|
||||
|
||||
contentItem: RowLayout {
|
||||
KirigamiComponents.Avatar {
|
||||
implicitWidth: height
|
||||
sourceSize {
|
||||
height: Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing * 2.5
|
||||
width: Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing * 2.5
|
||||
}
|
||||
source: userDelegate.avatar
|
||||
name: userDelegate.userId
|
||||
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
|
||||
QQC2.Label {
|
||||
text: userDelegate.name
|
||||
textFormat: Text.PlainText
|
||||
elide: Text.ElideRight
|
||||
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
QQC2.Label {
|
||||
visible: userDelegate.powerLevel > 0
|
||||
|
||||
text: userDelegate.powerLevelString
|
||||
color: Kirigami.Theme.disabledTextColor
|
||||
textFormat: Text.PlainText
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -255,6 +261,6 @@ ListView {
|
||||
if (root.headerItem) {
|
||||
root.headerItem.userListSearchField.text = "";
|
||||
}
|
||||
root.currentIndex = -1
|
||||
userList.currentIndex = -1
|
||||
}
|
||||
}
|
||||
|
||||
71
src/qml/RoomDrawer/RoomMedia.qml
Normal file
71
src/qml/RoomDrawer/RoomMedia.qml
Normal file
@@ -0,0 +1,71 @@
|
||||
// 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 2.15
|
||||
import QtQuick.Controls 2.15 as QQC2
|
||||
import QtQuick.Layouts 1.15
|
||||
import Qt.labs.qmlmodels 1.0
|
||||
|
||||
import org.kde.kirigami 2.20 as Kirigami
|
||||
|
||||
import org.kde.neochat 1.0
|
||||
|
||||
/**
|
||||
* @brief Component for visualising the loaded media items in the room.
|
||||
*
|
||||
* The component is a simple list of media delegates (videos or images) with the
|
||||
* ability to open them in the mamimize component.
|
||||
*
|
||||
* @note This component is only the contents, it will need to be placed in either
|
||||
* a drawer (desktop) or page (mobile) to be used.
|
||||
*
|
||||
* @sa RoomDrawer, RoomDrawerPage
|
||||
*/
|
||||
QQC2.ScrollView {
|
||||
id: root
|
||||
|
||||
/**
|
||||
* @brief The title that should be displayed for this component if available.
|
||||
*/
|
||||
readonly property string title: i18nc("@action:title", "Room Media")
|
||||
|
||||
/**
|
||||
* @brief The current room that user is viewing.
|
||||
*/
|
||||
required property NeoChatRoom currentRoom
|
||||
|
||||
// HACK: Hide unnecessary horizontal scrollbar (https://bugreports.qt.io/browse/QTBUG-83890)
|
||||
QQC2.ScrollBar.horizontal.policy: QQC2.ScrollBar.AlwaysOff
|
||||
|
||||
ListView {
|
||||
// So that delegates can access current room properly.
|
||||
readonly property NeoChatRoom currentRoom: root.currentRoom
|
||||
|
||||
clip: true
|
||||
verticalLayoutDirection: ListView.BottomToTop
|
||||
|
||||
model: RoomManager.mediaMessageFilterModel
|
||||
|
||||
delegate: DelegateChooser {
|
||||
role: "type"
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: 0//MediaMessageFilterModel.Image
|
||||
delegate: ImageDelegate {
|
||||
alwaysShowAuthor: true
|
||||
alwaysMaxWidth: true
|
||||
cardBackground: false
|
||||
}
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: 1//MediaMessageFilterModel.Video
|
||||
delegate: VideoDelegate {
|
||||
alwaysShowAuthor: true
|
||||
alwaysMaxWidth: true
|
||||
cardBackground: false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -141,6 +141,7 @@
|
||||
<file alias="DirectChatDrawerHeader.qml">qml/RoomDrawer/DirectChatDrawerHeader.qml</file>
|
||||
<file alias="GroupChatDrawerHeader.qml">qml/RoomDrawer/GroupChatDrawerHeader.qml</file>
|
||||
<file alias="RoomInformation.qml">qml/RoomDrawer/RoomInformation.qml</file>
|
||||
<file alias="RoomMedia.qml">qml/RoomDrawer/RoomMedia.qml</file>
|
||||
<file alias="ChooseRoomDialog.qml">qml/Page/ChooseRoomDialog.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "roommanager.h"
|
||||
|
||||
#include "controller.h"
|
||||
#include "models/messageeventmodel.h"
|
||||
#include "neochatconfig.h"
|
||||
#include "neochatroom.h"
|
||||
#include <KLocalizedString>
|
||||
@@ -26,8 +27,15 @@ RoomManager::RoomManager(QObject *parent)
|
||||
, m_currentRoom(nullptr)
|
||||
, m_lastCurrentRoom(nullptr)
|
||||
, m_config(KConfig(QStringLiteral("data"), KConfig::SimpleConfig, QStandardPaths::AppDataLocation))
|
||||
, m_messageEventModel(new MessageEventModel(this))
|
||||
, m_messageFilterModel(new MessageFilterModel(this, m_messageEventModel))
|
||||
, m_mediaMessageFilterModel(new MediaMessageFilterModel(this, m_messageFilterModel))
|
||||
{
|
||||
m_lastRoomConfig = m_config.group(QStringLiteral("LastOpenRoom"));
|
||||
|
||||
connect(this, &RoomManager::currentRoomChanged, this, [this]() {
|
||||
m_messageEventModel->setRoom(m_currentRoom);
|
||||
});
|
||||
}
|
||||
|
||||
RoomManager::~RoomManager()
|
||||
@@ -45,6 +53,21 @@ NeoChatRoom *RoomManager::currentRoom() const
|
||||
return m_currentRoom;
|
||||
}
|
||||
|
||||
MessageEventModel *RoomManager::messageEventModel() const
|
||||
{
|
||||
return m_messageEventModel;
|
||||
}
|
||||
|
||||
MessageFilterModel *RoomManager::messageFilterModel() const
|
||||
{
|
||||
return m_messageFilterModel;
|
||||
}
|
||||
|
||||
MediaMessageFilterModel *RoomManager::mediaMessageFilterModel() const
|
||||
{
|
||||
return m_mediaMessageFilterModel;
|
||||
}
|
||||
|
||||
void RoomManager::openResource(const QString &idOrUri, const QString &action)
|
||||
{
|
||||
Uri uri{idOrUri};
|
||||
@@ -72,6 +95,14 @@ void RoomManager::openResource(const QString &idOrUri, const QString &action)
|
||||
}
|
||||
}
|
||||
|
||||
void RoomManager::maximizeMedia(int index)
|
||||
{
|
||||
if (index < -1 || index > m_mediaMessageFilterModel->rowCount()) {
|
||||
return;
|
||||
}
|
||||
Q_EMIT showMaximizedMedia(index);
|
||||
}
|
||||
|
||||
bool RoomManager::hasOpenRoom() const
|
||||
{
|
||||
return m_currentRoom != nullptr;
|
||||
@@ -266,7 +297,6 @@ void RoomManager::leaveRoom(NeoChatRoom *room)
|
||||
if (m_currentRoom && m_currentRoom->id() == room->id()) {
|
||||
m_currentRoom = m_lastCurrentRoom;
|
||||
m_lastCurrentRoom = nullptr;
|
||||
|
||||
Q_EMIT currentRoomChanged();
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <Quotient/uriresolver.h>
|
||||
|
||||
#include "chatdocumenthandler.h"
|
||||
#include "models/mediamessagefiltermodel.h"
|
||||
#include "models/messageeventmodel.h"
|
||||
#include "models/messagefiltermodel.h"
|
||||
|
||||
class NeoChatRoom;
|
||||
|
||||
@@ -36,6 +39,34 @@ class RoomManager : public QObject, public UriResolverBase
|
||||
*/
|
||||
Q_PROPERTY(NeoChatRoom *currentRoom READ currentRoom NOTIFY currentRoomChanged)
|
||||
|
||||
/**
|
||||
* @brief The MessageEventModel that should be used for room message visualisation.
|
||||
*
|
||||
* The room object the model uses to get the data will be updated by this class
|
||||
* so there is no need to do this manually or replace the model when a room
|
||||
* changes.
|
||||
*
|
||||
* @note Available here so that the room page and drawer both have access to the
|
||||
* same model.
|
||||
*/
|
||||
Q_PROPERTY(MessageEventModel *messageEventModel READ messageEventModel CONSTANT)
|
||||
|
||||
/**
|
||||
* @brief The MessageFilterModel that should be used for room message visualisation.
|
||||
*
|
||||
* @note Available here so that the room page and drawer both have access to the
|
||||
* same model.
|
||||
*/
|
||||
Q_PROPERTY(MessageFilterModel *messageFilterModel READ messageFilterModel CONSTANT)
|
||||
|
||||
/**
|
||||
* @brief The MediaMessageFilterModel that should be used for room media message visualisation.
|
||||
*
|
||||
* @note Available here so that the room page and drawer both have access to the
|
||||
* same model.
|
||||
*/
|
||||
Q_PROPERTY(MediaMessageFilterModel *mediaMessageFilterModel READ mediaMessageFilterModel CONSTANT)
|
||||
|
||||
/**
|
||||
* @brief Whether a room is currently open in NeoChat.
|
||||
*
|
||||
@@ -57,6 +88,10 @@ public:
|
||||
|
||||
NeoChatRoom *currentRoom() const;
|
||||
|
||||
MessageEventModel *messageEventModel() const;
|
||||
MessageFilterModel *messageFilterModel() const;
|
||||
MediaMessageFilterModel *mediaMessageFilterModel() const;
|
||||
|
||||
bool hasOpenRoom() const;
|
||||
|
||||
/**
|
||||
@@ -151,6 +186,15 @@ public:
|
||||
*/
|
||||
Q_INVOKABLE void openResource(const QString &idOrUri, const QString &action = {});
|
||||
|
||||
/**
|
||||
* @brief Show a media item maximized.
|
||||
*
|
||||
* @param index the index to open the maximize delegate model at. This is the
|
||||
* index in the MediaMessageFilterModel owned by this RoomManager. A value
|
||||
* of -1 opens a the default item.
|
||||
*/
|
||||
Q_INVOKABLE void maximizeMedia(int index);
|
||||
|
||||
/**
|
||||
* @brief Call this when the current used connection is dropped.
|
||||
*/
|
||||
@@ -208,6 +252,15 @@ Q_SIGNALS:
|
||||
*/
|
||||
void showUserDetail(const Quotient::User *user);
|
||||
|
||||
/**
|
||||
* @brief Request a media item is shown maximized.
|
||||
*
|
||||
* @param index the index to open the maximize delegate model at. This is the
|
||||
* index in the MediaMessageFilterModel owned by this RoomManager. A value
|
||||
* of -1 opens a the default item.
|
||||
*/
|
||||
void showMaximizedMedia(int index);
|
||||
|
||||
/**
|
||||
* @brief Show the direct chat confirmation dialog.
|
||||
*
|
||||
@@ -232,4 +285,8 @@ private:
|
||||
KConfig m_config;
|
||||
KConfigGroup m_lastRoomConfig;
|
||||
QPointer<ChatDocumentHandler> m_chatDocumentHandler;
|
||||
|
||||
MessageEventModel *m_messageEventModel;
|
||||
MessageFilterModel *m_messageFilterModel;
|
||||
MediaMessageFilterModel *m_mediaMessageFilterModel;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user