Add delegates to show room upgrades into the timeline model.
The delegates are at the beginning for upgraded rooms and end for predecessors. Closes: network/neochat#620 and network/neochat#619
This commit is contained in:
@@ -38,6 +38,8 @@ public:
|
|||||||
ReadMarker, /**< The local user read marker. */
|
ReadMarker, /**< The local user read marker. */
|
||||||
Loading, /**< A delegate to tell the user more messages are being loaded. */
|
Loading, /**< A delegate to tell the user more messages are being loaded. */
|
||||||
TimelineEnd, /**< A delegate to inform that all messages are loaded. */
|
TimelineEnd, /**< A delegate to inform that all messages are loaded. */
|
||||||
|
Predecessor, /**< A delegate to show a room predecessor. */
|
||||||
|
Successor, /**< A delegate to show a room successor. */
|
||||||
Other, /**< Anything that cannot be classified as another type. */
|
Other, /**< Anything that cannot be classified as another type. */
|
||||||
};
|
};
|
||||||
Q_ENUM(Type);
|
Q_ENUM(Type);
|
||||||
|
|||||||
@@ -3,11 +3,15 @@
|
|||||||
|
|
||||||
#include "timelinemodel.h"
|
#include "timelinemodel.h"
|
||||||
|
|
||||||
|
#include <Quotient/qt_connection_util.h>
|
||||||
|
|
||||||
#include "delegatetype.h"
|
#include "delegatetype.h"
|
||||||
|
|
||||||
TimelineModel::TimelineModel(QObject *parent)
|
TimelineModel::TimelineModel(QObject *parent)
|
||||||
: QConcatenateTablesProxyModel(parent)
|
: QConcatenateTablesProxyModel(parent)
|
||||||
{
|
{
|
||||||
|
m_timelineBeginningModel = new TimelineBeginningModel(this);
|
||||||
|
addSourceModel(m_timelineBeginningModel);
|
||||||
m_messageEventModel = new MessageEventModel(this);
|
m_messageEventModel = new MessageEventModel(this);
|
||||||
addSourceModel(m_messageEventModel);
|
addSourceModel(m_messageEventModel);
|
||||||
m_timelineEndModel = new TimelineEndModel(this);
|
m_timelineEndModel = new TimelineEndModel(this);
|
||||||
@@ -23,6 +27,7 @@ void TimelineModel::setRoom(NeoChatRoom *room)
|
|||||||
{
|
{
|
||||||
// Both models do their own null checking so just pass along.
|
// Both models do their own null checking so just pass along.
|
||||||
m_messageEventModel->setRoom(room);
|
m_messageEventModel->setRoom(room);
|
||||||
|
m_timelineBeginningModel->setRoom(room);
|
||||||
m_timelineEndModel->setRoom(room);
|
m_timelineEndModel->setRoom(room);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,6 +41,69 @@ QHash<int, QByteArray> TimelineModel::roleNames() const
|
|||||||
return m_messageEventModel->roleNames();
|
return m_messageEventModel->roleNames();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TimelineBeginningModel::TimelineBeginningModel(QObject *parent)
|
||||||
|
: QAbstractListModel(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimelineBeginningModel::setRoom(NeoChatRoom *room)
|
||||||
|
{
|
||||||
|
if (room == m_room) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
beginResetModel();
|
||||||
|
|
||||||
|
if (m_room != nullptr) {
|
||||||
|
m_room->disconnect(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_room = room;
|
||||||
|
|
||||||
|
if (m_room != nullptr) {
|
||||||
|
Quotient::connectUntil(m_room.get(), &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();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
endResetModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant TimelineBeginningModel::data(const QModelIndex &idx, int role) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(idx)
|
||||||
|
if (m_room == nullptr) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (role == DelegateTypeRole) {
|
||||||
|
return DelegateType::Successor;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
int TimelineBeginningModel::rowCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(parent)
|
||||||
|
if (m_room == nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return m_room->successorId().isEmpty() ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
QHash<int, QByteArray> TimelineBeginningModel::roleNames() const
|
||||||
|
{
|
||||||
|
return {{DelegateTypeRole, "delegateType"}};
|
||||||
|
}
|
||||||
|
|
||||||
TimelineEndModel::TimelineEndModel(QObject *parent)
|
TimelineEndModel::TimelineEndModel(QObject *parent)
|
||||||
: QAbstractListModel(parent)
|
: QAbstractListModel(parent)
|
||||||
{
|
{
|
||||||
@@ -78,7 +146,11 @@ QVariant TimelineEndModel::data(const QModelIndex &idx, int role) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (role == DelegateTypeRole) {
|
if (role == DelegateTypeRole) {
|
||||||
return m_room->allHistoryLoaded() ? DelegateType::TimelineEnd : DelegateType::Loading;
|
if (idx.row() == 1 || rowCount() == 1) {
|
||||||
|
return m_room->allHistoryLoaded() ? DelegateType::TimelineEnd : DelegateType::Loading;
|
||||||
|
} else {
|
||||||
|
return DelegateType::Predecessor;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@@ -86,7 +158,10 @@ QVariant TimelineEndModel::data(const QModelIndex &idx, int role) const
|
|||||||
int TimelineEndModel::rowCount(const QModelIndex &parent) const
|
int TimelineEndModel::rowCount(const QModelIndex &parent) const
|
||||||
{
|
{
|
||||||
Q_UNUSED(parent)
|
Q_UNUSED(parent)
|
||||||
return 1;
|
if (m_room == nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return m_room->predecessorId().isEmpty() ? 1 : (m_room->allHistoryLoaded() ? 2 : 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
QHash<int, QByteArray> TimelineEndModel::roleNames() const
|
QHash<int, QByteArray> TimelineEndModel::roleNames() const
|
||||||
|
|||||||
@@ -10,6 +10,57 @@
|
|||||||
#include "messageeventmodel.h"
|
#include "messageeventmodel.h"
|
||||||
#include "neochatroom.h"
|
#include "neochatroom.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class TimelineBeginningModel
|
||||||
|
*
|
||||||
|
* A model to provide a delegate at the start of the timeline to show upgrades.
|
||||||
|
*/
|
||||||
|
class TimelineBeginningModel : 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 TimelineBeginningModel(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:
|
||||||
|
QPointer<NeoChatRoom> m_room = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class TimelineEndModel
|
* @class TimelineEndModel
|
||||||
*
|
*
|
||||||
@@ -108,5 +159,6 @@ Q_SIGNALS:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
MessageEventModel *m_messageEventModel = nullptr;
|
MessageEventModel *m_messageEventModel = nullptr;
|
||||||
|
TimelineBeginningModel *m_timelineBeginningModel = nullptr;
|
||||||
TimelineEndModel *m_timelineEndModel = nullptr;
|
TimelineEndModel *m_timelineEndModel = nullptr;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,8 +10,10 @@ ecm_add_qml_module(timeline GENERATE_PLUGIN_SOURCE
|
|||||||
HiddenDelegate.qml
|
HiddenDelegate.qml
|
||||||
MessageDelegate.qml
|
MessageDelegate.qml
|
||||||
LoadingDelegate.qml
|
LoadingDelegate.qml
|
||||||
|
PredecessorDelegate.qml
|
||||||
ReadMarkerDelegate.qml
|
ReadMarkerDelegate.qml
|
||||||
StateDelegate.qml
|
StateDelegate.qml
|
||||||
|
SuccessorDelegate.qml
|
||||||
TimelineEndDelegate.qml
|
TimelineEndDelegate.qml
|
||||||
Bubble.qml
|
Bubble.qml
|
||||||
AvatarFlow.qml
|
AvatarFlow.qml
|
||||||
|
|||||||
@@ -41,6 +41,20 @@ DelegateChooser {
|
|||||||
delegate: LoadingDelegate {}
|
delegate: LoadingDelegate {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: DelegateType.Predecessor
|
||||||
|
delegate: PredecessorDelegate {
|
||||||
|
room: root.room
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: DelegateType.Successor
|
||||||
|
delegate: SuccessorDelegate {
|
||||||
|
room: root.room
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: DelegateType.TimelineEnd
|
roleValue: DelegateType.TimelineEnd
|
||||||
delegate: TimelineEndDelegate {
|
delegate: TimelineEndDelegate {
|
||||||
|
|||||||
33
src/timeline/PredecessorDelegate.qml
Normal file
33
src/timeline/PredecessorDelegate.qml
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
// 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.Layouts
|
||||||
|
|
||||||
|
import org.kde.kirigami as Kirigami
|
||||||
|
|
||||||
|
import org.kde.neochat
|
||||||
|
|
||||||
|
TimelineDelegate {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The current room that user is viewing.
|
||||||
|
*/
|
||||||
|
required property NeoChatRoom room
|
||||||
|
|
||||||
|
width: parent?.width
|
||||||
|
rightPadding: NeoChatConfig.compactLayout && root.ListView.view.width >= Kirigami.Units.gridUnit * 20 ? Kirigami.Units.gridUnit * 2 + Kirigami.Units.largeSpacing : Kirigami.Units.largeSpacing
|
||||||
|
|
||||||
|
alwaysFillWidth: NeoChatConfig.compactLayout
|
||||||
|
|
||||||
|
contentItem: Kirigami.InlineMessage {
|
||||||
|
visible: true
|
||||||
|
text: i18n("This room continues another conversation.")
|
||||||
|
type: Kirigami.MessageType.Information
|
||||||
|
actions: Kirigami.Action {
|
||||||
|
text: i18n("See older messages…")
|
||||||
|
onTriggered: RoomManager.resolveResource(root.room.predecessorId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
33
src/timeline/SuccessorDelegate.qml
Normal file
33
src/timeline/SuccessorDelegate.qml
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
// 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.Layouts
|
||||||
|
|
||||||
|
import org.kde.kirigami as Kirigami
|
||||||
|
|
||||||
|
import org.kde.neochat
|
||||||
|
|
||||||
|
TimelineDelegate {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The current room that user is viewing.
|
||||||
|
*/
|
||||||
|
required property NeoChatRoom room
|
||||||
|
|
||||||
|
width: parent?.width
|
||||||
|
rightPadding: NeoChatConfig.compactLayout && root.ListView.view.width >= Kirigami.Units.gridUnit * 20 ? Kirigami.Units.gridUnit * 2 + Kirigami.Units.largeSpacing : Kirigami.Units.largeSpacing
|
||||||
|
|
||||||
|
alwaysFillWidth: NeoChatConfig.compactLayout
|
||||||
|
|
||||||
|
contentItem: Kirigami.InlineMessage {
|
||||||
|
visible: true
|
||||||
|
text: i18n("This room has been replaced.")
|
||||||
|
type: Kirigami.MessageType.Information
|
||||||
|
actions: Kirigami.Action {
|
||||||
|
text: i18n("See new room…")
|
||||||
|
onTriggered: RoomManager.resolveResource(root.room.successorId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user