Itinerary Component
Move the itinerary model representation to it's own component and instantiate from MessageComponentModel. This starts to lay some groundwork for previewing other files.
This commit is contained in:
@@ -338,6 +338,7 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
|||||||
qml/StateKeys.qml
|
qml/StateKeys.qml
|
||||||
qml/CodeComponent.qml
|
qml/CodeComponent.qml
|
||||||
qml/QuoteComponent.qml
|
qml/QuoteComponent.qml
|
||||||
|
qml/ItineraryComponent.qml
|
||||||
RESOURCES
|
RESOURCES
|
||||||
qml/confetti.png
|
qml/confetti.png
|
||||||
qml/glowdot.png
|
qml/glowdot.png
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ public:
|
|||||||
Code, /**< A code section. */
|
Code, /**< A code section. */
|
||||||
Quote, /**< A quote section. */
|
Quote, /**< A quote section. */
|
||||||
File, /**< A message that is a file. */
|
File, /**< A message that is a file. */
|
||||||
|
Itinerary, /**< A preview for a file that can integrate with KDE itinerary.. */
|
||||||
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). */
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include "itinerarymodel.h"
|
#include "itinerarymodel.h"
|
||||||
|
|
||||||
|
#include <QJsonDocument>
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
|
|
||||||
#include "config-neochat.h"
|
#include "config-neochat.h"
|
||||||
@@ -16,20 +17,6 @@ ItineraryModel::ItineraryModel(QObject *parent)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void ItineraryModel::setConnection(NeoChatConnection *connection)
|
|
||||||
{
|
|
||||||
if (m_connection == connection) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_connection = connection;
|
|
||||||
Q_EMIT connectionChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
NeoChatConnection *ItineraryModel::connection() const
|
|
||||||
{
|
|
||||||
return m_connection;
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant ItineraryModel::data(const QModelIndex &index, int role) const
|
QVariant ItineraryModel::data(const QModelIndex &index, int role) const
|
||||||
{
|
{
|
||||||
if (!index.isValid()) {
|
if (!index.isValid()) {
|
||||||
@@ -133,11 +120,7 @@ QString ItineraryModel::path() const
|
|||||||
|
|
||||||
void ItineraryModel::setPath(const QString &path)
|
void ItineraryModel::setPath(const QString &path)
|
||||||
{
|
{
|
||||||
if (path == m_path) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_path = path;
|
m_path = path;
|
||||||
Q_EMIT pathChanged();
|
|
||||||
loadData();
|
loadData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,19 +4,16 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QAbstractListModel>
|
#include <QAbstractListModel>
|
||||||
|
#include <QJsonArray>
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
#include <QQmlEngine>
|
#include <QQmlEngine>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
#include "neochatconnection.h"
|
|
||||||
|
|
||||||
class ItineraryModel : public QAbstractListModel
|
class ItineraryModel : public QAbstractListModel
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
|
QML_UNCREATABLE("")
|
||||||
Q_PROPERTY(NeoChatConnection *connection READ connection WRITE setConnection NOTIFY connectionChanged)
|
|
||||||
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged)
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum Roles {
|
enum Roles {
|
||||||
@@ -37,9 +34,6 @@ public:
|
|||||||
Q_ENUM(Roles)
|
Q_ENUM(Roles)
|
||||||
explicit ItineraryModel(QObject *parent = nullptr);
|
explicit ItineraryModel(QObject *parent = nullptr);
|
||||||
|
|
||||||
void setConnection(NeoChatConnection *connection);
|
|
||||||
NeoChatConnection *connection() const;
|
|
||||||
|
|
||||||
QVariant data(const QModelIndex &index, int role) const override;
|
QVariant data(const QModelIndex &index, int role) const override;
|
||||||
int rowCount(const QModelIndex &parent = {}) const override;
|
int rowCount(const QModelIndex &parent = {}) const override;
|
||||||
|
|
||||||
@@ -50,12 +44,7 @@ public:
|
|||||||
|
|
||||||
Q_INVOKABLE void sendToItinerary();
|
Q_INVOKABLE void sendToItinerary();
|
||||||
|
|
||||||
Q_SIGNALS:
|
|
||||||
void connectionChanged();
|
|
||||||
void pathChanged();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QPointer<NeoChatConnection> m_connection;
|
|
||||||
QJsonArray m_data;
|
QJsonArray m_data;
|
||||||
QString m_path;
|
QString m_path;
|
||||||
void loadData();
|
void loadData();
|
||||||
|
|||||||
@@ -64,11 +64,13 @@ MessageContentModel::MessageContentModel(const Quotient::RoomEvent *event, NeoCh
|
|||||||
});
|
});
|
||||||
connect(m_room, &NeoChatRoom::fileTransferCompleted, this, [this](const QString &eventId) {
|
connect(m_room, &NeoChatRoom::fileTransferCompleted, this, [this](const QString &eventId) {
|
||||||
if (m_event != nullptr && eventId == m_event->id()) {
|
if (m_event != nullptr && eventId == m_event->id()) {
|
||||||
|
updateComponents();
|
||||||
Q_EMIT dataChanged(index(0), index(rowCount() - 1), {FileTransferInfoRole});
|
Q_EMIT dataChanged(index(0), index(rowCount() - 1), {FileTransferInfoRole});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
connect(m_room, &NeoChatRoom::fileTransferFailed, this, [this](const QString &eventId) {
|
connect(m_room, &NeoChatRoom::fileTransferFailed, this, [this](const QString &eventId) {
|
||||||
if (m_event != nullptr && eventId == m_event->id()) {
|
if (m_event != nullptr && eventId == m_event->id()) {
|
||||||
|
updateComponents();
|
||||||
Q_EMIT dataChanged(index(0), index(rowCount() - 1), {FileTransferInfoRole});
|
Q_EMIT dataChanged(index(0), index(rowCount() - 1), {FileTransferInfoRole});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -152,6 +154,9 @@ QVariant MessageContentModel::data(const QModelIndex &index, int role) const
|
|||||||
return QVariant::fromValue(m_room->fileTransferInfo(event->id()));
|
return QVariant::fromValue(m_room->fileTransferInfo(event->id()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (role == ItineraryModelRole) {
|
||||||
|
return QVariant::fromValue<ItineraryModel *>(m_itineraryModel);
|
||||||
|
}
|
||||||
if (role == LatitudeRole) {
|
if (role == LatitudeRole) {
|
||||||
return eventHandler.getLatitude();
|
return eventHandler.getLatitude();
|
||||||
}
|
}
|
||||||
@@ -209,6 +214,7 @@ QHash<int, QByteArray> MessageContentModel::roleNames() const
|
|||||||
roles[AuthorRole] = "author";
|
roles[AuthorRole] = "author";
|
||||||
roles[MediaInfoRole] = "mediaInfo";
|
roles[MediaInfoRole] = "mediaInfo";
|
||||||
roles[FileTransferInfoRole] = "fileTransferInfo";
|
roles[FileTransferInfoRole] = "fileTransferInfo";
|
||||||
|
roles[ItineraryModelRole] = "itineraryModel";
|
||||||
roles[LatitudeRole] = "latitude";
|
roles[LatitudeRole] = "latitude";
|
||||||
roles[LongitudeRole] = "longitude";
|
roles[LongitudeRole] = "longitude";
|
||||||
roles[AssetRole] = "asset";
|
roles[AssetRole] = "asset";
|
||||||
@@ -247,6 +253,12 @@ void MessageContentModel::updateComponents(bool isEditing)
|
|||||||
const auto event = eventCast<const Quotient::RoomMessageEvent>(m_event);
|
const auto event = eventCast<const Quotient::RoomMessageEvent>(m_event);
|
||||||
auto body = EventHandler::rawMessageBody(*event);
|
auto body = EventHandler::rawMessageBody(*event);
|
||||||
m_components.append(TextHandler().textComponents(body, EventHandler::messageBodyInputFormat(*event), m_room, event, event->isReplaced()));
|
m_components.append(TextHandler().textComponents(body, EventHandler::messageBodyInputFormat(*event), m_room, event, event->isReplaced()));
|
||||||
|
} else if (eventHandler.messageComponentType() == MessageComponentType::File) {
|
||||||
|
m_components += MessageComponent{MessageComponentType::File, QString(), {}};
|
||||||
|
updateItineraryModel();
|
||||||
|
if (m_itineraryModel != nullptr) {
|
||||||
|
m_components += MessageComponent{MessageComponentType::Itinerary, QString(), {}};
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
m_components += MessageComponent{eventHandler.messageComponentType(), QString(), {}};
|
m_components += MessageComponent{eventHandler.messageComponentType(), QString(), {}};
|
||||||
}
|
}
|
||||||
@@ -262,3 +274,25 @@ void MessageContentModel::updateComponents(bool isEditing)
|
|||||||
|
|
||||||
endResetModel();
|
endResetModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MessageContentModel::updateItineraryModel()
|
||||||
|
{
|
||||||
|
if (m_room == nullptr || m_event == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto event = eventCast<const Quotient::RoomMessageEvent>(m_event)) {
|
||||||
|
if (event->hasFileContent()) {
|
||||||
|
auto filePath = m_room->fileTransferInfo(event->id()).localPath;
|
||||||
|
if (filePath.isEmpty() && m_itineraryModel != nullptr) {
|
||||||
|
delete m_itineraryModel;
|
||||||
|
m_itineraryModel = nullptr;
|
||||||
|
} else if (!filePath.isEmpty()) {
|
||||||
|
if (m_itineraryModel == nullptr) {
|
||||||
|
m_itineraryModel = new ItineraryModel(this);
|
||||||
|
}
|
||||||
|
m_itineraryModel->setPath(filePath.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include "enums/messagecomponenttype.h"
|
#include "enums/messagecomponenttype.h"
|
||||||
#include "eventhandler.h"
|
#include "eventhandler.h"
|
||||||
|
#include "itinerarymodel.h"
|
||||||
#include "linkpreviewer.h"
|
#include "linkpreviewer.h"
|
||||||
#include "neochatroom.h"
|
#include "neochatroom.h"
|
||||||
|
|
||||||
@@ -45,6 +46,7 @@ public:
|
|||||||
AuthorRole, /**< The author of the event. */
|
AuthorRole, /**< The author of the event. */
|
||||||
MediaInfoRole, /**< The media info for the event. */
|
MediaInfoRole, /**< The media info for the event. */
|
||||||
FileTransferInfoRole, /**< FileTransferInfo for any downloading files. */
|
FileTransferInfoRole, /**< FileTransferInfo for any downloading files. */
|
||||||
|
ItineraryModelRole, /**< The itinerary model for a file. */
|
||||||
LatitudeRole, /**< Latitude for a location event. */
|
LatitudeRole, /**< Latitude for a location event. */
|
||||||
LongitudeRole, /**< Longitude for a location event. */
|
LongitudeRole, /**< Longitude for a location event. */
|
||||||
AssetRole, /**< Type of location event, e.g. self pin of the user location. */
|
AssetRole, /**< Type of location event, e.g. self pin of the user location. */
|
||||||
@@ -92,4 +94,7 @@ private:
|
|||||||
void updateComponents(bool isEditing = false);
|
void updateComponents(bool isEditing = false);
|
||||||
|
|
||||||
LinkPreviewer *m_linkPreviewer = nullptr;
|
LinkPreviewer *m_linkPreviewer = nullptr;
|
||||||
|
ItineraryModel *m_itineraryModel = nullptr;
|
||||||
|
|
||||||
|
void updateItineraryModel();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -59,7 +59,6 @@ ColumnLayout {
|
|||||||
*/
|
*/
|
||||||
readonly property bool downloaded: root.fileTransferInfo && root.fileTransferInfo.completed
|
readonly property bool downloaded: root.fileTransferInfo && root.fileTransferInfo.completed
|
||||||
onDownloadedChanged: {
|
onDownloadedChanged: {
|
||||||
itineraryModel.path = root.fileTransferInfo.localPath;
|
|
||||||
if (autoOpenFile) {
|
if (autoOpenFile) {
|
||||||
openSavedFile();
|
openSavedFile();
|
||||||
}
|
}
|
||||||
@@ -145,15 +144,6 @@ ColumnLayout {
|
|||||||
QQC2.ToolTip.text: i18nc("tooltip for a button on a message; stops downloading the message's file", "Stop Download")
|
QQC2.ToolTip.text: i18nc("tooltip for a button on a message; stops downloading the message's file", "Stop Download")
|
||||||
onClicked: root.room.cancelFileTransfer(root.eventId)
|
onClicked: root.room.cancelFileTransfer(root.eventId)
|
||||||
}
|
}
|
||||||
},
|
|
||||||
State {
|
|
||||||
name: "raw"
|
|
||||||
when: true
|
|
||||||
|
|
||||||
PropertyChanges {
|
|
||||||
target: downloadButton
|
|
||||||
onClicked: root.saveFileAs()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -196,6 +186,7 @@ ColumnLayout {
|
|||||||
QQC2.Button {
|
QQC2.Button {
|
||||||
id: downloadButton
|
id: downloadButton
|
||||||
icon.name: "download"
|
icon.name: "download"
|
||||||
|
onClicked: root.saveFileAs()
|
||||||
|
|
||||||
QQC2.ToolTip.text: i18nc("tooltip for a button on a message; offers ability to download its file", "Download")
|
QQC2.ToolTip.text: i18nc("tooltip for a button on a message; offers ability to download its file", "Download")
|
||||||
QQC2.ToolTip.visible: hovered
|
QQC2.ToolTip.visible: hovered
|
||||||
@@ -220,83 +211,4 @@ ColumnLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Repeater {
|
|
||||||
id: itinerary
|
|
||||||
model: ItineraryModel {
|
|
||||||
id: itineraryModel
|
|
||||||
connection: root.room.connection
|
|
||||||
}
|
|
||||||
delegate: DelegateChooser {
|
|
||||||
role: "type"
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: "TrainReservation"
|
|
||||||
delegate: ColumnLayout {
|
|
||||||
Kirigami.Separator {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
|
||||||
RowLayout {
|
|
||||||
QQC2.Label {
|
|
||||||
text: model.name
|
|
||||||
}
|
|
||||||
QQC2.Label {
|
|
||||||
text: model.coach ? i18n("Coach: %1, Seat: %2", model.coach, model.seat) : ""
|
|
||||||
visible: model.coach
|
|
||||||
opacity: 0.7
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RowLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
ColumnLayout {
|
|
||||||
QQC2.Label {
|
|
||||||
text: model.departureStation + (model.departurePlatform ? (" [" + model.departurePlatform + "]") : "")
|
|
||||||
}
|
|
||||||
QQC2.Label {
|
|
||||||
text: model.departureTime
|
|
||||||
opacity: 0.7
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Item {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
|
||||||
ColumnLayout {
|
|
||||||
QQC2.Label {
|
|
||||||
text: model.arrivalStation + (model.arrivalPlatform ? (" [" + model.arrivalPlatform + "]") : "")
|
|
||||||
}
|
|
||||||
QQC2.Label {
|
|
||||||
text: model.arrivalTime
|
|
||||||
opacity: 0.7
|
|
||||||
Layout.alignment: Qt.AlignRight
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: "LodgingReservation"
|
|
||||||
delegate: ColumnLayout {
|
|
||||||
Kirigami.Separator {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
|
||||||
QQC2.Label {
|
|
||||||
text: model.name
|
|
||||||
}
|
|
||||||
QQC2.Label {
|
|
||||||
text: i18nc("<start time> - <end time>", "%1 - %2", model.startTime, model.endTime)
|
|
||||||
}
|
|
||||||
QQC2.Label {
|
|
||||||
text: model.address
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
QQC2.Button {
|
|
||||||
icon.name: "map-globe"
|
|
||||||
text: i18nc("@action", "Send to KDE Itinerary")
|
|
||||||
QQC2.ToolTip.visible: hovered
|
|
||||||
QQC2.ToolTip.text: text
|
|
||||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
|
||||||
onClicked: itineraryModel.sendToItinerary()
|
|
||||||
visible: itinerary.count > 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
109
src/qml/ItineraryComponent.qml
Normal file
109
src/qml/ItineraryComponent.qml
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2024 Tobias Fella <tobias.fella@kde.org>
|
||||||
|
// 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 Qt.labs.qmlmodels
|
||||||
|
|
||||||
|
import org.kde.kirigami as Kirigami
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A component to show a preview of a file that can integrate with KDE itinerary.
|
||||||
|
*/
|
||||||
|
ColumnLayout {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A model with the itinerary preview of the file.
|
||||||
|
*/
|
||||||
|
required property var itineraryModel
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The maximum width that the bubble's content can be.
|
||||||
|
*/
|
||||||
|
property real maxContentWidth: -1
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.maximumWidth: root.maxContentWidth
|
||||||
|
spacing: Kirigami.Units.largeSpacing
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: itinerary
|
||||||
|
model: root.itineraryModel
|
||||||
|
onModelChanged: console.warn(itinerary.count)
|
||||||
|
delegate: DelegateChooser {
|
||||||
|
role: "type"
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: "TrainReservation"
|
||||||
|
delegate: ColumnLayout {
|
||||||
|
Kirigami.Separator {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
RowLayout {
|
||||||
|
QQC2.Label {
|
||||||
|
text: model.name
|
||||||
|
}
|
||||||
|
QQC2.Label {
|
||||||
|
text: model.coach ? i18n("Coach: %1, Seat: %2", model.coach, model.seat) : ""
|
||||||
|
visible: model.coach
|
||||||
|
opacity: 0.7
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
ColumnLayout {
|
||||||
|
QQC2.Label {
|
||||||
|
text: model.departureStation + (model.departurePlatform ? (" [" + model.departurePlatform + "]") : "")
|
||||||
|
}
|
||||||
|
QQC2.Label {
|
||||||
|
text: model.departureTime
|
||||||
|
opacity: 0.7
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
ColumnLayout {
|
||||||
|
QQC2.Label {
|
||||||
|
text: model.arrivalStation + (model.arrivalPlatform ? (" [" + model.arrivalPlatform + "]") : "")
|
||||||
|
}
|
||||||
|
QQC2.Label {
|
||||||
|
text: model.arrivalTime
|
||||||
|
opacity: 0.7
|
||||||
|
Layout.alignment: Qt.AlignRight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: "LodgingReservation"
|
||||||
|
delegate: ColumnLayout {
|
||||||
|
Kirigami.Separator {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
QQC2.Label {
|
||||||
|
text: model.name
|
||||||
|
}
|
||||||
|
QQC2.Label {
|
||||||
|
text: i18nc("<start time> - <end time>", "%1 - %2", model.startTime, model.endTime)
|
||||||
|
}
|
||||||
|
QQC2.Label {
|
||||||
|
text: model.address
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QQC2.Button {
|
||||||
|
icon.name: "map-globe"
|
||||||
|
text: i18nc("@action", "Send to KDE Itinerary")
|
||||||
|
QQC2.ToolTip.visible: hovered
|
||||||
|
QQC2.ToolTip.text: text
|
||||||
|
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||||
|
onClicked: itineraryModel.sendToItinerary()
|
||||||
|
visible: itinerary.count > 0
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -117,6 +117,13 @@ DelegateChooser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: MessageComponentType.Itinerary
|
||||||
|
delegate: ItineraryComponent {
|
||||||
|
maxContentWidth: root.maxContentWidth
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: MessageComponentType.Poll
|
roleValue: MessageComponentType.Poll
|
||||||
delegate: PollComponent {
|
delegate: PollComponent {
|
||||||
|
|||||||
Reference in New Issue
Block a user