Loading and End of Timeline Delegates
Add delegate for showing the user a loading indicator and for the beginning of the timeline. BUG: 455045 BUG: 465285
This commit is contained in:
@@ -141,6 +141,8 @@ add_library(neochat STATIC
|
|||||||
colorschemer.h
|
colorschemer.h
|
||||||
models/notificationsmodel.cpp
|
models/notificationsmodel.cpp
|
||||||
models/notificationsmodel.h
|
models/notificationsmodel.h
|
||||||
|
models/timelinemodel.cpp
|
||||||
|
models/timelinemodel.h
|
||||||
)
|
)
|
||||||
|
|
||||||
qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
||||||
@@ -293,6 +295,8 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
|||||||
qml/SelectSpacesDialog.qml
|
qml/SelectSpacesDialog.qml
|
||||||
qml/AttachDialog.qml
|
qml/AttachDialog.qml
|
||||||
qml/NotificationsView.qml
|
qml/NotificationsView.qml
|
||||||
|
qml/LoadingDelegate.qml
|
||||||
|
qml/TimelineEndDelegate.qml
|
||||||
RESOURCES
|
RESOURCES
|
||||||
qml/confetti.png
|
qml/confetti.png
|
||||||
qml/glowdot.png
|
qml/glowdot.png
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ public:
|
|||||||
Poll, /**< The initial event for a poll. */
|
Poll, /**< The initial event for a poll. */
|
||||||
Location, /**< A location event. */
|
Location, /**< A location event. */
|
||||||
LiveLocation, /**< The initial event of a shared live location (i.e., the place where this is supposed to be shown in the timeline). */
|
LiveLocation, /**< The initial event of a shared live location (i.e., the place where this is supposed to be shown in the timeline). */
|
||||||
|
Loading, /**< A delegate to tell the user more messages are being loaded. */
|
||||||
|
TimelineEnd, /**< A delegate to inform that all messages are loaded. */
|
||||||
Other, /**< Anything that cannot be classified as another type. */
|
Other, /**< Anything that cannot be classified as another type. */
|
||||||
};
|
};
|
||||||
Q_ENUM(Type);
|
Q_ENUM(Type);
|
||||||
|
|||||||
@@ -389,13 +389,7 @@ int MessageEventModel::rowCount(const QModelIndex &parent) const
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto firstIt = m_currentRoom->messageEvents().crbegin();
|
return int(m_currentRoom->pendingEvents().size()) + m_currentRoom->timelineSize() + (m_lastReadEventIndex.isValid() ? 1 : 0);
|
||||||
if (firstIt != m_currentRoom->messageEvents().crend()) {
|
|
||||||
const auto &firstEvt = **firstIt;
|
|
||||||
return m_currentRoom->timelineSize() + (lastReadEventId != firstEvt.id() ? 1 : 0);
|
|
||||||
} else {
|
|
||||||
return m_currentRoom->timelineSize();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MessageEventModel::canFetchMore(const QModelIndex &parent) const
|
bool MessageEventModel::canFetchMore(const QModelIndex &parent) const
|
||||||
@@ -422,7 +416,7 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
|||||||
}
|
}
|
||||||
const auto row = idx.row();
|
const auto row = idx.row();
|
||||||
|
|
||||||
if (!m_currentRoom || row < 0 || row >= int(m_currentRoom->pendingEvents().size()) + m_currentRoom->timelineSize()) {
|
if (!m_currentRoom || row < 0 || row >= rowCount()) {
|
||||||
return {};
|
return {};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -465,7 +459,6 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
|||||||
return (reason.isEmpty()) ? i18n("<i>[This message was deleted]</i>")
|
return (reason.isEmpty()) ? i18n("<i>[This message was deleted]</i>")
|
||||||
: i18n("<i>[This message was deleted: %1]</i>", evt.redactedBecause()->reason());
|
: i18n("<i>[This message was deleted: %1]</i>", evt.redactedBecause()->reason());
|
||||||
}
|
}
|
||||||
|
|
||||||
return eventHandler.getRichBody();
|
return eventHandler.getRichBody();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,14 +8,15 @@
|
|||||||
#include "enums/delegatetype.h"
|
#include "enums/delegatetype.h"
|
||||||
#include "messageeventmodel.h"
|
#include "messageeventmodel.h"
|
||||||
#include "neochatconfig.h"
|
#include "neochatconfig.h"
|
||||||
|
#include "timelinemodel.h"
|
||||||
|
|
||||||
using namespace Quotient;
|
using namespace Quotient;
|
||||||
|
|
||||||
MessageFilterModel::MessageFilterModel(QObject *parent, MessageEventModel *sourceMessageModel)
|
MessageFilterModel::MessageFilterModel(QObject *parent, TimelineModel *sourceModel)
|
||||||
: QSortFilterProxyModel(parent)
|
: QSortFilterProxyModel(parent)
|
||||||
{
|
{
|
||||||
Q_ASSERT(sourceMessageModel);
|
Q_ASSERT(sourceModel);
|
||||||
setSourceModel(sourceMessageModel);
|
setSourceModel(sourceModel);
|
||||||
|
|
||||||
connect(NeoChatConfig::self(), &NeoChatConfig::ShowStateEventChanged, this, [this] {
|
connect(NeoChatConfig::self(), &NeoChatConfig::ShowStateEventChanged, this, [this] {
|
||||||
invalidateFilter();
|
invalidateFilter();
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include <QSortFilterProxyModel>
|
#include <QSortFilterProxyModel>
|
||||||
|
|
||||||
#include "messageeventmodel.h"
|
#include "messageeventmodel.h"
|
||||||
|
#include "timelinemodel.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class MessageFilterModel
|
* @class MessageFilterModel
|
||||||
@@ -36,7 +37,7 @@ public:
|
|||||||
LastRole, // Keep this last
|
LastRole, // Keep this last
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit MessageFilterModel(QObject *parent = nullptr, MessageEventModel *sourceMessageModel = nullptr);
|
explicit MessageFilterModel(QObject *parent = nullptr, TimelineModel *sourceModel = nullptr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Custom filter function to remove hidden messages.
|
* @brief Custom filter function to remove hidden messages.
|
||||||
|
|||||||
95
src/models/timelinemodel.cpp
Normal file
95
src/models/timelinemodel.cpp
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2022 James Graham <james.h.graham@protonmail.com>
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||||
|
|
||||||
|
#include "timelinemodel.h"
|
||||||
|
|
||||||
|
#include "delegatetype.h"
|
||||||
|
|
||||||
|
TimelineModel::TimelineModel(QObject *parent)
|
||||||
|
: QConcatenateTablesProxyModel(parent)
|
||||||
|
{
|
||||||
|
m_messageEventModel = new MessageEventModel(this);
|
||||||
|
addSourceModel(m_messageEventModel);
|
||||||
|
m_timelineEndModel = new TimelineEndModel(this);
|
||||||
|
addSourceModel(m_timelineEndModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
NeoChatRoom *TimelineModel::room() const
|
||||||
|
{
|
||||||
|
return m_messageEventModel->room();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimelineModel::setRoom(NeoChatRoom *room)
|
||||||
|
{
|
||||||
|
// Both models do their own null checking so just pass along.
|
||||||
|
m_messageEventModel->setRoom(room);
|
||||||
|
m_timelineEndModel->setRoom(room);
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageEventModel *TimelineModel::messageEventModel() const
|
||||||
|
{
|
||||||
|
return m_messageEventModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
QHash<int, QByteArray> TimelineModel::roleNames() const
|
||||||
|
{
|
||||||
|
return m_messageEventModel->roleNames();
|
||||||
|
}
|
||||||
|
|
||||||
|
TimelineEndModel::TimelineEndModel(QObject *parent)
|
||||||
|
: QAbstractListModel(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimelineEndModel::setRoom(NeoChatRoom *room)
|
||||||
|
{
|
||||||
|
if (room == m_room) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
beginResetModel();
|
||||||
|
|
||||||
|
if (m_room != nullptr) {
|
||||||
|
m_room->disconnect(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_room = room;
|
||||||
|
|
||||||
|
if (m_room != nullptr) {
|
||||||
|
connect(m_room, &Quotient::Room::eventsHistoryJobChanged, this, [this]() {
|
||||||
|
if (m_room->allHistoryLoaded()) {
|
||||||
|
// HACK: We have to do it this way because DelegateChooser doesn't update dynamically.
|
||||||
|
beginRemoveRows({}, 0, 0);
|
||||||
|
endRemoveRows();
|
||||||
|
beginInsertRows({}, 0, 0);
|
||||||
|
endInsertRows();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
endResetModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant TimelineEndModel::data(const QModelIndex &idx, int role) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(idx)
|
||||||
|
if (m_room == nullptr) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (role == DelegateTypeRole) {
|
||||||
|
return m_room->allHistoryLoaded() ? DelegateType::TimelineEnd : DelegateType::Loading;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
int TimelineEndModel::rowCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(parent)
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
QHash<int, QByteArray> TimelineEndModel::roleNames() const
|
||||||
|
{
|
||||||
|
return {{DelegateTypeRole, "delegateType"}};
|
||||||
|
}
|
||||||
112
src/models/timelinemodel.h
Normal file
112
src/models/timelinemodel.h
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2022 James Graham <james.h.graham@protonmail.com>
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QAbstractListModel>
|
||||||
|
#include <QConcatenateTablesProxyModel>
|
||||||
|
#include <QQmlEngine>
|
||||||
|
|
||||||
|
#include "messageeventmodel.h"
|
||||||
|
#include "neochatroom.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class TimelineEndModel
|
||||||
|
*
|
||||||
|
* A model to provide a single delegate to mark the end of the timeline.
|
||||||
|
*
|
||||||
|
* The delegate will either be a loading delegate if more events are being loaded
|
||||||
|
* or a timeline end delegate if all history is loaded.
|
||||||
|
*/
|
||||||
|
class TimelineEndModel : public QAbstractListModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
QML_ELEMENT
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Defines the model roles.
|
||||||
|
*/
|
||||||
|
enum Roles {
|
||||||
|
DelegateTypeRole = MessageEventModel::DelegateTypeRole, /**< The delegate type of the message. */
|
||||||
|
};
|
||||||
|
Q_ENUM(Roles)
|
||||||
|
|
||||||
|
explicit TimelineEndModel(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the room for the timeline.
|
||||||
|
*/
|
||||||
|
void setRoom(NeoChatRoom *room);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the given role value at the given index.
|
||||||
|
*
|
||||||
|
* @sa QAbstractItemModel::data
|
||||||
|
*/
|
||||||
|
[[nodiscard]] QVariant data(const QModelIndex &idx, int role = Qt::DisplayRole) const override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 1, the answer is always 1.
|
||||||
|
*
|
||||||
|
* @sa QAbstractItemModel::rowCount
|
||||||
|
*/
|
||||||
|
[[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns a map with DelegateTypeRole it's the only one.
|
||||||
|
*
|
||||||
|
* @sa Roles, QAbstractItemModel::roleNames()
|
||||||
|
*/
|
||||||
|
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
NeoChatRoom *m_room = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class TimelineModel
|
||||||
|
*
|
||||||
|
* A model to visualise a room timeline.
|
||||||
|
*
|
||||||
|
* This model combines a MessageEventModel with a TimelineEndModel.
|
||||||
|
*
|
||||||
|
* @sa MessageEventModel, TimelineEndModel
|
||||||
|
*/
|
||||||
|
class TimelineModel : public QConcatenateTablesProxyModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
QML_ELEMENT
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The current room that the model is getting its messages from.
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(NeoChatRoom *room READ room WRITE setRoom NOTIFY roomChanged)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The MessageEventModel for the timeline.
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(MessageEventModel *messageEventModel READ messageEventModel CONSTANT)
|
||||||
|
|
||||||
|
public:
|
||||||
|
TimelineModel(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
[[nodiscard]] NeoChatRoom *room() const;
|
||||||
|
void setRoom(NeoChatRoom *room);
|
||||||
|
|
||||||
|
MessageEventModel *messageEventModel() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns a mapping from Role enum values to role names.
|
||||||
|
*
|
||||||
|
* @sa Roles, QAbstractProxyModel::roleNames()
|
||||||
|
*/
|
||||||
|
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void roomChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
MessageEventModel *m_messageEventModel = nullptr;
|
||||||
|
TimelineEndModel *m_timelineEndModel = nullptr;
|
||||||
|
};
|
||||||
@@ -99,6 +99,7 @@ DelegateChooser {
|
|||||||
connection: root.connection
|
connection: root.connection
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: DelegateType.LiveLocation
|
roleValue: DelegateType.LiveLocation
|
||||||
delegate: LiveLocationDelegate {
|
delegate: LiveLocationDelegate {
|
||||||
@@ -107,6 +108,18 @@ DelegateChooser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: DelegateType.Loading
|
||||||
|
delegate: LoadingDelegate {}
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: DelegateType.TimelineEnd
|
||||||
|
delegate: TimelineEndDelegate {
|
||||||
|
room: root.room
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: DelegateType.Other
|
roleValue: DelegateType.Other
|
||||||
delegate: Item {}
|
delegate: Item {}
|
||||||
|
|||||||
15
src/qml/LoadingDelegate.qml
Normal file
15
src/qml/LoadingDelegate.qml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
// 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 org.kde.kirigami as Kirigami
|
||||||
|
|
||||||
|
import org.kde.neochat
|
||||||
|
|
||||||
|
TimelineDelegate {
|
||||||
|
id: root
|
||||||
|
contentItem: Kirigami.PlaceholderMessage {
|
||||||
|
text: i18n("Loading…")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,17 +23,17 @@ Kirigami.Page {
|
|||||||
required property NeoChatConnection connection
|
required property NeoChatConnection connection
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The MessageEventModel to use.
|
* @brief The TimelineModel to use.
|
||||||
*
|
*
|
||||||
* Required so that new events can be requested when the end of the current
|
* Required so that new events can be requested when the end of the current
|
||||||
* local timeline is reached.
|
* local timeline is reached.
|
||||||
*
|
*
|
||||||
* @note For loading a room in a different window, override this with a new
|
* @note For loading a room in a different window, override this with a new
|
||||||
* MessageEventModel set with the room to be shown.
|
* TimelineModel set with the room to be shown.
|
||||||
*
|
*
|
||||||
* @sa MessageEventModel
|
* @sa TimelineModel
|
||||||
*/
|
*/
|
||||||
property MessageEventModel messageEventModel: RoomManager.messageEventModel
|
property TimelineModel timelineModel: RoomManager.timelineModel
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The MessageFilterModel to use.
|
* @brief The MessageFilterModel to use.
|
||||||
@@ -41,9 +41,9 @@ Kirigami.Page {
|
|||||||
* This model has the filtered list of events that should be shown in the timeline.
|
* This model has the filtered list of events that should be shown in the timeline.
|
||||||
*
|
*
|
||||||
* @note For loading a room in a different window, override this with a new
|
* @note For loading a room in a different window, override this with a new
|
||||||
* MessageFilterModel with the new MessageEventModel as the source model.
|
* MessageFilterModel with the new TimelineModel as the source model.
|
||||||
*
|
*
|
||||||
* @sa MessageEventModel, MessageFilterModel
|
* @sa TimelineModel, MessageFilterModel
|
||||||
*/
|
*/
|
||||||
property MessageFilterModel messageFilterModel: RoomManager.messageFilterModel
|
property MessageFilterModel messageFilterModel: RoomManager.messageFilterModel
|
||||||
|
|
||||||
@@ -56,7 +56,7 @@ Kirigami.Page {
|
|||||||
* @note For loading a room in a different window, override this with a new
|
* @note For loading a room in a different window, override this with a new
|
||||||
* MediaMessageFilterModel with the new MessageFilterModel as the source model.
|
* MediaMessageFilterModel with the new MessageFilterModel as the source model.
|
||||||
*
|
*
|
||||||
* @sa MessageEventModel, MessageFilterModel
|
* @sa TimelineModel, MessageFilterModel
|
||||||
*/
|
*/
|
||||||
property MediaMessageFilterModel mediaMessageFilterModel: RoomManager.mediaMessageFilterModel
|
property MediaMessageFilterModel mediaMessageFilterModel: RoomManager.mediaMessageFilterModel
|
||||||
|
|
||||||
@@ -120,7 +120,7 @@ Kirigami.Page {
|
|||||||
sourceComponent: TimelineView {
|
sourceComponent: TimelineView {
|
||||||
id: timelineView
|
id: timelineView
|
||||||
currentRoom: root.currentRoom
|
currentRoom: root.currentRoom
|
||||||
messageEventModel: root.messageEventModel
|
timelineModel: root.timelineModel
|
||||||
messageFilterModel: root.messageFilterModel
|
messageFilterModel: root.messageFilterModel
|
||||||
actionsHandler: root.actionsHandler
|
actionsHandler: root.actionsHandler
|
||||||
onFocusChatBar: {
|
onFocusChatBar: {
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ Kirigami.ApplicationWindow {
|
|||||||
disableCancelShortcut: true
|
disableCancelShortcut: true
|
||||||
connection: root.connection
|
connection: root.connection
|
||||||
|
|
||||||
messageEventModel: MessageEventModel {
|
timelineModel: TimelineModel {
|
||||||
room: currentRoom
|
room: currentRoom
|
||||||
}
|
}
|
||||||
messageFilterModel: MessageFilterModel {
|
messageFilterModel: MessageFilterModel {
|
||||||
|
|||||||
90
src/qml/TimelineEndDelegate.qml
Normal file
90
src/qml/TimelineEndDelegate.qml
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
// 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.Layouts
|
||||||
|
|
||||||
|
import org.kde.kirigami as Kirigami
|
||||||
|
import org.kde.kirigamiaddons.labs.components as KirigamiComponents
|
||||||
|
|
||||||
|
import org.kde.neochat
|
||||||
|
|
||||||
|
TimelineDelegate {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The current room that user is viewing.
|
||||||
|
*/
|
||||||
|
required property NeoChatRoom room
|
||||||
|
|
||||||
|
contentItem: ColumnLayout {
|
||||||
|
RowLayout {
|
||||||
|
Layout.topMargin: Kirigami.Units.largeSpacing
|
||||||
|
Layout.bottomMargin: Kirigami.Units.largeSpacing
|
||||||
|
Layout.leftMargin: Kirigami.Units.largeSpacing
|
||||||
|
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||||
|
spacing: Kirigami.Units.largeSpacing
|
||||||
|
|
||||||
|
KirigamiComponents.Avatar {
|
||||||
|
Layout.preferredWidth: Kirigami.Units.iconSizes.large
|
||||||
|
Layout.preferredHeight: Kirigami.Units.iconSizes.large
|
||||||
|
|
||||||
|
name: root.room ? root.room.displayName : ""
|
||||||
|
source: root.room && root.room.avatarMediaId ? ("image://mxc/" + root.room.avatarMediaId) : ""
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
visible: room.usesEncryption
|
||||||
|
color: Kirigami.Theme.backgroundColor
|
||||||
|
|
||||||
|
width: Kirigami.Units.gridUnit
|
||||||
|
height: Kirigami.Units.gridUnit
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
bottom: parent.bottom
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
|
||||||
|
radius: Math.round(width / 2)
|
||||||
|
|
||||||
|
Kirigami.Icon {
|
||||||
|
source: "channel-secure-symbolic"
|
||||||
|
anchors.fill: parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
Kirigami.Heading {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: root.room ? root.room.displayName : i18n("No name")
|
||||||
|
textFormat: Text.PlainText
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
}
|
||||||
|
|
||||||
|
Kirigami.SelectableLabel {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
font: Kirigami.Theme.smallFont
|
||||||
|
textFormat: TextEdit.PlainText
|
||||||
|
visible: root.room && root.room.canonicalAlias
|
||||||
|
text: root.room && root.room.canonicalAlias ? root.room.canonicalAlias : ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Kirigami.SelectableLabel {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: Kirigami.Units.largeSpacing
|
||||||
|
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||||
|
Layout.bottomMargin: Kirigami.Units.largeSpacing
|
||||||
|
|
||||||
|
text: i18n("This is the beginning of the chat. There are no historical messages beyond this point.")
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
onLinkActivated: link => UrlHelper.openUrl(link)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,12 +28,12 @@ QQC2.ScrollView {
|
|||||||
property bool roomChanging: false
|
property bool roomChanging: false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The MessageEventModel to use.
|
* @brief The TimelineModel to use.
|
||||||
*
|
*
|
||||||
* Required so that new events can be requested when the end of the current
|
* Required so that new events can be requested when the end of the current
|
||||||
* local timeline is reached.
|
* local timeline is reached.
|
||||||
*/
|
*/
|
||||||
required property MessageEventModel messageEventModel
|
required property TimelineModel timelineModel
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The MessageFilterModel to use.
|
* @brief The MessageFilterModel to use.
|
||||||
@@ -85,16 +85,16 @@ QQC2.ScrollView {
|
|||||||
running: messageListView.atYBeginning
|
running: messageListView.atYBeginning
|
||||||
triggeredOnStart: true
|
triggeredOnStart: true
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
if (messageListView.atYBeginning && root.messageEventModel.canFetchMore(root.messageEventModel.index(0, 0))) {
|
if (messageListView.atYBeginning && root.timelineModel.messageEventModel.canFetchMore(root.timelineModel.index(0, 0))) {
|
||||||
root.messageEventModel.fetchMore(root.messageEventModel.index(0, 0));
|
root.timelineModel.messageEventModel.fetchMore(root.timelineModel.index(0, 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
repeat: true
|
repeat: true
|
||||||
}
|
}
|
||||||
|
|
||||||
// HACK: The view should do this automatically but doesn't.
|
// HACK: The view should do this automatically but doesn't.
|
||||||
onAtYBeginningChanged: if (atYBeginning && root.messageEventModel.canFetchMore(root.messageEventModel.index(0, 0))) {
|
onAtYBeginningChanged: if (atYBeginning && root.timelineModel.messageEventModel.canFetchMore(root.timelineModel.index(0, 0))) {
|
||||||
root.messageEventModel.fetchMore(root.messageEventModel.index(0, 0));
|
root.timelineModel.messageEventModel.fetchMore(root.timelineModel.index(0, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
@@ -270,7 +270,7 @@ QQC2.ScrollView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: root.messageEventModel
|
target: root.timelineModel
|
||||||
|
|
||||||
function onRowsInserted() {
|
function onRowsInserted() {
|
||||||
markReadIfVisibleTimer.restart()
|
markReadIfVisibleTimer.restart()
|
||||||
@@ -311,7 +311,7 @@ QQC2.ScrollView {
|
|||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
//enabled: Config.showFancyEffects
|
//enabled: Config.showFancyEffects
|
||||||
target: root.messageEventModel
|
target: root.timelineModel.messageEventModel
|
||||||
|
|
||||||
function onFancyEffectsReasonFound(fancyEffect) {
|
function onFancyEffectsReasonFound(fancyEffect) {
|
||||||
fancyEffectsContainer.processFancyEffectsReason(fancyEffect)
|
fancyEffectsContainer.processFancyEffectsReason(fancyEffect)
|
||||||
@@ -336,10 +336,10 @@ QQC2.ScrollView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function eventToIndex(eventID) {
|
function eventToIndex(eventID) {
|
||||||
const index = root.messageEventModel.eventIdToRow(eventID)
|
const index = root.timelineModel.messageEventModel.eventIdToRow(eventID)
|
||||||
if (index === -1)
|
if (index === -1)
|
||||||
return -1
|
return -1
|
||||||
return root.messageFilterModel.mapFromSource(root.messageEventModel.index(index, 0)).row
|
return root.messageFilterModel.mapFromSource(root.timelineModel.index(index, 0)).row
|
||||||
}
|
}
|
||||||
|
|
||||||
function firstVisibleIndex() {
|
function firstVisibleIndex() {
|
||||||
|
|||||||
@@ -8,8 +8,10 @@
|
|||||||
#include "controller.h"
|
#include "controller.h"
|
||||||
#include "enums/delegatetype.h"
|
#include "enums/delegatetype.h"
|
||||||
#include "models/messageeventmodel.h"
|
#include "models/messageeventmodel.h"
|
||||||
|
#include "models/timelinemodel.h"
|
||||||
#include "neochatconfig.h"
|
#include "neochatconfig.h"
|
||||||
#include "neochatroom.h"
|
#include "neochatroom.h"
|
||||||
|
|
||||||
#include <KLocalizedString>
|
#include <KLocalizedString>
|
||||||
#include <QDesktopServices>
|
#include <QDesktopServices>
|
||||||
#include <QQuickTextDocument>
|
#include <QQuickTextDocument>
|
||||||
@@ -29,14 +31,14 @@ RoomManager::RoomManager(QObject *parent)
|
|||||||
, m_currentRoom(nullptr)
|
, m_currentRoom(nullptr)
|
||||||
, m_lastCurrentRoom(nullptr)
|
, m_lastCurrentRoom(nullptr)
|
||||||
, m_config(KSharedConfig::openStateConfig())
|
, m_config(KSharedConfig::openStateConfig())
|
||||||
, m_messageEventModel(new MessageEventModel(this))
|
, m_timelineModel(new TimelineModel(this))
|
||||||
, m_messageFilterModel(new MessageFilterModel(this, m_messageEventModel))
|
, m_messageFilterModel(new MessageFilterModel(this, m_timelineModel))
|
||||||
, m_mediaMessageFilterModel(new MediaMessageFilterModel(this, m_messageFilterModel))
|
, m_mediaMessageFilterModel(new MediaMessageFilterModel(this, m_messageFilterModel))
|
||||||
{
|
{
|
||||||
m_lastRoomConfig = m_config->group(QStringLiteral("LastOpenRoom"));
|
m_lastRoomConfig = m_config->group(QStringLiteral("LastOpenRoom"));
|
||||||
|
|
||||||
connect(this, &RoomManager::currentRoomChanged, this, [this]() {
|
connect(this, &RoomManager::currentRoomChanged, this, [this]() {
|
||||||
m_messageEventModel->setRoom(m_currentRoom);
|
m_timelineModel->setRoom(m_currentRoom);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,9 +57,9 @@ NeoChatRoom *RoomManager::currentRoom() const
|
|||||||
return m_currentRoom;
|
return m_currentRoom;
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageEventModel *RoomManager::messageEventModel() const
|
TimelineModel *RoomManager::timelineModel() const
|
||||||
{
|
{
|
||||||
return m_messageEventModel;
|
return m_timelineModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageFilterModel *RoomManager::messageFilterModel() const
|
MessageFilterModel *RoomManager::messageFilterModel() const
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
#include "models/mediamessagefiltermodel.h"
|
#include "models/mediamessagefiltermodel.h"
|
||||||
#include "models/messageeventmodel.h"
|
#include "models/messageeventmodel.h"
|
||||||
#include "models/messagefiltermodel.h"
|
#include "models/messagefiltermodel.h"
|
||||||
|
#include "models/timelinemodel.h"
|
||||||
|
|
||||||
class NeoChatRoom;
|
class NeoChatRoom;
|
||||||
class NeoChatConnection;
|
class NeoChatConnection;
|
||||||
@@ -48,7 +49,7 @@ class RoomManager : public QObject, public UriResolverBase
|
|||||||
Q_PROPERTY(NeoChatRoom *currentRoom READ currentRoom NOTIFY currentRoomChanged)
|
Q_PROPERTY(NeoChatRoom *currentRoom READ currentRoom NOTIFY currentRoomChanged)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The MessageEventModel that should be used for room message visualisation.
|
* @brief The TimelineModel that should be used for room message visualisation.
|
||||||
*
|
*
|
||||||
* The room object the model uses to get the data will be updated by this class
|
* 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
|
* so there is no need to do this manually or replace the model when a room
|
||||||
@@ -57,7 +58,7 @@ class RoomManager : public QObject, public UriResolverBase
|
|||||||
* @note Available here so that the room page and drawer both have access to the
|
* @note Available here so that the room page and drawer both have access to the
|
||||||
* same model.
|
* same model.
|
||||||
*/
|
*/
|
||||||
Q_PROPERTY(MessageEventModel *messageEventModel READ messageEventModel CONSTANT)
|
Q_PROPERTY(TimelineModel *timelineModel READ timelineModel CONSTANT)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The MessageFilterModel that should be used for room message visualisation.
|
* @brief The MessageFilterModel that should be used for room message visualisation.
|
||||||
@@ -101,7 +102,7 @@ public:
|
|||||||
|
|
||||||
NeoChatRoom *currentRoom() const;
|
NeoChatRoom *currentRoom() const;
|
||||||
|
|
||||||
MessageEventModel *messageEventModel() const;
|
TimelineModel *timelineModel() const;
|
||||||
MessageFilterModel *messageFilterModel() const;
|
MessageFilterModel *messageFilterModel() const;
|
||||||
MediaMessageFilterModel *mediaMessageFilterModel() const;
|
MediaMessageFilterModel *mediaMessageFilterModel() const;
|
||||||
|
|
||||||
@@ -383,7 +384,7 @@ private:
|
|||||||
KConfigGroup m_lastRoomConfig;
|
KConfigGroup m_lastRoomConfig;
|
||||||
QPointer<ChatDocumentHandler> m_chatDocumentHandler;
|
QPointer<ChatDocumentHandler> m_chatDocumentHandler;
|
||||||
|
|
||||||
MessageEventModel *m_messageEventModel;
|
TimelineModel *m_timelineModel;
|
||||||
MessageFilterModel *m_messageFilterModel;
|
MessageFilterModel *m_messageFilterModel;
|
||||||
MediaMessageFilterModel *m_mediaMessageFilterModel;
|
MediaMessageFilterModel *m_mediaMessageFilterModel;
|
||||||
NeoChatConnection *m_connection;
|
NeoChatConnection *m_connection;
|
||||||
|
|||||||
Reference in New Issue
Block a user